2008年5月24日星期六

  一、windows server 2003 3790版本识别

  RTM=release to manufacture (公开发行批量生产)是给硬件制造商的版本!是送去压盘的,不是拿去卖的。

  OEM=Original Equipment Manufacturer只能全新安装, 和RTM差不多,只是称呼不同而已。

  RTL=retail(零售)正式零售版,可以升级或者全新安装。

  VLK=Volume License大量授权版,又称为企业版。无需激活。(网上所谓的简体中文VLK版

  实际是普通简体中文版加英文VLK版中的8个文件而已)

  二、Windows Server 2003的不同版本

  Windows Server 2003 Web版:为快速开发、部署Web服务与应用程序,提供Web托管与服务平台。支持2路

  SMP(对称多处理)系统、2GB内存。

  Windows Server 2003标准版:面向中小型企业和部门级应用。支持4路SMP、4GB内存。

  Windows Server 2003企业版:适合中心与大型组织使用,有32位和64位两个版本。支持8节点集群、NUMA;

  支持8路SMP,其中32位版支持32GB内存,64位版支持64GB内存。

  Windows Server 2003数据中心版:面向要求强伸缩性和高可用性的企业,有32位和64位两个版本。32位版

  支持32路SMP、64GB内存;64位版支持64路SMP、512GB内存;两个版本均支持8节点集群、NUMA  

  三、windows server 2003 3790版的激活   

  在正式版算号器没有出来之前,现在流行的激活方式有以下几种:

  (1)Reset5.02,在安全模式下运行即可激活,把时间调整为2008年都没有问题,一切使用正常。可以升级。

  缺点:激活程序被彻底屏蔽,表现为运行msoobe /a没有任何显示,并且在服务中有一项reset5,开机会自动运

  行此服务,C:WINDOWSsystem32srvany.exe,此程序应该是reset5添加入系统的。   

  (2)俄罗斯破解,记得在xp时代,只要把setupreg.hiv在安装前替换,然后电话激活就可以达到完美的激活境

  界,可在2003下,这样子做之后,当前显示已激活,可如果你调整了时间再开机又会显示要激活,甚至是不能

  使用。估计次次问题关键还是在那个setupreg.hiv文件。   

  (3)在论坛上有人贴出了一个Winxpactivation.exe的文件,号称可以激活,实际上这个还是假激活,暂时屏

  蔽了激活程序,是不能够修改时间的。   

  (4)用替换法做出来的伪VLK,安装使用都没有问题,只是不能升级。VLK是替换英文版的8个文件.但是SN已经

  被微软封掉了.所以不能升级,但是此法是最稳定的,没有任何问题。   

  结论:推荐大家用8个替换法激活和reset5.02激活!  

  四、win 2003 server的一些优化设置   

  1.禁用配置服务器向导:   

  禁止“配置你的服务器”(Manage Your Server)向导的出现:在控制面板(Control Panel) -> 管理员工具

  (Administrative Tools )-> 管理你的服务器(Manage Your Server)运行它,然后在窗口的左下角复选“

  登录时不要显示该页”(Don't display this page at logon)。   

  2.启用硬件和DirectX加速   

  ★硬件加速:桌面点击右键--属性(Properties) -> 设置(Settings )--高级( Advanced )--

  疑难解答(Troubleshoot)。把该页面的硬件加速滚动条拉到“完全”( Full),最好点击“确定”(OK)保

  存退出。这期间可能出现一瞬的黑屏是完全正常。   

  ★DirectX加速:打开“开始”(Start) -> “运行”(Run),键入“dxdiag”并回车打开“DirectX 诊

  断工具”(DirectX Tools),在“显示”(Display)页面,点击DirectDraw, Direct3D and AGP Texture 加

  速三个按钮启用加速。把“声音的硬件加速级别”(Hardware Sound Acceleration Level)滚动条拉到“完全

  加速”( Full Acceleration)。   

  3. 启用声卡:   

  系统安装后,声卡是禁止状态,所以要在 控制面板 -> 声音 -> 启用,重启之后再设置它在任务栏显示。

  如果你使用的是Windows server 2003标准版请从第二步xx作,因为标准版已允许声音服务。

  ★打开“开始”(Start) -> “运行”(Run),键入“Services.msc ”,在出现的窗口中找到

  “Windows Audio”并双击它,然后在启动模式(startup type )的下拉菜单选择“自动”ˋutomatic),

  并点击“应用”(Apply) ->“开始”(Start) -> “确定”(OK)   

  ★打开“开始”(Start) -> “运行”(Run),键入“dxdiag”并回车打开“DirectX 诊断工具”

  (DirectX Tools),在“Sound”(Display)页面,把“声音的硬件加速级别”

  (Hardware Sound Acceleration Level)滚动条拉到“完全加速”( Full Acceleration)。 

  4. 如何启用 ASP 支持:   

  Windows Server 2003 默认安装,是不安装 IIS 6 的,需要另外安装。安装完 IIS 6,

  还需要单独开启对于 ASP 的支持。方法是: 控制面板 -> 管理工具 ->

  Web服务扩展 -> Active Server Pages -> 允许。   

  5. 如何启用 XP 的桌面主题:   

  ★打开“开始”(Start) -> “运行”(Run),键入“Services.msc ”,选themes“主题”(默认是禁止的)

  ,然后改为“自动”,按“应用”,选“开启”。

  ★接着点“桌面”的属性,在“主题”里选“windows xp”

  ★我的电脑----属性----高级----性能-----在桌面上为图标标签使用阴影
