2004年6月15日

VCL的诞生和设计原理(摘抄inside vcl)

VCL的诞生和设计原理
从无到有--?VCL对象生命的成型

基本的对象管理服务至少应该包含下面的服务:
l 对象的创建和初始化
l 对象方法的分配
l 对象的消灭

Object Pascal 的对象模型
使用Object pascal定义的Tobject类非常简单,只要如下两行代码就可以
Tobject =class
End;

这虽然时合法的类定义,但是实际用处不大,因为在创建类对象时必须为对象分配内存并且进行对象初始化之后才能正确的使用对象.
因此一般的类都会定义对象的构造函数(constructor或者ctor),而构造函数的目的就是为对象分配内存及进行对象初始化工作.由于对象分配了内存,因此当对象生命结束之后当然需要释放分配的内存以及进行初始化反向的工作,如此在能够归还系统资源,所以类也会定义析构函数(destructor 或者 dtor).因此最基础的类通常会定义成如下形式:
Tobject = class
  Constructor Create;
  Destructor Destroy; virtual;
End;

其中析构函数Destroy声明为virtual方法时因为在Tobject的派生类中可能会分配额外的资源,因此派生类可以改写(override)Tobject的析构函数,当派生类对象释放时,先释放他自己分配的资源,在调用Tobject的析构函数来释放Tobject为对象分配的资源.如果Tobject的析构函数不声明成虚拟方法,那么派生类的析构函数便会覆盖Tobject的析构函数,如此一来,只有派生类分配的内存会被释放,由Tobject为对象分配的资源则可能没有释放,这就造成了内存泄漏(leak)问题.

定义这样一个类:
TMyObject = class(Tobject)
  Destructor Destory ; override;
End;



Destructor TMyObject.Destory;
Begin
  //释放TmyObject分配的内存;
  inherited Destory;//调用父类的Destory;
End;

当我们使用如下程序代码创建TmyObject 时,发生了什么?
Obj := TMyObject.Create;
Object Pascal的对象模型在这行程序代码后进行了很多的工作,包括分配内存,设定字段变量数据结构,设定执行框架等工作,因此上面的程序可分解成如下的程序代码:
TMyObject.AllocateMemory;
TMyObject.InitializeSpecialFields;
Obj:= TMyObject.SetupExecFrame;

创建对象的第一步是分配内存,Object Pascal会使用内建的内存管理器为对象分配内存:
PMemoryManager = ^TMemoryManager;
TMemoryManager = record
  GetMem:function(Size:Integer):Pointer;
  FreeMem:function(p:Pointer):Integer;
  ReallocMem :function(P:pointer; Size:Integer):Pointer;
End;

虽然Object Pascal使用了默认的内存管理器,但是Object Pascal对象模型的设计似乎是允许切换内存管理器的.
在分配了对象的原始内存之后,Object Pascal的对象模型会先初始化所有的内存内容为0.
FillChar(Instance^ ,InstanceSize, 0);
然后对类中声明的特别字段进行初始化工作,如对于接口变量必须设定引用记数值为0,对于动态数组则必须初始化内存区块.
下面的TMyObject声明了Variant类型的字段变量vDate,那么Object Pascal的对象模式会对 vDate进行特别的初始化工作,至于GetHashValue虚拟方法则会进入VMT(Virtual Method Table)之中.
TMyObject =class(TObject)
Private
  VDate :Variant;
Public
  Function GetHashValue:Integer; virtual;
  Destructor Destory;override;
End;

posted @ 2004-06-15 17:40 khan 阅读(693) 评论(1) 编辑

如何设计和驱动多任务执行环境(摘抄自inside Vcl)

如何设计和驱动多任务执行环境

这一节描述,一个单任务的操作系统,以一个大型的循环(loop),不断的检查每一个应用程序是否触发了特定的事件,如鼠标,键盘。然后否定其运行效率及反应速度
( ?) 这里所说的事件驱动式操作系统是如何运作,是不是也由一个loop来检测机器的当前事件,然后将事件转化成消息(Message),然后分派给正确的应用程序来处理。