6. 禁止关机时出现的关机理由选择项:   

  关机事件跟踪(Shutdown Event Tracker)也是Windows server 2003区别于其他工作站系统的一个设置,

  对于服务器来说这是一个必要的选择,但是对于工作站系统却没什么用,我们同样可以禁止它。

  打开”开始“Start ->”运行“ Run ->输入”gpedit.msc “,在出现的窗口的左边部分,

  选择 ”计算机配置“(Computer Configuration )-> ”管理模板“(Administrative Templates )

  -> ”系统“(System),在右边窗口双击“Shutdown Event Tracker” 在出现的对话框中选择“禁止”

  (Disabled),点击然后“确定”(OK)保存后退出,这样,你将看到类似于windows 2000的关机窗口   

  7. 如何使用USB硬盘、U盘,添加已经有分区的硬盘   

  我的电脑(单击右键)----管理----磁盘管理-----在相应的硬盘上执行导入和分配盘符操作  

  8. 在控制面板里显示全部组件:

  把 Windowsinf 目录中的 sysoc.inf 文件里的 "hide" 替换掉。   

  9.禁用Internet Explorer Enhanced Security 和禁止安全询问框的出现

  在IE工具选项中自定义设置IE的安全级别。在”安全“(Security)选项卡上拉动滚动条把Internet区域

  安全设置为”中“(Medium)或“中低”。自定义设置中将有关的选择“提示”修改为选择“禁止”或“启用”。   

  10. 禁用开机 CTRL+ALT+DEL和实现自动登陆   

  ★方法1:打开注册表(运行->“Regedit”),再打开:

  HKEY_LOCAL_MACHIN SOFTWARE MicroSoft Windows NT CurrentVersion  Winlogon段,在此段中按右键,新建

  二个字符串段,AutoAdminLogon=“1”,DefaultPassword=“为超级用户Administrator所设置的Password”。

  注意,一定要为Administrator设置一个密码,否则不能实现自启动。 然后,重新启动Windows即可实现自动登录。  

  ★方法2:“gpedit.msc” - 计算机配置-Windows 设置-安全设置- Local Security Settings(本地安全策略) -> 本地策略 -> 安全选项 ->

  interactive logon: Do not require CTRL+ALT+DEL,启用之。   

  ★方法3(自动登陆):使用Windows XP的Tweak UI来实现Server 2003自动登陆。

  下载:Tweak UI http://www.ssite.org/uppic/sun_pic/...003/tweakui.exe

  下载后直接执行tweakui.exe 在左边的面板中选择Logon -> Autologon -> 在右边勾选Log on automatically

  at system startup输入你的用户名和域名(如果没有就不写),点击下面的Set Password,输入用户名的密码

  ,然后点击OK。   

  11.隐藏文件   

  Windows Server 2003默认情况下是显示所有的文件夹的,如果你不想这样,可以通过一下方法来隐藏:

  打开任意一个文件夹,选择工具(Tools) -> 文件夹选项(Folder Options) -> 查看(View),

  调整 显示系统文件夹的内容、隐藏受保护的操作系统文件、隐藏文件和文件夹 三项   

  12.允许内置的IMAPI CD-Burning服务和支持Windows影像设备服务   

  .允许内置的IMAPI CD-Burning服务和支持Windows影像设备服务

  ★假如你希望启用Windows内置的IMAPI CD-Burning服务。做如下xx作:

  打开“开始”(Start) -> “运行”(Run),键入“Services.msc ”,在出现的窗口中找到

  “IMAPI CD-Burning COM Service ”并双击它,然后在启动模式(startup type )的下拉菜单选择“自动”

  (Automatic),并点击“应用”(Apply) ->“开始”(Start) -> “确定”(OK)   

  ★假如你有如数码相机和扫描仪之类的影像设备,你应该打开Windows Image Acquisition 服务。

  打开“开始”(Start) -> “运行”(Run),键入“Services.msc ”,在出现的窗口中找到

  “Windows Image Acquisition (WIA) ”并双击它,然后在启动模式(startup type )的下拉

  菜单选择“自动”(Automatic),并点击“应用”(Apply) ->“开始”(Start) -> “确定”

 13.高级设置   

  ★我们可以修改一些windows server 2003的高级设置以适合工作站的应用环境。

  右键点击“我的电脑”(My Computer)--属性(Properties)--高级(Advanced)--性能(Performance)

  --设置(Setting)--高级(Advanced),把“处理器计划”(Processor scheduling )和内存使用

  (Memory usage)分配给“程序”(Programs)使用。然后点击“确定”(OK.)   

  ★禁用错误报告

  右键点击“我的电脑”(My Computer)--属性(Properties)--高级(Advanced)--点击“错误报告”

  (Error Reporting )按钮,在出现的窗口中把“禁用错误报告”(Disable Error Reporting)选上并复选“

  但在发生严重错误时通知我”(But, notify me when critical errors occur.)   

  ★调整虚拟内存

  一些朋友经常会对关机和注销缓慢感到束手无策,解决办法就是禁用虚拟内存,这样你的注销和关机时间可能

  会加快很多。右键点击“我的电脑”(My Computer)--属性(Properties)--高级(Advanced)--性能

  (Performance)--设置(Setting)--高级(Advanced),点击“虚拟内存”(Virtual memory)部分的

  “更改”(Change),然后在出现的窗口选择“无分页文件”。重启系统即可。   

  14.加快启动和运行速度   

  ★修改注册表,减少预读取,减少进度条等待时间:

  开始→运行→regedit启动注册表编辑器,HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession

  ManagerMemory ManagementPrefetchParameters, 有一个键值名为EnablePrefetcher,它的值是3,把它改为

  “1”或“5”。找到 HKEY_LOCAL_MACHINESystemCurrentControlSetControl,

  将 WaitToKillServiceTimeout 设为:1000或更小。 ( 原设定值:20000 )

  找到 HKEY_CURRENT_USERControl PanelDesktop 键,将右边视窗的

  WaitToKillAppTimeout 改为 1000, ( 原设定值:20000 )即关闭程序时仅等待1秒。

  将 HungAppTimeout 值改为:200( 原设定值:5000 ), 表示程序出错时等待0.5秒。   

  ★让系统自动关闭停止回应的程式。

  打开注册表 HKEY_CURRENT_USERControl PanelDesktop 键,

  将 AutoEndTasks 值设为 1。 ( 原设定值:0 )   

  ★禁用系统服务Qos

  开始菜单→运行→键入 gpedit.msc ,出现“组策略”窗口, 展开 "管理模板”→“网络” , 展开 "QoS 数

  据包调度程序", 在右边窗右键单击“限制可保留带宽" ,在属性中的“设置”中有“限制可保留带宽" ,选

  择“已禁用”,确定即可。当上述修改完成并应用后,用户在网络连接的属性对话框内的一般属性标签栏中如

  果能够看到"QoS Packet Scheduler(QoS 数据包调度程序)"。说明修改成功,否则说明修改失败。
  ★改变窗口弹出的速度:

  找到HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics子键分支,在右边的窗口中找到

  MinAniMate键值,其类型为REG_SZ,默认情况下此健值的值为1,表示打开窗口显示的动画,把它改为0,则禁

  止动画显示,接下来从开始菜单中选择“注销”命令,激活刚才所作的修改。   

  ★禁止Windows XP的压缩功能:

  点击“开始”下的“运行”,在“运行”输入框中输入“regsvr32/u zipfldr.dll”,然后按回车键即可。   

  ★设置个性的启动信息或警告信息:

  个性化的Windows XP启动:打开注册表编辑器,找到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT

  \CurrentVersion\Winlogon子键分支,双击LegalNoticeCaption健值,打开“编辑字符串”对话框,在“数

  值数据”下的文本框中输入自己想要的信息标题,如“哥们儿,你好!”,然后点击“确定”,重新启动。

  如果想要改变警告信息的话可以双击LegalNoticeText健值名称,在出现的“编辑字符串”窗口中输入想要显示

  的警告信息,单击“确定”,重新启动。   

  15.安装Java VM

  Windows server 2003没有集成MS Java VM或Sun Java VM,你可以自行下载并安装它。   

  16.安装DirectX 9a   

  在Windows Server 2003上安装DirectX 9a和在其他版本的Windows上安装DirectX 9a的方法是一样的。安装之前

  必须先启用DirectX and Graphics Acceleration。   

  17.可用的杀毒软件和防火墙:

  Symantec Norton Antivirus Corporate 8.01

  Zone Alarm 3.7.159

  Norton Personal Firewall 2003
  五、如何防范ipc$入侵   

  1、禁止空连接进行枚举(此操作并不能阻止空连接的建立)   

  首先运行regedit,找到如下组建[HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlLSA]把

  RestrictAnonymous = DWORD的键值改为:00000001。

  restrictanonymous REG_DWORD

  0x0 缺省

  0x1 匿名用户无法列举本机用户列表

  0x2 匿名用户无法连接本机IPC$共享

  说明:不建议使用2,否则可能会造成你的一些服务无法启动,如SQL Server   

  2、禁止默认共享

  1)察看本地共享资源

  运行-cmd-输入net share

  2)删除共享(每次输入一个)

  net share ipc$ /delete

  net share admin$ /delete

  net share c$ /delete

  net share d$ /delete(如果有e,f,……可以继续删除)

  3)修改注册表删除共享

  运行-regedit

  找到如下主键[HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesLanmanServerParameters]

  把AutoShareServer(DWORD)的键值改为0000000。

  如果上面所说的主键不存在,就新建(右击-新建-双字节值)一个主健再改键值。   

  3、停止server服务   

  1)暂时停止server服务

  net stop server /y (重新启动后server服务会重新开启)

  2)永久关闭ipc$和默认共享依赖的服务:lanmanserver即server服务

  控制面板-管理工具-服务-找到server服务(右击)-属性-常规-启动类型-已禁用   

  4、安装防火墙(选中相关设置),或者端口过滤(滤掉139,445等)   

  1).解开文件和打印机共享绑定

  鼠标右击桌面上[网络邻居]→[属性]→[本地连接]→[属性],去掉“Microsoft网络的文件和打印机共享”前面的勾,解开文件和打印机共享绑定。这样就会禁止所有从139和445端口来的请求,别人也就看不到本机的共享了。

  2).利用TCP/IP筛选

  鼠标右击桌面上[网络邻居]→[属性]→[本地连接]→[属性],打开“本地连接属性”对话框。选择[Internet协议(TCP/IP)]→[属性]→[高级]→[选项],在列表中单击选中“TCP/IP筛选”选项。单击[属性]按钮,选择“只允许”,再单击[添加]按钮(如图2),填入除了139和445之外要用到的端口。这样别人使用扫描器对139和445两个端口进行扫描时,将不会有任何回应。

  3).使用IPSec安全策略阻止对端口139和445的访问

  选择[我的电脑]→[控制面板]→[管理工具]→[本地安全策略]→[IP安全策略,在本地机器],在这里定义一条阻止任何IP地址从TCP139和TCP445端口访问IP地址的IPSec安全策略规则,这样别人使用扫描器扫描时,本机的139和445两个端口也不会给予任何回应。

  4).使用防火墙防范攻击

  在防火墙中也可以设置阻止其他机器使用本机共享。如在“天网个人防火墙”中,选择一条空规则,设置数据包方向为“接收”,对方IP地址选“任何地址”,协议设定为“TCP”,本地端口设置为“139到139”,对方端口设置为“0到0”,设置标志位为“SYN”,动作设置为“拦截”,最后单击[确定]按钮,并在“自定义IP规则”列表中勾选此规则即可启动拦截139端口攻击了(如图3)。   

  5、给所有账户设置复杂密码,防止通过ipc$穷举密码

周规则

《金融评论》曾发表过一篇论文,里面刊载了十年间对二十多种技术型交易系统的测试和研究,最终得出了结论,周规则名列榜首,仅随其后的是移动平均线。同时期,理查德·丹尼斯(RichardDennis)创办了举世轰动的“海龟交易班”,“龟儿”们创造了四年年均复利八十的收益,而《海龟交易法则》中的具体操作信号正是周规则。对于移动平均线,大家早已熟,那么周规则是什么呢?为什么它如此优秀,就连世界上最顶级的交易员都在使用它?
  周规则是由理查德·唐迁(RichardDonchian)发明的,它是一种追随趋势的自动交易系统。最初它以四周的形式出现。以周规则为基础的交易系统十分简单,下面以四周规则为例,讲述它的使用方法。
四周规则的使用方法:
  1、只要价格超出前四周内的最高价,就平掉空头仓位并做多;
  2、只要价格跌破前四周内的最低价,就平掉多头仓位并做空
  3。交易者可以在四周规则的基础上,对其进行优化,为了避免在无趋势时产生的错误开仓信号和尽量的保护手中利润,可以将四周规则用于开仓,而将二周规则用于平仓。同样周规则也适用于任何时间周期,同时,它不仅可以当作交易系统使用,还可以当作辨别趋势是否反转的工具。如果你是一个系统交易者,那么通过优化周规则和在其基础上进行交易是最好的选择,因为它已被证明具备在任何市场中获利的能力。最后需要指出的是,使用周规则应该始终如一按照它的指示去操作,往往一年甚至几年的利润就在一次信号之中。
四周规则用于开仓,使用方法如下:
  1、只要价格超出前四周内的最高价,就平掉空头仓位并做多;
  2、只要价格跌破前四周内的最低价,就平掉多头仓位并做空。
二周规则用于平仓,使用方法如下:
  1、只要价格超出前二周内的最高价,就平掉空头仓位并做多;
  2、只要价格跌破前二周内的最低价,就平掉多头仓位并做空。
个人体会:股票上应用的话,建仓用四周规则,清仓用二周规则。谢谢提供!

著名的Donchian周规则及MT4指标

看过此文启发不小,同时对海龟系统的思想又有了进一步的理解,转过来共享
此文及指标转自520fx的“bull”

【周规则说明】

《金融评论》曾发表过一篇论文,里面刊载了十年间对二十多种技术型交易系统的测试和研究,最终得出了结论,周规则名列榜首,仅随其后的是移动平均线。同时期,理查德·丹尼斯(Richard Dennis)创办了举世轰动的“海龟交易班”,“龟儿”们创造了四年年均复利八十的收益,而《海龟交易法则》中的具体操作信号正是周规则。对于移动平均线,大家早已熟,那么周规则是什么呢?为什么它如此优秀,就连世界上最顶级的交易员都在使用它?