消息结构:
MyMessage = packed record
  Hwnd :HWND; //消息触发窗口句柄
  MessageID : Longint; //事件id
  wParam : Longint; // 封装事件额外信息
  lParam : Longint; // 封装事件额外信息
  time : Longint; //事件发生事件
  pt : TPont; //存储鼠标的全域坐标
end;

这节作者提出了3个问题
1. 应用程序可能拥有多个窗口,如何把触发的消息分派给正确的窗口区处理?
2. 把消息分派给应用程序或者窗口是如何办到的?
3. 分派了消息给窗口之后,窗口药如何处理此消息?

解答:
1.仔细看上面的消息结构MyMessage, 窗口句柄解决了第一个问题,由于句柄的唯一性.所以根据得到的消息触发窗口句柄,可以知道消息所对应的窗口.
我们的执行环境为每个应用程序建立一个消息队列(Message Queue),当事件发生时,执行环境久把代表他的消息分派到此消息队列中,等待应用程序从其中取出并处理.如此,程序就可以应付快速产生的数个事件而不会丢失消息.

2.窗口信息数据结构:
MyWindowClassInfo =packed record
  Style:UINT; //窗口的类型
  iWidth:integer; //窗口的宽
  iHeight:integer; //窗口的高
  lpfnWndProc:Pointer; //回调函数,窗体消息处理函数的指针
  hIcon:HICON;
  hCursor:HCURSOR; //窗口使用的光标种类
  hbrBackground:HBRUSH;//背景颜色
  lpszMenuName:PAnsiChar; //
  lpszClassName:PAnsiChar; //窗体类名
  hIconSm:HICON;
end;
当窗口发生事件并由执行环境转换为消息后,接下来就是要应用程序中对应的窗口来处理这个消息了,要让窗口处理消息最简单的方法就是让执行环境调用窗口提供的函数,并且传递消息给此函数来处理,但问题时要如何方窗口提供函数给执行环境,执行环境才能够知道在消息产生后要调用什么函数.
MyWindowClassInfo数据结构中有一个lpfnWndProc字段是一个Pointer类型的字段,代表应用程序可以把任何的函数地址指定给此字段以代表可以处理此窗口消息的函数.由于这个函数会有执行环境调用,这种函数也被称为回调函数(Callback Routine).
由于执行环境在调用应用程序提供的回调函数时必须传递和消息相关的信息给此函数,因此执行环境必须定义此函数的原型(Prototype):
function WindowProc(
  Window:HWnd; //窗口句柄
  AMessage:UNIT; //分派的消息
  Wparam:WPARAM;
  Lparam:LPARAM
): LRESULT;stdcall;export;

当应用程序遵照回调函数的原型,提供了适当的处理窗体消息函数并指定给MyWindowClassInfo中的lpfnWindProc字段,那么应用程序就可以在窗体事件触发后自动调用这个回调函数来处理消息了

3.这个问题由如下源代码实现:
case AMessage of
  WM_PAINT //窗体重画
  .. .. ..
  WM_DESTROY//窗体销毁
  .. .. ..

posted @ 2004-06-15 17:37 khan 阅读(506) 评论(0) 编辑

RetroGuard的使用方法(转)

RetroGuard的使用方法:

RetroGuard是一个很不错的Java混淆器,而且在JBuilder7的企业版中也带了这个混淆器。
RetroGuard本身是一个Java程序(一个Jar包),所以要使用的话必须先安装JDK。运行前需要设置一些环境变量,除了要在PATH中包含java的执行路径以外,还要在CLASSPATH中加入D:\retroguard\RetroGuard.jar(假设你的RetroGuard.jar在D:\retroguard下)。如果没有设置CLASSPATH的话也不要紧,只不过每次运行时就要通过 -classpath 加上路径参数。设置好运行环境以后还要准备好你要混淆的.jar文件包,至于如何把编译好的类打包成.jar文件就不在这里多说了。
RetroGuard的执行格式是:
java RetroGuard [输入JAR文件 [输出JAR文件 [“保留脚本文件” [日志文件]]

其中“保留脚本文件”描述了那些类、函数、变量名必须保留,不能被混淆;日志文件是执行后生成的记录文件,记录的错误、混淆情况等信息。
对于“保留脚本文件”可以通过一个图形界面RGgui来设置,执行命令是:
java RGgui
运行后,可以根据提示选择输入Jar文件,选择要保留的类、函数、变量,完成后会自动生成“保留脚本文件”。其实通常情况下只要保留运行的起始类名称就可以了。
如果你的Jar包用到了第三方的函数库,比如Siemens的API,那么就要在 -classpath 参数中指定。例如:
java -classpath c:\siemens\smtk\6688i1_b8\lib\api.jar RetroGuard in.jar out.jar script.txt log.txt

如果RetroGuard混淆失败,可以查看日志文件取得错误信息。
这里我发现一个问题,就是遇到有类似 ...$$1.class 这样的文件,RetroGuard会提示出错,后来我修改了他的源代码,直接跳过了对这种文件的处理。

JBuilder7中RetroGuard的设置和用法:
JBuilder7的企业版中带了RetroGuard v1.1,再JBuilder7安装路径下的retroguard-v1.1目录中。在JBuilder7中可以直接调用RetroGuard。设置的方法如下:
点菜单Tools -> Configure Obfuscators配置混淆器,点左下角的New...新建一个混淆器,选择RetroGuard的路径,完成配置。使用起来要通过Archive Builder,点菜单Wizards -> Archive Builder...,Archive type选择MIDlet(这里仅仅介绍针对J2ME应用),然后根据提示一步一步往下设置,到达第9步也就是混淆器设置的时候,选中obfuscate the contents of the archive,Obfuscator选择混淆器,Use the classes specified blow下面添加要保留的类(默认保留起始执行类),结束设置。
这样当每次编译的时候,就会自动生成混淆过的Jar文件包了

posted @ 2004-06-15 17:34 khan 阅读(1268) 评论(0) 编辑

写了一些java的字符处理函数,可能对大家有些用

/**将一串以空格分隔并以空格结尾的字符串转换为字符串数
*@param s 以空格分隔并以空格结尾的字符串
*@return String[] 字符串数组 */
private
 String[] StringToArray(String s) {
    String[] strs = new String[StringCount(s,' ')];
    String tmp = s;
    for (int i = 0; i < strs.length; i++) {
        strs[i] = tmp.substring(0, tmp.indexOf(" "));
        tmp = tmp.substring(tmp.indexOf(" ") + 1);
    }
    return strs;
}






/**
 统计以空格分隔并以空格结尾的字符串转换为字符串数组中的字段个数
*@param s 以空格分隔并以空格结尾的字符串
*@param cut 分割字符标记
*@return int 字段个数 */
private
 int StringCount(String s,char cut) {
    int j = 0;
    for (int i = 0; i < s.length(); i++) {
        if (s.charAt(i) == cut)
        j++;
    }
    return j;
}





/**
替换空格分割的字符串中第index个子串
*@param str 字符串
*@param index 索引
*@param subString 用来替换的的子串 
*@return String 替换后的字符串 */
private
 String replaceStr(String str, int index, String subString) {
    if (index < 61)
        return str.substring(0, pos(str, index, ' ')) + " " + subString + str.substring(pos(str, index + 1, ' '), str.length());
    else
        return str.substring(0, pos(str, index, ' ')) + " " + subString;
}



/**
返回某字符串中,indexcut实际所在的实际位置
*@param str 源字符串
*@param index 分割标志所在序号
*@param cut 分割字符 
*@return int 位置 */
private
 int pos(String str, int index, char cut) {
    int i = 0, j = -1, tmp = -1;
    while (i < str.length()) {
        i = str.indexOf(cut, i + 1);
        tmp = i;
        if (i != -1) j++;
        else break;
        if (j == index)break;
    }
    return tmp;
}

posted @ 2004-06-15 17:32 khan 阅读(947) 评论(0) 编辑