周规则是由理查德·唐迁(Richard Donchian)发明的,它是一种追随趋势的自动交易系统。最初它以四周的形式出现。以周规则为基础的交易系统十分简单,下面以四周规则为例,讲述它的使用方法。

四周规则的使用方法:
1、只要价格超出前四周内的最高价,就平掉空头仓位并做多;
2、只要价格跌破前四周内的最低价,就平掉多头仓位并做空。

交易者可以在四周规则的基础上,对其进行优化,为了避免在无趋势时产生的错误开仓信号和尽量的保护手中利润,可以将四周规则用于开仓,而将二周规则用于平仓。同样周规则也适用于任何时间周期,同时,它不仅可以当作交易系统使用,还可以当作辨别趋势是否反转的工具。如果你是一个系统交易者,那么通过优化周规则和在其基础上进行交易是最好的选择,因为它已被证明具备在任何市场中获利的能力。最后需要指出的是,使用周规则应该始终如一按照它的指示去操作,往往一年甚至几年的利润就在一次信号之中。
以周期理论为主要工具的分析者认为,市场运动的最终线索就在 其运行周期上。不可否认,时间周期的研究成果,为我们的测市手段增加了时间维度。作为理论,经过不断丰富和发展之后,变得繁复而 深奥是可以理解的;作为手段,其存在和发展必定有其特殊理由,但任何一种技术都会因其自身利弊、得失而无法概全。在这里,笔者力 求以简练的语言和朋友们交流其核心内容的应用心得。

  通常,周期分析者认为,波谷比波峰可靠,所以周期长度的度量 都是从波谷到波谷进行的,原因大概是绝大多数周期的变异出现在波峰上,也就是说波峰的形成比较复杂,因而认为波谷更可靠些。从实 际应用结果来看,在牛市中周期分析远比在熊市中表现优异。原因何在,笔者认为,这与周期理论研究倾向于关注底部有关。同时笔者发 现,在牛市中,波谷比波峰形成或驻留的时间相对较短,而波峰因常出现强势整理的态势,变得复杂起来,所以较难把握。在熊市中则相 反,因为市态较弱,市场常以整理形态取代反弹,所以波峰比波谷形成时间要短,易于发现。在运用周期理论测市的时候,牛市中以波谷 法度量较为准确,熊市中以波峰法度量胜算更高些。笔者之所以倾向于度量构筑时间较短的形态,是因为这样的形态比较容易判别,预测 时间目标与实际发生时间的偏差较小。有兴趣的朋友不妨一试。

  在决定使用峰测法还是谷测法度量的时候,除了使用趋势线来筛 选之外,还有一种方法也可以给您很大的帮助,那就是先观察上一层次周期中,波峰是向周期时间中线左移还是右移,即一个涨跌周期如 是40天,波峰是向20天之前移还是向20天之后移,左移看跌,右移看涨。看跌时用峰测法,看涨时用谷测法。波峰左移和右移作为辅助工 具之一,适用于任何趋势和长度的周期。周期理论中四个重要的基本原理:叠加原理、谐波原理、同步原理、比例原理,以及两个通则原 理:变通原理、基准原理,本文中不再赘述了。

  关于时间周期,则不能不提神奇的菲波纳契数列1、1、2、3、5、 8、12、21、34、55、89、144……,这组数字之间的关系,有书籍专论,本文不详细述及。由于它是波浪理论的基础,波浪理论与周期理 论也颇有渊源,在运用周期理论测市的时候,不论是从重要的市场顶部只是底部起向未来数算,得出菲波纳契时间目标,这些日子都可能 意味着成为市场重要的转折点。在这些时间窗口,如何取得交易信号,还需辅以其他技术手段以验证。对于神奇数字,笔者从江恩理论及其 小说中体会到一种默契,江恩将“7”及其倍数的周期视作重要的转折点。笔者发现,如果这个数字是菲波纳契数×7,那这个数字更神奇。 我们如何理解“7”这个数字呢,在江恩眼里,上帝用7天创造了世界,因此“7”是一个完整的数字;在圣经中,人类最大的敌人-死亡的恐 俱也是可以克服的,耶酥在死后的第3天站起来,第7天复活,这意昧着7天是一个周期,“3”是菲波纳契数字,就是“4”也相当不平凡。 地球自转一周为360度,每4分钟旋转1度,因此,最短的循环可以是4 分钟,地球启转一周需再24小时,也是4的倍数,所以4×7天的周期 也是一个很重要的短期周期。而上述一系列数字构成了价格变化的时间窗,一旦市场进入了时间窗,我们还须依靠其他技术工具做过滤器, 如摆动指标KDJ、W%、RSI等,过滤伪杂信息来判断转折点的出现,并得出交易信号。

  需要特别指出的是,在运用周期理论、波浪理论菲波纳契数列的时 候,要注意它们都是以群体心理为基础的,也就是说市场规模越大,参 与的人数越多,就越符合上述理论,比如股指远比个股符合上述理论, 况且波浪理论本意也是应用于股市平均指数的。


3为始数,6为节数,9为终数。7为复来之数。


周 规 则
在我们设计跟踪趋势系统的时候,除了移动平均线外,也有别的选择。其中最著名也最成功的技术之一,称为周价格管道,或简称周规则。本方法也具有移动平均线的许多优点,而且省了不少麻烦,使用起来也简便一些。
在过去十年中,随着计算机技术的进步,关于在期货市场建立技术性交易系统的问题,人们进行了大量的研究。这些系统在本质上是自动化的,消除了人类情感和主观判断的影响。另一方面,它们也越来越臻于复杂。起初用的是简单的移动平均法,后来,又加入了双移动平均线交*、三移动平均线交*的内容,再后来,又把移动平均值线性加权、指数加权。最近,人们又引入了高级的统计学系统,例如线性回归系统。上述系统的首要目的依然是追随趋势,即首先识别趋势.然后顺着既有趋势的方向交易。
不过,随着越来越复杂、越来越富于想象力的系统和指标的出现,也有些不妥的倾向。人们往往忽视了那些简单、基本的工具,而它们的效果相当好,经受住了时间的考验。下面我们就来说说其中一种最简便的方法——周规则。
1970年,邓恩和哈吉特公司的金融服务部门推出了一本《交易商手册》。其中对当时最流行的自动交易系统进行了模拟测试和比较研究。该项研究的最后结果表明,在所有的测试对象中,“四周规则”系统最为成功。这种系统是由理查德·唐迁创立的。唐迁先生目前在希尔森·莱曼·运通公司工作,担任其高级副总裁兼金融顾问。他被推祟为商品期货自动交易系统领域的先驱(在1983年,《投资帐户管理报道》推举唐迁为首届“最佳获利奖”得主,表彰他对商品市场资金运作领域的巨大贡献。该机构目前向后来的受奖人颁发“唐迁奖”)。

四周规则
根据四周规则建立的系统很简单:

1、只要价洛涨过前四个日历周内的最高价,则平回空头头寸,开立多头头寸。

2、只要价格跌过前四个周内(照日历算满)的最低价,则平回多头头寸,建立空头头寸。


如上所述,本系统属于连续工作性质(连续在市),即系统始终持有头寸,或者是多头,或者是空头。一般地,连续在市系统具有一个基本的缺陷。当市场进入了无趋势状态时,它仍处在市场中,难免出现“拉锯现象”。我们曾经强调过在市场处于这种无趋势的横向状态时,趋势顺应系统效果很差。
我们也可以对四周规则进行修正,使之不连续在市。办法是采用较短的时间跨度,比如一周或二周,作为平仓的信号。换言之,必须出现了“四周突破”,我们才能建立头寸,但是只要朝相反方向的一周或二周的信号出现,就平回该头寸。之后,交易商将居于市场外,直到下一个四周突破信号出现再入市。
本系统坚实地建立在技术分析原理之上。信号自动给出,并且清晰、分明。因为它是顺应趋势的,所以实际上能够保证,每当市场出现重大趋势时,用户总站在正确的一边。同时,它的结构也体现了商品交易一句老生常谈的格言——“让利润充分增长,把损失控制在小额”。本系统还有一个特点,由之引生的交易往往不太频繁,所以其佣金成本较低。这一点,正是很多资金管理者所重视的。因而这种系统(或其变体)很流行。不过,经纪商们的态度当然就两样了。最后一点,既可以应用计算机来实施本系统,也可以不用。
周规则也有自己的反面意见。同所有趋势顺应系统所遭受的指责一样,反对者怪它不能捕捉顶或底。那么,趋势顺应系统到底做了些什么呢?最重要的一点是,四周规则的表现同绝大多数趋势顺应系统一样漂亮,甚至超过其中许多种方法;同时,它还有个长处:惊人地简明。

对四周规则的修正
我们对四周规则的讨论是对其原形展开的,不过,它也具备许多种修正和改进形式。首先,我们并不是非得把本规则运用于交易系统不可。我们也可以把周规则的信号简单地看作一种技术指标,由之来辨识价格突破、趋势反转等信号。周规则的突破信号也可以辅助其它技术,比如移动平均线等,起到与过滤器类似的验证作用。一周和二周规则便是极佳的过滤器。因此,当移动平均线的交*信号出现后,为了确定是否依照这个信号开立头寸我们必须根据二周规则,考察在其相同方向是否也有两周规则的突破信号。

周规则也可以优化
我们也可以针对不同的市场,调整周规则的时间跨度。我们并不是对每个市场都机械地使用四周规则,而是具体市场具体分析。在讨论移动平均线时,我们曾引用了一系列美林公司的研究报告。在《美林公司商品研究报告》1979年2月号上,“自动化交易技术”一文,对各种周规则的突破信号也进行了广泛的研究。其中对每个市场,都得出了相应的优化的周规则。此外,在这篇文章中还提出,通过改变每周结束的日子,系统的成绩可以进一步改善。例如,据该报告称,在糖期货市场,最好采用五周规则,并且把每周的结束日人为地规定为星期四。在大豆市场,最佳的选择是两周规则,并把星期一人为规定为每周的最末一天。在更早的美林公司的研究中,还对日间突破(“日规则”)的各种天数进行了
检测。

根据灵敏度要求调整时间跨度
根据风险管理和灵敏度的具体需要,我们可以相应地扩大或缩短周规则的时间跨度。举例来说,如果我们要求系统更灵敏,则可以缩短时间跨度。当市场急剧上升,从而价格相对地处于“高处不胜寒”的境地的时候,我们就可以缩短时间跨度,使系统更灵敏。假定我们已经根据四周规则的向上突破信号,建立了一个多头头寸,那么,就可以把保护性止损点设置在过去两周的最低价的下方。如果随后市场急剧上冲,交易商试图采用更紧凑的保护性止损方法来监控该多头头寸的话,那么还可以选用一周规则来设置止损点。
当市场处于横向伸展的情形时,那些相趋势而动的交易者袖手旁观,专心等待重要趋势信号的出现,那么这时,就可以把时间跨度扩张到八周。这样,就能够避免开立短线的头寸,免得陷足于时机不成熟的趋势信号中。

把四周规则与周期联系起来
本章前面曾交代,在期货市场上,以日为长度单位的周期具有重要意义。在所有的市场上,为时四周(或20天)的周期都是极为显著的。这或许说明了利用四周这种时间区间为何如此成功。它可能是最佳的时间跨度。请注意,我们也曾提及一周、二周以及八周规则。根据周期分析中的谐波理论,每个周期都与它相邻的周期成倍数关系(上一级周期是其2倍,下一级周期是其1/2)。
在讨论移动平均线的时候,我们曾指出,月周期加上谐波理论,解释了5天、10天、20天和40天移动平均线之所以盛行的原因。同样的道理,在四周规则上也适合。如果我们把上述天数换算成星期数,那么它们分别就是一周、二周、四周和八周。因此,对四周规则的最有效的修正是,以四周作起点,依次乘以2或除以 2。在缩小时间跨度的时候,则从四周变为二周。如果要采取更短暂的时间跨度,那么甚至可以由二周而一周。在扩大时间跨度的时候,则从四周变为八周。因为本方法把价格与时间结合起来了,所以,谐波周期理论当然就起到了重要作用。我们把周期规则的时间跨度除以二以缩短之,乘以二以扩张之,这一做法着实可以从周期理论找到充分的根据。

看完《苹果》发现,男人靠不住
看完《色戒》发现,女人靠不住
看完《投名状》发现,兄弟靠不住
看完《集结号》发现,组织靠不住
看完《妈妈再爱我一次》发现,老爸靠不住
看完《新警察故事》发现,儿子靠不住
看完《满城尽带黄金甲》发现,老婆老公靠不住
看完《红楼梦》发现,祖母和嫂子也靠不住
看完《西游记》发现,师傅靠不住
看完《霍元甲》发现,徒弟靠不住
看完《无间道》发现,警察靠不住
看完《水浒》发现,领导靠不住
看完《肖申克的救赎》发现,出纳靠不住
看完《史密斯夫妇》发现,公司也是靠不住的
看完《无极》发现,馒头靠不住
看完《青蛇》发现,动物靠不住
看完《越狱》发现,牢房靠不住
看完《阳光灿烂的日子》发现,避孕套靠不住
看完《午夜凶铃》发现,电话靠不住,电视更靠不住
看完《疯狂的石头》发现,国际高手是靠不住的
看完《长江7号》发现,地球人靠不住
看完《变形金刚》发现,外星人也靠不住
看完《黑客帝国》发现,一切现实都靠不住
结论:只有“我”靠得住
简称:我靠!!!

Delphi 的RTTI机制浅探

⊙ RTTI 简介
==============================================================
RTTI(Run-Time Type Information) 翻译过来的名称是“运行期类型信息”,也就是说可以在运行期获得数据类型或类(class)的信息。这个 RTTI 到底有什么用处,我现在也说不清楚。我是在阅读 Delphi 持续机制的代码中发现了很多 RTTI 的运用,只好先把 RTTI 学习一遍。下面是我的学习笔记。如果你发现了错误请告诉我。谢谢!

Delphi 的 RTTI 主要分为类(class)的 RTTI 和一般数据类型的 RTTI,下面从类(class)开始。
==============================================================
⊙ 类(class) 和 VMT 的关系
==============================================================
一个类(class),从编译器的角度来看就是一个指向 VMT 的指针(在后文用 VMTptr 表示)。在类的 VMTptr 的负地址方向存储了一些类信息的指针,这些指针的值和指针所指的内容在编译后就确定了。比如 VMTptr - 44 的内容是指向类名称(ClassName)的指针。不过一般不使用数值来访问这些类信息,而是通过 System.pas 中定义的以 vmt 开头的常量,如 vtmClassName、vmtParent 等来访问。

类的方法有两种:对象级别的方法和类级别的方法。两者的 Self 指针意义是不同的。在对象级别的方法中 Self 指向对象地址空间,因此可以用它来访问对象的成员函数;在类级别的方法中 Self 指向类的 VMT,因此只能用它来访问 VMT 信息,而不能访问对象的成员字段。
==============================================================
⊙ 类(class)、类的类(class of class)、类变量(class variable) 的关系
==============================================================
上面说到类(class) 就是 VMTptr。在 Delphi 中还可以用 class of 关键字定义类的类,并且可以使用类的类定义类变量。从语法上理解这三者的关键并不难,把类当成普通的数据类型来考虑就可以了。在编译器级别上表现如何呢?

为了简化讨论,我们使用 TObject、TClass 和 TMyClass 来代表上面说的三种类型:

type
TClass = class of TObject;
var
TMyClass: TClass;
MyObject: TObject;
begin
TMyClass := TObject;
MyObject := TObject.Create;
MyObject := TClass.Create;
MyObject := TMyClass.Create;
end;

在上面的例子中,三个 TObject 对象都被成功地创建了。编译器的实现是:TObject 是一个 VMTPtr 常量。TClass 也是一个 VMTptr 常量,它的值就是 TObject。TMyClass 是一个 VMTptr 变量,它被赋值为 TObject。TObject.Create 与 TClass.Create 的汇编代码完全相同。但 TClass 不仅缺省代表一个类,而且还(主要)代表了类的类型,可以用它来定义类变量,实现一些类级别的操作。
==============================================================
⊙ TObject.ClassType 和 TObject.ClassInfo
==============================================================
function TObject.ClassType: TClass;
begin
Pointer(Result) := PPointer(Self)^;
end;

TObject.ClassType 是对象级别的方法,Self 的值是指向对象内存空间的指针,对象内存空间的前 4 个字节是类的 VMTptr。因此这个函数的返回值就是类的 VMTptr。

class function TObject.ClassInfo: Pointer;
begin
Result := PPointer(Integer(Self) + vmtTypeInfo)^;
end;

TObject.ClassInfo 使用 class 关键字定义,因此是一个类级别的方法。该方法中的 Self 指针就是 VMTptr。所以这个函数的返回值是 VMTptr 负方向的 vmtTypeInfo 的内容。

TObject.ClassInfo 返回的 Pointer 指针,实际上是指向类的 RTTI 结构的指针。但是不能访问 TObject.ClassInfo 指向的内容(TObject.ClassInfo 返回值是 0),因为 Delphi 只在 TPersistent 类及 TPersistent 的后继类中产生 RTTI 信息。(从编译器的角度来看,这是在 TPersistent 类的声明之前使用 {$M+} 指示字的结果。)
TObject 还定义了一些获取类 RTTI 信息的函数,列举在下,就不一一分析了:

TObject.ClassName: ShortString; 类的名称
TObject.ClassParent: TClass; 对象的父类
TObject.InheritsFrom: Boolean; 是否继承自某类
TObject.InstanceSize: Longint; 对象实例的大小
==============================================================
⊙ is 和 as 运算符的原理
==============================================================
我们知道可以在运行期使用 is 关键字判断一个对象是否属于某个类,可以使用 as 关键字把某个对象安全地转换为某个类。在编译器的层次上,is 和 as 的操作是由 System.pas 中两个函数完成的。

{ System.pas }
function _IsClass(Child: TObject; Parent: TClass): Boolean;
begin
Result := (Child <> nil) and Child.InheritsFrom(Parent);
end;

_IsClass 很简单,它使用 TObject 的 InheritsForm 函数判断该对象是否是从某个类或它的父类中继承下来的。每个类的 VMT 中都有一项 vmtParent 指针,指向该类的父类的 VMT。TObject.InheritsFrom 实际上是通过[递归]判断父类 VMT 指针是否等于自己的 VMT 指针来判断是否是从该类继承的。

{ System.pas }
class function TObject.InheritsFrom(AClass: TClass): Boolean;
var
ClassPtr: TClass;
begin
ClassPtr := Self;
while (ClassPtr <> nil) and (ClassPtr <> AClass) do
ClassPtr := PPointer(Integer(ClassPtr) + vmtParent)^;
Result := ClassPtr = AClass;
end;

as 操作符实际上是由 System.pas 中的 _AsClass 函数完成的。它简单地调用 is 操作符判断对象是否属于某个类,如果不是就触发异常。虽然 _AsClass 返回值为 TObject 类型,但编译器会自动把返回的对象改变为 Parent 类,否则返回的对象没有办法使用 TObject 之外的方法和数据。

{ System.pas }
function _AsClass(Child: TObject; Parent: TClass): TObject;
begin
Result := Child;
if not (Child is Parent) then
Error(reInvalidCast); // loses return address
end;
==============================================================
⊙ TTypeInfo – RTTI 信息的结构
==============================================================
RTTI 信息的结构定义在 TypInfo.pas 中:
TTypeInfo = record // TTypeInfo 是 RTTI 信息的结构
Kind: TTypeKind; // RTTI 信息的数据类型
Name: ShortString; // 数据类型的名称
{TypeData: TTypeData} // RTTI 的内容
end;

TTypeInfo 就是 RTTI 信息的结构。TObject.ClassInfo 返回指向存放 class TTypeInfo 信息的指针。Kind 是枚举类型,它表示 RTTI 结构中所包含数据类型。Name 是数据类型的名称。注意,最后一个字段 TypeData 被注释掉了,这说明该处的结构内容根据不同的数据类型有所不同。

TTypeKind 枚举定义了可以使用 RTTI 信息的数据类型,它几乎包含了所有的 Delphi 数据类型,其中包括 tkClass。

TTypeKind = (tkUnknown, tkInteger, tkChar, tkEnumeration, tkFloat,
tkString, tkSet, tkClass, tkMethod, tkWChar, tkLString, tkWString,
tkVariant, tkArray, tkRecord, tkInterface, tkInt64, tkDynArray);

TTypeData 是个巨大的记录类型,在此不再列出,后文会根据需要列出该记录的内容。
==============================================================
⊙ 获取类(class)的属性(property)信息
==============================================================
这一段是 RTTI 中最复杂的部分,努力把本段吃透,后面的内容都是非常简单的。
下面是一个获取类的属性的例子:

procedure GetClassProperties(AClass: TClass; AStrings: TStrings);
var
PropCount, I: SmallInt;
PropList: PPropList;
PropStr: string;
begin
PropCount := GetTypeData(AClass.ClassInfo).PropCount;
GetPropList(AClass.ClassInfo, PropList);
for I := 0 to PropCount - 1 do
begin
case PropList[I]^.PropType^.Kind of
tkClass : PropStr := '[Class] ';
tkMethod : PropStr := '[Method]';
tkSet : PropStr := '[Set] ';
tkEnumeration: PropStr := '[Enum] ';
else
PropStr := '[Field] ';
end;
PropStr := PropStr + PropList[I]^.Name;
PropStr := PropStr + ': ' + PropList[I]^.PropType^.Name;
AStrings.Add(PropStr);
end;
FreeMem(PropList);
end;

你可以在表单上放置一个 TListBox ,然后执行以下语句观察执行结果:

GetClassProperties(TForm1, ListBox1.Items);

该函数先使用 GetTypeData 函数获得类的属性数量。GetTypeData 是 TypInfo.pas 中的一个函数,它的功能是返回 TTypeInfo 的 TypeData 数据的指针:

{ TypInfo.pas }
function GetTypeData(TypeInfo: PTypeInfo): PTypeData; assembler;
class 的 TTypeData 结构如下:
TTypeData = packed record
case TTypeKind of
tkClass: (
ClassType: TClass; // 类 (VMTptr)
ParentInfo: PPTypeInfo; // 父类的 RTTI 指针
PropCount: SmallInt; // 属性数量
UnitName: ShortStringBase; // 单元的名称
{PropData: TPropData}); // 属性的详细信息
end;

其中的 PropData 又是一个大小可变的字段。TPropData 的定义如下:

TPropData = packed record
PropCount: Word; // 属性数量
PropList: record end; // 占位符,真正的意义在下一行
{PropList: array[1..PropCount] of TPropInfo}
end;

每个属性信息在内存中的结构就是 TPropInfo,它的定义如下:

PPropInfo = ^TPropInfo;
TPropInfo = packed record
PropType: PPTypeInfo; // 属性类型信息指针的指针
GetProc: Pointer; // 属性的 Get 方法指针
SetProc: Pointer; // 属性的 Set 方法指针
StoredProc: Pointer; // 属性的 StoredProc 指针
Index: Integer; // 属性的 Index 值
Default: Longint; // 属性的 Default 值
NameIndex: SmallInt; // 属性的名称索引(以 0 开始计数)
Name: ShortString; // 属性的名称
end;

为了方便访问属性信息,TypInfo.pas 中还定义了指向 TPropInfo 数组的指针:

PPropList = ^TPropList;
TPropList = array[0..16379] of PPropInfo;

我们可以使用 GetPropList 获得所有属性信息的指针数组,数组用完以后要记得用 FreeMem 把数组的内存清除。

{ TypInfo.pas }
function GetPropList(TypeInfo: PTypeInfo; out PropList: PPropList): Integer;

GetPropList 传入类的 TTypeInfo 指针和 TPropList 的指针,它为 PropList 分配一块内存后把该内存填充为指向 TPropInfo 的指针数组,最后返回属性的数量。

上面的例子演示了如何获得类的所有属性信息,也可以根据属性的名称单独获得属性信息:

{ TypInfo.pas }
function GetPropInfo(TypeInfo: PTypeInfo; const PropName: string): PPropInfo;

GetPropInfo 根据类的 RTTI 指针和属性的名称字符串,返回属性的信息 TPropInfo 的指针。如果没有找到该属性,则返回 nil。GetPropInfo 很容易使用,举个例子:

ShowMessage(GetPropInfo(TForm, 'Name')^.PropType^.Name);

这句调用显示了 TForm 类的 Name 属性的类型名称:TComponentName。
⊙ 获取方法(method)的类型信息
==============================================================
所谓方法就是以 of object 关键字声明的函数指针,下面的函数可以显示一个方法的类型信息:

procedure GetMethodTypeInfo(ATypeInfo: PTypeInfo; AStrings: TStrings);
type
PParamData = ^TParamData;
TParamData = record // 函数参数的数据结构
Flags: TParamFlags; // 参数传递规则
ParamName: ShortString; // 参数的名称
TypeName: ShortString; // 参数的类型名称
end;

function GetParamFlagsName(AParamFlags: TParamFlags): string;
var
I: Integer;
begin
Result := '';
for I := Integer(pfVar) to Integer(pfOut) do begin
if I = Integer(pfAddress) then Continue;
if TParamFlag(I) in AParamFlags then
Result := Result + ' ' + GetEnumName(TypeInfo(TParamFlag), I);
end;
end;

var
MethodTypeData: PTypeData;
ParamData: PParamData;
TypeStr: PShortString;
I: Integer;
begin
MethodTypeData := GetTypeData(ATypeInfo);
AStrings.Add('---------------------------------');
AStrings.Add('Method Name: ' + ATypeInfo^.Name);
AStrings.Add('Method Kind: ' + GetEnumName(TypeInfo(TMethodKind),
Integer(MethodTypeData^.MethodKind)));
AStrings.Add('Params Count: '+ IntToStr(MethodTypeData^.ParamCount));
AStrings.Add('Params List:');
ParamData := PParamData(@MethodTypeData^.ParamList);
for I := 1 to MethodTypeData^.ParamCount do
begin
TypeStr := Pointer(Integer(@ParamData^.ParamName) +
Length(ParamData^.ParamName) + 1);
AStrings.Add(Format(' [%s] %s: %s',[GetParamFlagsName(ParamData^.Flags),
ParamData^.ParamName, TypeStr^]));
ParamData := PParamData(Integer(ParamData) + SizeOf(TParamFlags) +
Length(ParamData^.ParamName) + Length(TypeStr^) + 2);
end;
if MethodTypeData^.MethodKind = mkFunction then
AStrings.Add('Result Value: ' + PShortString(ParamData)^);
end;

作为实验,在表单上放置一个 TListBox,然后执行以下代码,观察执行结果:

type
TMyMethod = function(A: array of Char; var B: TObject): Integer of object;

procedure TForm1.FormCreate(Sender: TObject);
begin
GetMethodTypeInfo(TypeInfo(TMyMethod), ListBox1.Items);
GetMethodTypeInfo(TypeInfo(TMouseEvent), ListBox1.Items);
GetMethodTypeInfo(TypeInfo(TKeyPressEvent), ListBox1.Items);
GetMethodTypeInfo(TypeInfo(TMouseWheelEvent), ListBox1.Items);
end;

由于获取方法的类型信息比较复杂,我尽量压缩代码也还是有这么长,让我们看看它的实现原理。GetMethodTypeInfo 的第一个参数是 PTypeInfo 类型,表示方法的类型信息地址。第二个参数是一个字符串列表,可以使用任何实现 TStrings 操作的对象。我们可以使用 System.pas 中的 TypeInfo 函数获得任何类型的 RTTI 信息指针。TypeInfo 函数像 SizeOf 一样,是内置于编译器中的。

GetMethodTypeInfo 还用到了 TypInfo.pas 中的 GetEnumName 函数。这个函数通过枚举类型的整数值得到枚举类型的名称。

function GetEnumName(TypeInfo: PTypeInfo; Value: Integer): string;

与获取类(class)的属性信息类似,方法的类型信息也在 TTypeData 结构中

TTypeData = packed record
case TTypeKind of
tkMethod: (
MethodKind: TMethodKind; // 方法指针的类型
ParamCount: Byte; // 参数数量
ParamList: array[0..1023] of Char // 参数详细信息,见下行注释
{ParamList: array[1..ParamCount] of
record
Flags: TParamFlags; // 参数传递规则
ParamName: ShortString; // 参数的名称
TypeName: ShortString; // 参数的类型
end;
ResultType: ShortString}); // 返回值的名称
end;

TMethodKind 是方法的类型,定义如下:
TMethodKind = (mkProcedure, mkFunction, mkConstructor, mkDestructor,
mkClassProcedure, mkClassFunction,
{ Obsolete }
mkSafeProcedure, mkSafeFunction);

TParamsFlags 是参数传递的规则,定义如下:

TParamFlag = (pfVar, pfConst, pfArray, pfAddress, pfReference, pfOut);

TParamFlags = set of TParamFlag;

由于 ParamName 和 TypeName 是变长字符串,不能直接取用该字段的值,而应该使用指针步进的方法,取出参数信息,所以上面的代码显得比较长。
==============================================================
⊙ 获取有序类型(ordinal)、集合(set)类型的 RTTI 信息
==============================================================
讨论完了属性和方法的 RTTI 信息之后再来看其它数据类型的 RTTI 就简单多了。所有获取 RTTI 的原理都是通过 GetTypeData 函数得到 TTypeData 的指针,再通过 TTypeInfo.TypeKind 来解析 TTypeData。任何数据类型的 TTypeInfo 指针可以通过 TypeInfo 函数获得。

有序类型的 TTypeData 定义如下:

TTypeData = packed record
tkInteger, tkChar, tkEnumeration, tkSet, tkWChar: (
OrdType: TOrdType; // 有序数值类型
case TTypeKind of
case TTypeKind of
tkInteger, tkChar, tkEnumeration, tkWChar: (
MinValue: Longint; // 类型的最小值
MaxValue: Longint; // 类型的最大值
case TTypeKind of
tkInteger, tkChar, tkWChar: ();
tkEnumeration: (
BaseType: PPTypeInfo; // 指针的指针,它指向枚举的 PTypeInfo
NameList: ShortStringBase; // 枚举的名称字符串(不能直接取用)
EnumUnitName: ShortStringBase)); // 所在的单元名称(不能直接取用)
tkSet: (
CompType: PPTypeInfo)); // 指向集合基类 RTTI 指针的指针
end;

下面是一个获取有序类型和集合类型的 RTTI 信息的函数:

procedure GetOrdTypeInfo(ATypeInfo: PTypeInfo; AStrings: TStrings);
var
OrdTypeData: PTypeData;
I: Integer;
begin
OrdTypeData := GetTypeData(ATypeInfo);
AStrings.Add('------------------------------------');
AStrings.Add('Type Name: ' + ATypeInfo^.Name);
AStrings.Add('Type Kind: ' + GetEnumName(TypeInfo(TTypeKind),
Integer(ATypeInfo^.Kind)));
AStrings.Add('Data Type: ' + GetEnumName(TypeInfo(TOrdType),
Integer(OrdTypeData^.OrdType)));
if ATypeInfo^.Kind <> tkSet then begin
AStrings.Add('Min Value: ' + IntToStr(OrdTypeData^.MinValue));
AStrings.Add('Max Value: ' + IntToStr(OrdTypeData^.MaxValue));
end;
if ATypeInfo^.Kind = tkSet then
GetOrdTypeInfo(OrdTypeData^.CompType^, AStrings);
if ATypeInfo^.Kind = tkEnumeration then
for I := OrdTypeData^.MinValue to OrdTypeData^.MaxValue do
AStrings.Add(Format(' Value %d: %s', [I, GetEnumName(ATypeInfo, I)]));
end;

在表单上放置一个 TListBox,运行以下代码查看结果:

type TMyEnum = (EnumA, EnumB, EnumC);

procedure TForm1.FormCreate(Sender: TObject);
begin
GetOrdTypeInfo(TypeInfo(Char), ListBox1.Items);
GetOrdTypeInfo(TypeInfo(Integer), ListBox1.Items);
GetOrdTypeInfo(TypeInfo(TFormBorderStyle), ListBox1.Items);
GetOrdTypeInfo(TypeInfo(TBorderIcons), ListBox1.Items);
GetOrdTypeInfo(TypeInfo(TMyEnum), ListBox1.Items);
end;

(如果枚举元素没有按缺省的 0 基准定义,那么将不能产生 RTTI 信息,为什么?)
==============================================================
⊙ 获取其它数据类型的 RTTI 信息
==============================================================
上面讨论了几个典型的 RTTI 信息的运行,其它的数据类型的 RTTI 信息的获取方法与上面类似。由于这些操作更加简单,就不一一讨论。下面概述其它类型的 RTTI 信息的情况:

LongString、WideString 和 Variant 没有 RTTI 信息;
ShortString 只有 MaxLength 信息;
浮点数类型只有 FloatType: TFloatType 信息;
TFloatType = (ftSingle, ftDouble, ftExtended, ftComp, ftCurr);
Int64 只有最大值和最小值信息(也是 64 位整数表示);
Interface 和动态数组不太熟悉,就不作介绍了。
⊙ GetTypeData 函数
==============================================================
GetTypeData 函数根据 TTypeInfo 指针获得 TTypeData 的地址。

function GetTypeData(TypeInfo: PTypeInfo): PTypeData;
asm
XOR EDX,EDX ; EDX 清零
MOV DL,[EAX].TTypeInfo.Name.Byte[0] ; 获得 Name 字符串长度
LEA EAX,[EAX].TTypeInfo.Name[EDX+1] ; 获得 TTypeData 的地址
end;
==============================================================
⊙ GetPropInfo 函数
==============================================================
GetPropInfo 函数用于获得属性的 RTTI 指针 PPropInfo。它有四种重载形式,后面三种重载的实现都是调用第一种形式。AKinds 参数用于限制属性的类型,如果得到的 PPropInfo 不属于指定的类型,则返回 nil。
function GetPropInfo(TypeInfo: PTypeInfo; const PropName: string): PPropInfo;
function GetPropInfo(Instance: TObject; const PropName: string;
AKinds: TTypeKinds = []): PPropInfo;
function GetPropInfo(AClass: TClass; const PropName: string;
AKinds: TTypeKinds = []): PPropInfo;
function GetPropInfo(TypeInfo: PTypeInfo; const PropName: string;
AKinds: TTypeKinds): PPropInfo;
==============================================================
⊙ FindPropInfo 函数
==============================================================
FindPropInfo 函数根据属性名称获得属性的 RTTI 指针,它只是在 GetPropInfo 函数的基础上加上了错误检查功能,如果没有属性 RTTI 信息,则触发 EPropertyError 异常。
function FindPropInfo(Instance: TObject; const PropName: string): PPropInfo;
function FindPropInfo(AClass: TClass; const PropName: string): PPropInfo;
==============================================================
⊙ GetPropInfos 函数
==============================================================
GetPropInfos 函数的功能是把一个类(class)所有属性 RTTI 指针 PPropInfo 填充至传入的参数 PPropList 数组中。
注意:这个函数不负责分配该数组的内容,使用前必须根据属性的数量分配足够的空间。该数组结束后必须清除分配的内容。
procedure GetPropInfos(TypeInfo: PTypeInfo; PropList: PPropList);
注:使用 GetPropList 实现相同的功能更方便。
==============================================================
⊙ SortPropList 函数
==============================================================
SortPropList 可以对 GetPropInfos 函数填充的属性信息指针数组按属性名称排序。
procedure SortPropList(PropList: PPropList; PropCount: Integer);
在 VCL 中 SortPropList 只被 GetPropList 函数使用。
==============================================================
⊙ GetPropList 函数
==============================================================
GetPropList 函数同 GetPropInfos 一样,填充 PPropList 数组。GetPropList 实际上是调用 GetPropInfos 进行填充工作,最后返回已填充的属性的数量。
function GetPropList(TypeInfo: PTypeInfo; TypeKinds: TTypeKinds;
PropList: PPropList; SortList: Boolean): Integer;
function GetPropList(TypeInfo: PTypeInfo; out PropList: PPropList): Integer;
function GetPropList(AObject: TObject; out PropList: PPropList): Integer;
注意:GetPropList 的内存分配有点混乱,上面第一个 GetPropList 必须自己分配 PPrpList 数组的内存,后面二个 GetPropList 会自动分配 PPropList 数组的内存。造成这种情况的原因是:第一个 GetPropList 可以设置 TypeKinds 参数限制只返回指定类型的属性,这样就不能直接得到可能返回的属性数量。TypeKinds 参数可以设置为 tkAny,表示返回所有数据类型的属性。
第一个 GetPropList 函数可以设置 SortList 参数对属性名称进行排序。它实际上是调用第二个 GetPropList 并调用 SortPropList 函数执行排序。
注意:PPropList 不再使用的时候,要记得使用 FreeMem 函数清除数组内存(根据返回值是否大于1)。
==============================================================
⊙ GetObjectPropClass 函数
==============================================================
GetObjectPropClass 函数用于返回对象类型的属性所属的类(class)。
function GetObjectPropClass(Instance: TObject; PropInfo: PPropInfo): TClass;
function GetObjectPropClass(Instance: TObject; const PropName: string): TClass;
function GetObjectPropClass(PropInfo: PPropInfo): TClass;
这个函数被 SetObjectProp 函数使用,用于参数检验。
==============================================================
⊙ PropType / PropIsType 函数
==============================================================
PropType 函数用于获得属性的数据类型。
function PropType(Instance: TObject; const PropName: string): TTypeKind;
function PropType(AClass: TClass; const PropName: string): TTypeKind;
PropIsType 判断属性是否属于某种数据类型。它调用 PropType 实现功能。
function PropIsType(Instance: TObject; const PropName: string;
TypeKind: TTypeKind): Boolean;
function PropIsType(AClass: TClass; const PropName: string;
TypeKind: TTypeKind): Boolean;
==============================================================
⊙ IsPublishedProp 函数
==============================================================
IsPublishedProp 函数用于判断属性是否是 published 属性,它通过检查该属性 RTTI 指针是否等于 nil 来实现功能。
function IsPublishedProp(Instance: TObject; const PropName: string): Boolean;
function IsPublishedProp(AClass: TClass; const PropName: string): Boolean;
IsPublishedProp 函数没有被 VCL 使用。
==============================================================
⊙ IsStoredProp 函数
==============================================================
IsStoredProp 函数使用属性信息中的 TPropInfo.StoredProp 函数指针来调用属性定义时用 stored 关键字定义的函数的结果。
这个函数被用于 Delphi 持续机制,TWriter.WriteProperties 方法调用 IsStoredProp 判断是否需要把该属性的值写入流中。
function IsStoredProp(Instance: TObject; PropInfo: PPropInfo): Boolean;
function IsStoredProp(Instance: TObject; const PropName: string): Boolean;
==============================================================
⊙ FreeAndNilProperties 函数
==============================================================
FreeAndNilProperties 函数用于清除一个对象的所有 published 的对象类型的属性的对象。这个函数调用 GetObjectProp 执行获得对象属性的对象句柄,并调用对象的 Free 方法清除这个对象,然后调用 SetObjectProp 设置该属性为 nil。
procedure FreeAndNilProperties(AObject: TObject);
我不知道这个函数能用在哪里,至少 VCL 中没有使用这个函数。
==============================================================
⊙ SetToString / StringToSet 函数
==============================================================
SetToString 和 StringToSet 是两个 RTTI 辅助函数,它们把集合值转换为字符串,或者把字符串转换为集合值。
function SetToString(PropInfo: PPropInfo; Value: Integer;
Brackets: Boolean = False): string;
function StringToSet(PropInfo: PPropInfo; const Value: string): Integer;
注意:这里的集合值最多只能包含 32 个元素(4 bytes),这是集合 RTTI 的限制。
==============================================================
⊙ GetEnumName / GetEnumValue / GetEnumNameValue 函数
==============================================================
GetEnumName 函数根据枚举整数值返回枚举字符串。它可以返回以下三种枚举名称:
Integer:直接返回 IntToStr(Integer)
Boolean:返回 True/False
Enum :返回 TTypeData^.NameList 中存储的枚举名称
function GetEnumName(TypeInfo: PTypeInfo; Value: Integer): string;
GetEnumValue 函数根据枚举字符串返回枚举整数值。它与 GetEnumName 类似,可以返回三种枚举的整数值,但对于 Enum 类型,它调用了 GetEnumNameValue 函数。
function GetEnumValue(TypeInfo: PTypeInfo; const Name: string): Integer;
GetEnumNameValue 函数与 GetEnumValue 函数功能差不多,但它是个汇编函数,只能返回纯枚举类型的值。其工作原理也是匹配 TTypeData^.NameList 值。
function GetEnumNameValue(TypeInfo: PTypeInfo; const Name: string): Integer;
注意:GetEnumNameValue 隐藏在 Implementation 段,不能直接使用,它是为 GetEnumValue 函数服务的。
⊙ GetOrdProp 函数详解
==============================================================
GetOrdProp 是 Delphi RTTI 中使用频繁的函数。GetOrdProp 根据对象句柄和对象属性的 TPropInfo 指针获得对象的属性值。它的返回值是 Longint,需要强制转换成相应的属性类型才能使用。
function GetOrdProp(Instance: TObject; PropInfo: PPropInfo): Longint;
GetOrdProp 调用 TPropInfo.GetProc 函数指针得到属性的返回值。它的工作过程是:
如果该属性的类型是 class 类型,那么返回值是 4 个字节(对象句柄)。

否则通过 TTypeData.OrdType 得到返回值的类型,存储在 BL 中。

{ TOrdType = (otSByte, otUByte, otSWord, otUWord, otSLong, otULong); }
检查 TPropInfo.GetProc 的第一个字节(注意是 GetProc 指针的第一个字节):
如果 GetProc[0] = $FF,说明 GetProc 是 field offset;
如果 GetProc[0] = $FE,说明 GetProc 是 virtual method offset;
如果 GetProc[0] < $FE,说明 GetProc 是 static method;
然后根据不同的 GetProc 类型解析后,调用 GetProc。
根据 BL 中存储的类型符号信息修正返回值(EAX)的符号信息。
根据 BL 中存储的类型的大小裁剪返回值 EAX 为 EAX/AX/AL。
EAX(AX/AL) 即是返回的属性值。
GetOrdProp 的汇编代码及注释如下:
function GetOrdProp(Instance: TObject; PropInfo: PPropInfo): Longint;
asm
PUSH EBX
PUSH EDI
MOV EDI,[EDX].TPropInfo.PropType ; EDI <- PPTypeInfo
MOV EDI,[EDI] ; EDI <- PTypeInfo
MOV BL,otSLong ; BL <- otSLong
CMP [EDI].TTypeInfo.Kind,tkClass ; if Prop is Class
JE @@isClass ; jmp @@isClass
XOR ECX,ECX ; ECX <- 0
MOV CL,[EDI].TTypeInfo.Name.Byte[0] ; CL <- Name StrLength
MOV BL,[EDI].TTypeInfo.Name[ECX+1].TTypeData.OrdType
; BL <- Prop OrdType
@@isClass:
MOV ECX,[EDX].TPropInfo.GetProc ; ECX <- GetProc Addr
CMP [EDX].TPropInfo.GetProc.Byte[3],$FE ; cmp HiByte(GetProc), $FE
MOV EDX,[EDX].TPropInfo.Index ; EDX <- Prop Index
JB @@isStaticMethod ; if below $FE
JA @@isField ; if is $FF
{ the GetProc is a virtual method } ; if is $FE
MOVSX ECX,CX { sign extend slot offs }
ADD ECX,[EAX] { vmt + slotoffs }
CALL dword ptr [ECX] { call vmt[slot] }
JMP @@final
@@isStaticMethod:
CALL ECX ; call GetProc directly
JMP @@final
@@isField:
AND ECX,$00FFFFFF ; clear HiByte(GetProc)
ADD ECX,EAX ; ECX <- Field Addr
MOV AL,[ECX] ; AL <- Field Addr[0]
CMP BL,otSWord ; if OrdType < otSWord
JB @@final ; Exit
MOV AX,[ECX] ; else AX <- Field[0..1]
CMP BL,otSLong ; if OrdType < otSLong
JB @@final ; Exit
MOV EAX,[ECX] ; else EAX <- Field[0..3]
@@final:
CMP BL,otSLong ; if OrdType >= otSLong
JAE @@exit ; Exit
CMP BL,otSWord ; if OrdType >= otSWord
JAE @@word ; jmp @@word
CMP BL,otSByte ; if OrdType = otSByte
MOVSX EAX,AL ; AL <- Sign(EAX)
JE @@exit ; Exit
AND EAX,$FF ; clear HiWord(EAX)
JMP @@exit ; Exit
@@word:
MOVSX EAX,AX ; AX <= Sign(EAX)
JE @@exit ; if OrdType = otSWord then Exit
AND EAX,$FFFF ; clear HiWord(EAX)
@@exit:
POP EDI
POP EBX
end;
TypInfo.pas 中重载了 GetOrdProp 函数,将 PPropInfo 参数替换为 PropName,方便程序员调用,它其实也是调用了上面介绍的 GetOrdProp 函数。
function GetOrdProp(Instance: TObject; const PropName: string): Longint;
begin
Result := GetOrdProp(Instance, FindPropInfo(Instance, PropName));
end;
下面是使用 GetOrdProp 的例子:
Self.Width := Self.Width - GetOrdProp(Self, 'Height');
上面的语句相当于:
Self.Width := Self.Width - Self.Height;
* 后文介绍的 Get___Prop 系列函数或者调用本函数,或者它的实现方法与本函数类似。
==============================================================
⊙ SetOrdProp 函数
==============================================================
SetOrdProp 函数是 GetOrdProp 的逆过程,它调用 TPropInfo.SetProc 函数指针设置对象的属性值。SetProc 指针的第一个字节的意义同 GetProc 一样,也是表示该 SetProc 是字段偏移、虚方法偏移和静态方法。
procedure SetOrdProp(Instance: TObject; PropInfo: PPropInfo; Value: Longint);
SetOrdProc 也根据属性名称重载了:
procedure SetOrdProp(Instance: TObject; const PropName: string; Value: Longint);
由于 SetOrdProp 的汇编代码与 GetOrdProp 的几乎一样,在此就不再列出。作为练习,试用一下:
SetOrdProp(Self, 'Height', Self.Height + 10);
该语句的功能相当于:
Self.Height := Self.Height + 10;
* 后文介绍的 Set___Prop 系列函数或者调用本函数,或者它的实现方法与本函数类似。
==============================================================
⊙ GetEnumProp / SetEnumProp 函数
==============================================================
GetEnumProp 函数获取枚举类型属性的枚举字符串,它调用 GetEnumName 转换 GetOrdProp 的返回值。
function GetEnumProp(Instance: TObject; PropInfo: PPropInfo): string;
function GetEnumProp(Instance: TObject; const PropName: string): string;
SetEnumProp 函数使用枚举字符串设置枚举类型属性值,它调用 GetEnumValue 转换枚举字符串后再调用 SetOrdProp 设置属性值。
procedure SetEnumProp(Instance: TObject; PropInfo: PPropInfo;
const Value: string);
procedure SetEnumProp(Instance: TObject; const PropName: string;
const Value: string);
==============================================================
⊙ GetSetProp / SetSetProp 函数
==============================================================
GetSetProp 函数用于获取集合类型属性的字符串值,它也是调用 GetOrdProp 获得属性值,然后调用 SetToString 函数把数值转换成字符串。
注意:GetOrdProp 函数返回值是 Integer,那么它是如何表示可以存储 256 个元素的集合类型呢?答案是:如果是 published 集合属性,那么该集合最大只能是 4 个字节,也就是最多只能存储 32 个元素。
function GetSetProp(Instance: TObject; PropInfo: PPropInfo;
Brackets: Boolean): string;
function GetSetProp(Instance: TObject; const PropName: string;
Brackets: Boolean = False): string;
SetSetProp 函数用于通过字符串设置集合类型属性的值。它先调用 StringToSet 函数把字符串转换为整数值,然后使用 SetOrdProp 函数设置属性值。
procedure SetSetProp(Instance: TObject; PropInfo: PPropInfo;
const Value: string);
procedure SetSetProp(Instance: TObject; const PropName: string;
const Value: string);
试验: SetSetProp(Self, 'BorderIcons', '[biSystemMenu]');

==============================================================
⊙ GetObjectProp / SetObjectProp 函数
==============================================================
对象实际上是指针,也就是整数值,所以 GetObjectProp 直接调用 GetOrdProp 就可以了。
MinClass 参数指定得到的 Object 必须属于某个 class ,如果不是则返回 nil 。
function GetObjectProp(Instance: TObject; PropInfo: PPropInfo;
MinClass: TClass = nil): TObject;
function GetObjectProp(Instance: TObject; const PropName: string;
MinClass: TClass = nil): TObject;
SetObjectProp 用于设置属性的对象句柄。ValidateClass 参数表示是否需要检查传入的对象类型与属性信息的类信息是否兼容。
procedure SetObjectProp(Instance: TObject; PropInfo: PPropInfo;
Value: TObject; ValidateClass: Boolean = True);
procedure SetObjectProp(Instance: TObject; const PropName: string;
Value: TObject);
例子:

var
MyFont: TFont;
begin
MyFont := TFont.Create;
MyFont.Height := 20;
SetObjectProp(Self, 'Font', MyFont);
end;
==============================================================
⊙ GetStrProp / SetStrProp 函数
==============================================================
GetStrProp 函数用于获得字符串类型的属性值。
function GetStrProp(Instance: TObject; PropInfo: PPropInfo): string;
function GetStrProp(Instance: TObject; const PropName: string): string;
由于 Delphi 支持三种类型的字符串,GetStrProp 根据字符串的类型,分别调用三个获得字符串属性值的函数:
case PropInfo^.PropType^.Kind of
tkString: GetShortStrPropAsLongStr(Instance, PropInfo, Result);
tkLString: GetLongStrProp(Instance, PropInfo, Result);
tkWString: GetWideStrPropAsLongStr(Instance, PropInfo, Result);
end;
其中 GetShortStrPropAsLongStr 又调用了 GetShortStrProp;GetWideStrPropAsLongStr 又调用了 GetWideStrProp,进行字符串间的类型转换。
SetStrProp 函数用于设置字符串类型的属性值。它的实现方法与 GetStrProp 类似。
procedure SetStrProp(Instance: TObject; PropInfo: PPropInfo;
const Value: string);
procedure SetStrProp(Instance: TObject; const PropName: string;
const Value: string);
==============================================================
⊙ GetFloatProp / SetFloatProp 函数
==============================================================
GetFloatProp 用于获得浮点型属性值。它将 Single(4 bytes)、Double(8 bytes)、Comp(8 bytes)、Currency(8 bytes) 类型的浮点数属性转换为 Extented(10 bytes) 类型返回。
function GetFloatProp(Instance: TObject; PropInfo: PPropInfo): Extended;
function GetFloatProp(Instance: TObject; const PropName: string): Extended;
SetFloatProp 用于设置浮点型属性值。它的实现方法与 GetFloatProp 类似。
procedure SetFloatProp(Instance: TObject; PropInfo: PPropInfo;
const Value: Extended);
procedure SetFloatProp(Instance: TObject; const PropName: string;
const Value: Extended);
==============================================================
⊙ GetVariantProp / SetVariantProp
==============================================================
GetVariantProp 函数用于获得 Variant 类型的属性值。
function GetVariantProp(Instance: TObject; PropInfo: PPropInfo): Variant;
function GetVariantProp(Instance: TObject; const PropName: string): Variant;
SetVariantProp 函数用于设置 Variant 类型的属性值。
procedure SetVariantProp(Instance: TObject; PropInfo: PPropInfo;
const Value: Variant);
procedure SetVariantProp(Instance: TObject; const PropName: string;
const Value: Variant);
==============================================================
⊙ GetMethodProp / SetMethodProp
==============================================================
GetMethodProp 函数用于获得 Method 类型的属性值。
function GetMethodProp(Instance: TObject; PropInfo: PPropInfo): TMethod;
function GetMethodProp(Instance: TObject; const PropName: string): TMethod;
SetMethodProp 函数用于设置 Method 类型的属性值。
procedure SetMethodProp(Instance: TObject; const PropName: string;
const Value: TMethod);
procedure SetMethodProp(Instance: TObject; PropInfo: PPropInfo;
const Value: TMethod);
==============================================================
⊙ GetInt64Prop / SetInt64Prop
==============================================================
SetInt64Prop 函数用于设置 Int64 类型的属性值。不同于一般整数用 EAX 返回,Int64 类型的返回值由 EDX:EAX 返回,所以有必要单独定义 Int64 的获取和设置方法。
function GetInt64Prop(Instance: TObject; PropInfo: PPropInfo): Int64;
function GetInt64Prop(Instance: TObject; const PropName: string): Int64;
SetInt64Prop 函数用于设置 Int64 类型的属性值。
procedure SetInt64Prop(Instance: TObject; PropInfo: PPropInfo;
const Value: Int64);
procedure SetInt64Prop(Instance: TObject; const PropName: string;
const Value: Int64);
==============================================================
⊙ GetInterfaceProp / SetInterfaceProp 函数
==============================================================
GetInterfaceProp 函数用于获得 Interface 类型的属性值。
function GetInterfaceProp(Instance: TObject; PropInfo: PPropInfo): IInterface;
function GetInterfaceProp(Instance: TObject; const PropName: string): IInterface;
SetInterfaceProp 函数用于设置 Interface 类型的属性值。
procedure SetInterfaceProp(Instance: TObject; PropInfo: PPropInfo;
const Value: IInterface);
procedure SetInterfaceProp(Instance: TObject; const PropName: string;
const Value: IInterface);
* 不太熟悉 Interface,以后再看实现过程。
==============================================================
⊙ GetPropValue / SetPropValue 函数
==============================================================
GetPropValue 函数用于获得任何类型的属性值,它返回 Variant 类型。
注意,这个函数没有重载函数,只能使用属性名称字符串为参数。
GetPropValue 先调用 GetPropInfo 函数获得属性的类型,然后根据属性的数据类型选择调用以上介绍的 GetOrdProp、GetEnumProp、GetSetProp、GetStrProp 等函数实现具体的功能。
GetPropValue 的参数 PreferStrings 如果设置为 True,那么对于枚举、集合类型,将返回字符串值,否则返回整数值。GetPropValue 还可以返回动态数组类型的属性值。(目前对动态数组不太熟悉,先记下来。)
function GetPropValue(Instance: TObject; const PropName: string;
PreferStrings: Boolean): Variant;
SetPropValue 函数用于设置任何类型的属性值。SetPropValue 的实现与 GetPropValue 类似。并且 SetPropValue 内部分析 Value 参数是否是字符串来设置枚举和集合类型的属性,所以不需要 PreferStrings 参数。SetPropValue 也可以设置动态数组属性,它使用了 SetOrdProp 函数实现这一功能,看来动态数组在内存中的表现是一个指针。
procedure SetPropValue(Instance: TObject; const PropName: string;
const Value: Variant);
==============================================================
⊙ TPublishableVariantType class
==============================================================
在 TypInfo.pas 的代码注释中说 TPublishableVariantType 是用来代替 TCustomVariantType 以便更容易在 RTTI 中使用自定义的 Variant 类型。
* 现在对这两个类型都不太了解,先记在这里以后再学。
==============================================================
⊙ RegisterClass / FindClass 系列函数 (Classes.pas)
==============================================================
Delphi 提供了一种机制,可以使用类(class)的名称获得类(class VMTptr)。缺省情况下这些类必须是从 TPersistent 类继承下来的。使用这项功能之前必须在先把类信息注册到全局对象 RegGroup 中。
RegisterClass 函数用于注册类信息至 RegGroup 中,注意该函数名称和 Win32 API 中注册窗口类的函数同名。如果类已经被注册过了,RegisterClass 将直接返回。如果有一个不同的类以相同的名称注册了,RegisterClass 将触发异常(EFilerError)。
procedure RegisterClass(AClass: TPersistentClass);
RegisterClasses 函数可以方便地注册一批类:
procedure RegisterClasses(AClasses: array of TPersistentClass);
RegisterClassAlias 函数可以为类以其它的名称注册,以避免名称冲突。
procedure RegisterClassAlias(AClass: TPersistentClass; const Alias: string);
GetClass 函数根据类名称字符串获得类(class),如果没找到,将返回 nil:
function GetClass(const AClassName: string): TPersistentClass;
FindClass 函数包装了 GetClass,不同的是如果没找到该类,则触发异常(EClassNotFound):
function FindClass(const ClassName: string): TPersistentClass;
UnRegisterClass 系列函数执行 RegisterClass 相反的工作:
procedure UnRegisterClass(AClass: TPersistentClass);
procedure UnRegisterClasses(AClasses: array of TPersistentClass);
procedure UnRegisterModuleClasses(Module: HMODULE);
缺省的 RegGroup 用于组织从 TPersistent 继承下来的类,下面五个函数可以设置自己的 RegGroup:
procedure StartClassGroup(AClass: TPersistentClass);
procedure GroupDescendentsWith(AClass, AClassGroup: TPersistentClass);
function ActivateClassGroup(AClass: TPersistentClass): TPersistentClass;
function ClassGroupOf(AClass: TPersistentClass): TPersistentClass; overload;
function ClassGroupOf(Instance: TPersistent): TPersistentClass; overload;
==============================================================
⊙ IdentToInt / IntToIdent 系列函数 (Classes.pas)
==============================================================
IdentToInt 和 IntToIdent 函数用于实现字符串值和数值之间的转换。它的原理很简单,就是通过数组一一映射查找。不过一般不用直接使用这两个函数,而是使用 Delphi 中已经包装好的函数。这些函数的返回值都是 Boolean,表示转换是否成功。
function IdentToInt(const Ident: string; var Int: Longint;
const Map: array of TIdentMapEntry): Boolean;
function IntToIdent(Int: Longint; var Ident: string;
const Map: array of TIdentMapEntry): Boolean;
{ Graphics.pas }
function CharsetToIdent(Charset: Longint; var Ident: string): Boolean;
function IdentToCharset(const Ident: string; var Charset: Longint): Boolean;
function ColorToIdent(Color: Longint; var Ident: string): Boolean;
function IdentToColor(const Ident: string; var Color: Longint): Boolean;
{ Controls.pas }
function CursorToIdent(Cursor: Longint; var Ident: string): Boolean;
function IdentToCursor(const Ident: string; var Cursor: Longint): Boolean;
例子:

var
NewColor: Integer;
begin
if IdentToColor('clWindow', NewColor) then
Self.Color := NewColor;
end;

==============================================================
⊙ 结束

指定目录的图片2值化

```python # -*- coding: utf-8 -*- """指定目录的图片,自适应2值化 """ import os from PIL import Image import numpy as np imp...