delphi教程

知识博客专集

如何在C Builder中使用Delphi控件


使c builder使用delphivcl类库的方法基于windows中较通用的dll方式。在实际应用中 找到了将vcl控件转化为dll库,在c builder动态调用dll。此法适用于非可视vcl控件。

假令在delphi中有一sample控件,有属性actived、pro1、pro2,欲将这个控件转到 c builder中使用。一、delphi中dll的制作在delphi中新建一dll项目sampledll,时在此项 目中create一个新的类tttempcomp基类为tcomponent即也为一个控件,在其中加入一个 constructorcreate1,但不作任何动作;在dll中加入要导出的属性的function(actived、 pro1、pro2)&create、destroy的框架,exports中加入导出的function、procdure名称 ;在dll的主过程中对ttempcomp的实例temp1进行create1,另外保存出口和设置exitproc; 在opensample的函数中加入hwctrl:=sample1.create(temp1)对sample进行实例化,对 closesample和其它属性加入相应的语句;二、c builder中dll的使用将delphi中生成的dll 用implib生成lib文件加入c builder的工程文件;

在头文件中加入

extern \”c\” __declspec(dllimport) bool _stdcall opensample(void);

extern \”c\” __declspec(dllimport) void _stdcall closesample (void);

extern \”c\” __declspec(dllimport) bool _stdcall actived (void);

extern \”c\” __declspec(dllimport) int _stdcall pro1 (void);

extern \”c\” __declspec(dllimport) int _stdcall pro2 (void);

在opensample后你就可以使用delphi中的属性actived、pro1、pro2

三、参考dll source如下

library sampledll;
uses
sysutils, classes, sample;
type
ttempcomp = class(tcomponent)
private
public
constructor create1;
published
end
var
sample1 :sample;
saveexit :pointer;
temp1 :ttempcomp;
constructor ttempcomp.create1;
begin
// inherited create(self);
end;
/==============================================
function opensample: boolean; stdcall; export;
begin
hwctrl:= sample1.create(temp1);
if sample1.actived then result:=true;
end;
procedure closesample; stdcall; export;
begin
sample1.destroy;
end;
function actived: boolean; stdcall; export;
begin
result:=sample1.actived;
end;
function pro1: interger; stdcall; export;
begin
result:= sample1.pro1;
end;
function pro2: interger; stdcall; export;
begin
result:= sample1.pro2;
end;
/==============================================
procedure libexit; far
begin
if sample1.actived =true then
sample1.destroy;
exitproc:=saveexit;
temp1.destroy;
end;
exports
opensample,closesample,actived ,pro1,pro2;
begin
temp1:=ttempcomp.create1;
saveexit:=exitproc;
exitproc:=@libexit;
end.

解释:

因为vcl控件都继承于tcomponent,tcomponent的构造函数需要一个aowner并且也是 tcomponent,vcl控件的create、destroy都由控件的拥有者来动作,也就是aowner;所以我 在此dll中新设置了一个ttempcomp类继承于tcomponent且性设置了一个constructor(构造函 数)create1,而实际构造时什么都不做,以次作为要create的aowner;

其他还有一种办法就是用application作为aowner但是它是基于tform的作出来的dll太大 ;其实,inprise(原borland)尽可以象microsoft一样用一些象dcom类似的组件形式使得产 品在同一产品时代保持一定的互用性,来增加产品群的生命力。

DELPHI 在DLL中封装的VCL窗体Tab键响应的问题


在dll中的子窗体不会响应tab按键的,这个时候就需要手动去指定tab键的操作,但是前提是主窗体要向这个窗体发送一个消息,一个tab键按下的消息。基本顺序是这样的: nbz平坦软件园

1. 主窗体用hook技术捕获tab按键并向活动子窗体发送一个tab键按下的消息nbz平坦软件园

2. 子窗体手动定义一个tab键被按下的处理(需要用到formkeydown事件)。nbz平坦软件园

3. 移动到下一个焦点的winapi函数:perform(wm_nextdlgctl,0,0);nbz平坦软件园

4.移动到上一个焦点的函数:perform(wm_nextdlgctl,1,0)nbz平坦软件园

dll中封装的子窗体代码:nbz平坦软件园

procedure tform1.formkeydown(sender: tobject; var key: word;nbz平坦软件园
  shift: tshiftstate);nbz平坦软件园
beginnbz平坦软件园
  if (ssshift in shift) and (key = vk_tab) thennbz平坦软件园
    perform(wm_nextdlgctl,1,0)nbz平坦软件园
  else if key = vk_tab thennbz平坦软件园
    perform(wm_nextdlgctl,0,0);nbz平坦软件园
end;
nbz平坦软件园

主窗体的hook代码:nbz平坦软件园

function keyhook(ncode: integer; wparam: wparam; lparam: lparam):lresult;nbz平坦软件园
//实现回调函数nbz平坦软件园
beginnbz平坦软件园
  //tab键处理函数nbz平坦软件园
  if (ncode = hc_action) and (wparam = vk_tab) and (lparam = 983041) thennbz平坦软件园
  beginnbz平坦软件园
    if screen.activeform.handle <> form2.handle thennbz平坦软件园
      sendmessage(screen.activeform.handle, wm_keydown,vk_tab,0);//直接跳到下一个控件上nbz平坦软件园
  end;nbz平坦软件园
  result := callnexthookex(hook,ncode,wparam,lparam);nbz平坦软件园
end;
nbz平坦软件园

procedure tform2.formcreate(sender: tobject);nbz平坦软件园
//窗体创建过程中的代码nbz平坦软件园
beginnbz平坦软件园
  //设置键盘钩子nbz平坦软件园
  hook := setwindowshookex(wh_keyboard, @keyhook, 0, getcurrentthreadid);nbz平坦软件园
end; nbz平坦软件园
nbz平坦软件园

nbz平坦软件园
procedure tform2.formdestroy(sender: tobject);nbz平坦软件园
//窗体销毁的代码nbz平坦软件园
beginnbz平坦软件园
  //释放键盘钩子nbz平坦软件园
  unhookwindowshookex(hook);nbz平坦软件园
end;
nbz平坦软件园

varnbz平坦软件园
  hook: hhook;//设置hook单元变量nbz平坦软件园
 nbz平坦软件园
{在implementation部分上面interface部分声明键盘钩子回调函数; 其参数传递方式要用 api 的 stdcall}nbz平坦软件园
function keyhook(ncode: integer; wparam: wparam; lparam: lparam):lresult;stdcall;
nbz平坦软件园

用Delphi获取当前系统时间


在开发应用程序时往往需要获取当前系统时间。尽管y2k似乎已经平安过去,但在我们新开发的应用程序中还是要谨慎处理“时间”问题。jfc平坦软件园

  在《融会贯通–delphi4.0实战技巧》(以下简称“该书”)第89页专门介绍了两种获取当前系统时间的方法,但这两种方法都存在不足或错误,以下就此进行讨论。jfc平坦软件园

  该书第一种方法是利用time()函数获得当前系统时间,返回结果是tdatetime结构类型的变量。例如:jfc平坦软件园

  procedure tform1.button2click(sender: tobject);jfc平坦软件园

  varjfc平坦软件园

  datetime:tdatetime;jfc平坦软件园

  beginjfc平坦软件园

  datetime:=time();jfc平坦软件园

  caption:=datetostr(datetime) \’ \’ timetostr(datetime);jfc平坦软件园

  end;jfc平坦软件园

  但不论何日期,其结果却都是99-12-30 xx:xx:xx, 显然日期出错了。通过分析delphi的帮助,time()用于返回正确的“时间–时分秒”即timetostr(datetime),而不应该用于返回“日期”。事实上,单独用于返回日期的系统函数是date。jfc平坦软件园

  那么有什么是既可返回正确的“时分秒”又可返回正确的“年月日”呢? 可以用now函数,例如:jfc平坦软件园

  procedure tform1.button1click(sender: tobject);jfc平坦软件园

  varjfc平坦软件园

  mytime: tdatetime;jfc平坦软件园

  beginjfc平坦软件园

  mytime:=now;jfc平坦软件园

  caption:=datetostr(mytime) \’ \’ timetostr(mytime);jfc平坦软件园

  //或直接用 caption := datetimetostr(now);jfc平坦软件园

  end;jfc平坦软件园

  用now返回的日期格式中年只有2位,即2000年显示为00, 这似乎不太令人满意. 此外now和time都只能获得精确到秒的时间,为了得到更精确的毫秒级时间,可以使用api函数getsystemtime,它对应的tsystemtime类型的定义为:jfc平坦软件园

  tsystemtime = recordjfc平坦软件园

  wyear: word;jfc平坦软件园

  wmonth: word;jfc平坦软件园

  wdayofweek: word;jfc平坦软件园

  wday: word;jfc平坦软件园

  whour: word;jfc平坦软件园

  wminute: word;jfc平坦软件园

  wsecond: word;jfc平坦软件园

  wmilliseconds: word;jfc平坦软件园

  end;jfc平坦软件园

  显然,在程序逻辑中还能够方便地使用其结构成?时—各类时间值,因此使用函数getsystemtime具有很大优越性。但该书中该函数的用法是错误的,通过查阅windows sdk帮助可知,该函数原型为:jfc平坦软件园

  void getsystemtime(lpsystemtime lpst),参数指针lpst获取系统时间,因此可如以下程序段实现:jfc平坦软件园

  procedure tform1.button3click(sender: tobject);jfc平坦软件园

  varjfc平坦软件园

  systime: tsystemtime;jfc平坦软件园

  beginjfc平坦软件园

  getsystemtime(systime);jfc平坦软件园

  caption:=inttostr(systime.wyear) \’ \’ inttostr(systime.wmonth);jfc平坦软件园

  //if systime.wyear>2000 thenjfc平坦软件园

  // ……在程序逻辑中利用获取的各类时间值jfc平坦软件园

  end;jfc平坦软件园

  综合以上讨论,获取当前系统时间利用函数getsystemtime比较方便而且灵活。jfc平坦软件园

权限控制(delphi TActionList方案)


权限控制(delphi tactionlist方案)
在软件开发中,为软件加入权限控制功能,使不同的用户有不同的使用权限,是非常重要的一项功能,
由其在开发数据库方面的应用,这项功能更为重要。
但是,要为一个应用加入全面的权限控制功能,又怎样实现呢?
大家知道,现在的应用,一般均以菜单访问功能的形式出现,按照常规的做法,
只要让注册进入应用的不同用户,可以访问不同的功能菜单,从而实现功能权限的控制,
但是,有这样一个问题,此种方法便无能为力,现在的应用软件,为了提高软件的易操作性,
同一功能可能有多种不同的访问方式,如工具条,右键菜单等;
同样,同一个功能,也可能在软件的不同地方被调用,而不仅仅被限制为用程序的主菜单来调用,
这样,才能保证应用的易用性。
写到这,问题已经非常清楚,对于要限制的软件功能,仅通过一次代码设计,
无论在整个应用的任何地方或通过何种形式调用此功能,都能被功能权限所限制。

笔者利用delphi平台作应用开发时,通过delphi7提供的vcl控件解决了这一问题。
在了解如何实现功能权限控制之前,得先看一下delphi7提供的新控件tactionlist,
通过tactionlist,应用程序可以统一管理其taction,这里的action,可以理解为应用程序的功能。
在应用的设计期间,可以通过tactionlist编辑器将功能(action)加入tactionlist,
将action加入tactionlist后,就可能通过object inspector设置action的属性或为其建立事件句柄。
在这里,我们可以用action的onexecute事件句柄实现具体的功能,如下代码来显示一个操作窗体:

procedure tfrmmain.setuserexecute(sender: tobject);

begin

   frmuser.showmodal;

end;

  当要限定这一功能时,可能利用taction的enabled,将其设为false,此功能对于用户将被屏蔽掉,
如果要此功能对用户不可见,则可以设定visible为false。

  当成功能的建立了tactionlist后,可能有人问,如果使用其中的action,
在delphi7中,象tbutton、tmenuitem、tspeedbutton、tradiobutton等控件,均有一个属性action,
正是通过它,我们可以将menu或button连接到tactionlist中taction,从而实现功能按钮或菜单的功能。

在理解了delphi中的tactionlist及taction之后,就可以看看功能权限的具体实现方法。

第一步,建立两张表,一张表存储用户信息,另一张表存储权限定义。

用户信息表user结构如下:
userid(string/用户的id号,为表关键字)
username(string用户名称)
userpassword(string,用户口令)

userright表结构如下:

userid(string,用户的id号,为表关键字)
actioncaption(string,存储功能的名称,即action的caption属性值)
actionenable(boolean,存储功能是否可以访问,即action的enable 属性值)
actionvisible(boolean,存储功能是否可见,即action的visible属性值)

第二步,增加用户时填加用户功能权限
当向user表中增加用户时,需要向userright中增加功能设置记录,先看看下面的实现代码:

procedure tfrmuser.n1click(sender: tobject);

var

   i:integer;

   action:taction;

begin

//add action into user right cds.

   with frmmain do begin

     for i:=0 to actionlist1.actioncount-1 do begin

       action:=actionlist1.actions[i];

      cdsuserright.appendrecord([cdsuser.fieldbyname(’username’).asstring,taction(action).caption,taction(action).enabled,i]);

    end;

  end;

end;

在这段代码中,用到了tactionlist的两个属性,一个是actioncount,另一个是actions。
actioncount表示tactionlist中有多少功能,
即action,actions是一个数组属性,
通过索引可能访问每一个taction,从而可以设置其具体的属性,象上面提到的enable及visible,
从而达到限制的目的,通过这段代码,将应用程序的所有功能都加入了userright表中。

第三步,可以用grid对上一步产生的表进行编辑操作

第四步,利用第二、三步产生的功能限制表userright,限制用户的权限,
这可以在应用程序的主窗体的oncreate 中实现。

procedure tfrmmain.formcreate(sender: tobject);

const

   testuser=’yh’;

var

   cdsright:tclientdataset;

   i:integer;

begin

  //set right of function

   cdsright:=tclientdataset.create(self);

   try

   cdsright.loadfromfile(’right.cds’);

   cdsright.addindex(’id’,’username;actioncaption’,[],’’,’’,0);

   cdsright.indexname:=’id’;

  for i:=0 to actionlist1.actioncount-1 do begin

     if cdsright.findkey([testuser,taction(actionlist1.actions[i]).caption]) then

     taction(actionlist1.actions[i]).enabled:=cdsright.fieldbyname(’actionenable’).asboolean;

   end;

   finally

     cdsright.close;

     cdsright.free;

  end;

end;

这段代码中,假设当前的用户id为yh,同时只设定了功能的enable属性

Delphi通用数据库连接文件(.udl)方法


  一、右键—新建—文本文档,重命名为 connection.udl 。

  二、双击打开 connection.udl 按提示操作配置数据库,选择本地或远程数据库,配置好后退出。

  三、使用delphi 控件tadoconnection连接代码:

  form1的oncreate事件如下

    try
      adoc.connected := false;
      adoc.connectionstring := \’file name=\’ extractfilepath(paramstr(0)) \’\\pymee.udl\’;
      adoc.provider := extractfilepath(paramstr(0)) \’\\pymee.udl\’;
      adoc.connected := true;
    except
      showmessage(\’连接失败,请重新配置pymee.udl文件\’);
    exit;
  end;

  四、如果你要把应用程序分发到局域网中使用,数据库服务器没有改变,直接复制应用程序就可以了,不用再配置connection.udl 文件。当在另外一个局域网中安装有不同名的数据库服务器时,右键用记事本打开connection.udl 文件,会看到:

  [oledb]
  ; everything after this line is an ole db initstring
  provider=sqloledb.1;persist security info=false;user id=sa;initial catalog=jbdatabase0;data source=jiabao9\\jiabao9
  上面几行文件,这是新建connection.udl 文件,并配置好数据库后自动生成的代码。我默认的登录用户为sa,密码为空,数据库为bdatabase0,局域网数据库服务器名为jiabao9\\jiabao9(ip:192.168.0.9),你可按照自己的实际环境进行配置。

  五、不同的局域网数据库服务器,你只要修改配置connection.udl连接文件就可以了,不用修改delphi代码,很方便哦。

  六、故障处理:运行应用程序时,出现错误提示“无效的授权说明”,是因为前面我们已经新建了connection.udl 连接,现在只要把delphi控件tadoconnection的属性connectionstring值删除为空,这是静态连接数据库的字符串值,所以删除connectionstring值后,再重新编译就可以了

delphi调试技巧


delphi开发的调试技巧
1、设置调试选项
  delphi主菜单。【project】->【options】,或单击工程管理器中的【project options】按钮。显示project options,选择complier

  <1>选中 debug information :把调试信息嵌入到dcu单元文件,会加大编译后的dcu文件,但不会影响最后生成的exe的大小和执行速度。同时激活【search】菜单中的【find error】命令。
  当文件中包含调试信息并得到一个运行时错误(run-time error),记录下delphi提供的16进制地址,在【search】->【find error】中输入,delphi将重新编译程序,并停留在产生错误的命令行。

  <2>local symbols:决定调试器能否看到在implementation中定义的局部变量。

  <3>reference infoj选项/definition only选项
是否产生应用程序中对象标识符的引用信息。信息和对象代码存在dcu文件中,可以使用对象浏览器查看。如果definition only 选项同时选中,编译器将生成标识符定义位置的信息,能够激活对象浏览器的refenrence页。如果不选,dcu文件更小。

  编辑器命令
  {&d} debug information
  {&l} local information
  {&y} symbol information
  {&c} assertions

  2、使用内部调试器

  <1> 命令行参数
  在【run】->【parameters】中设置

  <2>断点

  条件断点
  使源代码断点只有在满足某个条件时才有效。
  在【view】->[debug windows]->【breakpoints】打开“breakpoint list”对话框,右击断点,从弹出菜单中选择属性。打开“source breakpoint properties”对话框,在condition中输入条件,在对话框中点击按钮【advanced】,可以设置更多选项,“ignore subsequent exceptions”使调试程序遇到下一个断点时不中断;选中“handle subsequent exception”则调试程序遇到下一个断点时恢复默认性能。
也可以通过【run】->【add breakpoint】设置。

  数据断点
  特定地址的内存被修改时才使程序挂起。【run】->【add breakpoint】->【data breakpoint】命令添加,或者在breakpoint list对话框中右击,选择add data breakpoint。输入监测内存区域的开始地址和长度(字节数)

  地址断点
  特定地址的代码执行时就使程序挂起。通常在cpu窗口中设置。

  模块加载地址
  在一个模块加载时使程序挂起。【run】->【add breakpoint】

  断点组
  使用断点组,任何一个断点都能设置成使其他断点有效或无效,可以通过断点算法来查找特殊的错误。

  3、单步执行

  【step over】 (f8功能键),【trace into】(f7功能键),【run to cursor】(f4功能键)
临时使程序暂停【program pause】。
ctrl f5 【add watch】,ctrl f7 【evaluate/modify】

  4、其他工具

  <1>debug inspector 适合查看由许多数据组成的复合数据,诸如类和记录。单击省略号按钮,可以修改数据的值。
  <2>计算和修改evaluate/modify,可以改变变量的值,但不能访问超出域外的函数和变量。
  <3>访问调用栈 【view】->【debug windows】->【call stack】,可以看到函数和过程的调用情况及传递给他们的参数。
  <4>查看线程,如果应用程序运行了多线程,可以通过【view】【debug windows】【threads】查看线程的状态,可以查看某一线程的源代码,选择当前线程。(ctrl alt t)
  <5>事件日志
记录调试期间发生的事情。【view】【debug windows】(ctrl alt v),可以在【tools】【debugger options】对话框“debugger”选项组或事件日志的本地菜单配置事件日志功能。记录的事件包括:进程开始、终止、模块加载断点、发给应用程序的windows消息以及使用outputdebugstring()的输出。
  <6>模块视图
获取所有加载到应用程序进程中的模块。(ctrl alt m)
在过程、函数、事件内部, shift ctrl 向上的方向键  可跳跃到相应的过程、函数、事件的定义.相反,在过程、函数、事件的定义处,shift ctrl 向下的方向键 可跳跃到具体过程、函数、事件内部
ctrl shift c:编写申明或者补上函数.
如:
procedure tt.a(aa: string);  //函数申明
安下:ctrl shift c后,会写上
procedure tt.a(aa: string);
begin
end;

shift ctrl e 显示 explorer

crtl shift n (n=1,2,3,4……)
定义书签
crtl n (n=1,2,3,4……)
跳到书签n

alt 鼠标左键可以块选代码,删除对齐的重复代码非常有用。
ctrl b           buffer list窗口。
ctrl i           同tab键。
ctrl m           同enter键。
ctrl n           同enter键,但光标位置保持不变。
ctrl t           删除光标右边的一个单词。
ctrl y           删除光标所在行。

ctrl shift ↑    光标在函数体内时,将光标快速移至当前函数声明处。
ctrl shift ↓    光标在函数声明行时,将光标快速移至函数定义处。
ctrl shift c     声明一个过程或函数后,直接生成过程或函数的名称、begin、end;
ctrl shift e     光标在edit窗口和explorer窗口间切换。
ctrl shift j     弹出delphi语句提示窗口,选择所需语句将自动完成一条语句。
ctrl shift t     在光标行加入to-do注释。
ctrl shift y     删除光标之后至本行末尾之间的文本。

ctrl f3          call stack窗口。
ctrl f4          等于file菜单中的close项。

ctrl 鼠标转轮    加速滚屏。

shift f8         调试时弹出cpu窗口。
shift f10        等于鼠标右键(windows快捷键)。

alt f4           关闭所有编辑框中打开的源程序文件,但不关闭项目。

ctrl enter 定位到单元文件
shitf 箭头 选择 
如果同时按住alt shitf 箭头, 再进行选择, 则是一种区域选择, 而不是行选择, 粘贴的
时候也就是插入粘贴了. 
f9 运行 
ctrl f9编译
f8 step over (步进式调试不进入子过程)
f7 trace into (步进式调试同时追踪进入子过程)
f11,f12 切换editor,inspector,form designer. 
ctrl alt f11 弹出工程管理器 
ctrl f弹出查找对话框
ctrl r弹出替换对话框
ctrl shitf c 类过程代码补全(只要在定义部分定义一过程或一函数头则自动帮你生成
实现部分的套架;反之也成)
但是delphi 中怎样跳格呢? 其实也是有办法的, 就是ctrl k,i向后, ctrl k,u向前. 
有时候反复输入什么东西是不是很繁? ctrl shift r录制键盘宏, 比如我经常做的就是: 
下箭头-下箭头-home-回车-ctrl v, 再键入一次ctrl shift r录制完成, 之后您可以使用
ctrl shift p, 重复宏,还是省了一些事的. 
另外在deliphi中有一个很有用的快键极其有用
这就是ctrl j;列如:在一过程中你输入ifb后再按ctrl j你回去看看效果吧,可能你会大
吃一惊。这就是code insight.

ctrl shift u  //块缩进/反缩进 
ctrl shift i

取消书签: 在书签n处,再按一次 shift ctrl n

ctrl e 一种查找挺好的,其余的都被你们说玩了

按住ctrl再点对象、过程、函数,转到其定义原型,即便是delphi vcl源代码中的unit。

alt f4 关闭delphi [:d]

ctrl k o,ctrl k n  将选中的整块代码都转为小写或大写
ctrl f 查找
ctrl l 继续查找

在inspector,可以用ctrl tab切换properties页和events页
ctrl enter可进入相应的设置,如在preperties页的font按ctrl enter
在events中按ctrl enter可进入相应代码编辑

ctrl shift i : 同时推进2格
ctrl shift u : 同时退后2格[/red]

还有一招,我经常用的,不知大家常不常用
按住alt用鼠标选择文本
或者按住alt加shift,用方向键选择文本
没试过就赶快去试试![:d]

在form上放一个tedit控件,然后在代码中写入“edit1.”,稍等一下,会出现提示。
在“edit1.”未写入前,要想使用这个提示功能,可以按住windows键,然后按空白键。
(只有windows键盘可以用此功能!)

 
ctrl space与输入法冲突,把输入法的有无输入法快捷键从ctrl space该为其他键组合
就行了,我是改成了ctrl shift space.
然后在unit中,比如输入fo这是按下ctrl space就会出现一系列以fo开头的变量,函数。

ctrl backspace 后退删除一个词。好像是到 . 为止。

在object inspector窗口按tab键将光标移动到属性名区,然后键入属性名的开头
字母可快速定位到该属性

————————————————————————

一、以下快捷键指的是对窗体表格上组件的操作:
●快捷键1:
【ctrl up】~向上移动当前组件(精确);
【ctrl left】~向左移动当前组件(精确);
【ctrl down】~向下移动当前组件(精确);
【ctrl right】~向右移动当前组件(精确);
注:
以上快接键中再加shift进行组合(比如【ctrl shift right】)即可实现粗略调整;
●快捷键2:
【shift up】~减小当前组件的高度;
【shift left】~减小当前组件的宽度;
【shift down】~增加当前组件的高度;
【shift right】~增加当前组件的宽度;
注:
以上up,left等指的是方向键;

二、以下快捷键指的是在对象观察器上的操作:
●快捷键3:
【f11】~切换到对象观察器(注:如果连续按f11则将实现在对象观察器、窗体表格、代码编辑器之间切换);
【ctrl down】~下拉当前窗体的组件列表;
【ctrl enter】~编辑带…的属性值(如组件的字体tfont);
【alt down】~下拉组件当前属性选单队列(如align->alnone,alleft,alright等);
【ctrl tab】~在属性列表及事件列表中切换;

三、菜单快捷键:
●快捷键4:
【f12】,【shift f12】,【ctrl f12】等在delphi的菜单中已有定义的在此不必罗嗦.

技巧:
如何选择被组件覆盖了的窗体(比如某组件的align属性为alclient)?
方法一、按esc键,一层一层的往后选,直到窗体被选为当前组件为止;
方法二、按shift的同时单击鼠标左键,一步即可选定(推荐);
方法三、按f11选定对象观察器,然后切换到属性列表,再用上面快捷键3中的【ctrl down】

ctrl up向上移动选中组件(微调);  ctrl left向左移动选中组件(微调);
ctrl down向下移动选中组件(微调)  ;ctrl right向右移动选中组件(微调);
shift up减小选中组件的高度;  shift left减小选中组件的宽度;
shift down增加选中组件的高度;  shift right增加选中组件的宽度;

ctrl 鼠标拖动 可以选中一个控件上层的其他控件而不选中其本身!

delphi中的快捷方式一览(完全正式版)
1.shift 鼠标左键    先选中任一控件,按键后可选中窗体(选中控件后按esc效果一样)
2.shift f8          调试时弹出cpu窗口。
3.shift f10         等于鼠标右键(windows快捷键)。
4.shitf 箭头        选择
5.shift f12        快速查找窗体并打开
6.f7               (步进式调试同时追踪进入子过程)
7.f8               (步进式调试不进入子过程)
8.f9                运行
9.f12               切换editor,form
10.alt f4           关闭所有编辑框中打开的源程序文件,但不关闭项目
11.alt 鼠标左键      可以块选代码,用来删除对齐的重复代码非常有用
12.ctrl f9          编译
13.ctrl shift n(n=1,2,3,4……)  定义书签
14.ctrl n(n=1,2,3,4……)跳到书签n
15.ctrl shift n    在书签n处,再按一次 取消书签
16.ctrl pageup      将光标移至本屏的第一行,屏幕不滚动
17.ctrl pagedown    将光标移至本屏的最后一行,屏幕不滚动
18.ctrl ↓          向下滚动屏幕,光标跟随滚动不出本屏
19.ctrl ↑          向上滚动屏幕,光标跟随滚动不出本屏
20.ctrl home        将光标移至文件头
21.ctrl end         将光标移至文件尾
22.ctrl b           buffer list窗口
23.ctrl i           同tab键
24.ctrl j           (弹出delphi语句提示窗口,选择所需语句将自动完成一条语句)代码模板
25.ctrl m           同enter键。
26.ctrl n           同enter键,但光标位置保持不变
27.ctrl t           删除光标右边的一个单词
28.ctrl y           删除光标所在行
29.ctrl c           复制
30.ctrl v           粘贴
31.ctrl x           剪切
32.ctrl z           还原(undo)
33.ctrl s           保存
34.ctrl f           查找
35.ctrl l           继续查找
36.ctrl r           替换
37.ctrl enter       定位到单元文件
38.ctrl f3          弹出call stack窗口
39.ctrl f4          等于file菜单中的close项
40.ctrl backspace   后退删除一个词,直到遇到一个分割符
41.ctrl 鼠标转轮    加速滚屏
42.ctrl o u         切换选择块的大小写(注意松开o后再按u,ctrl保持按下)
43.ctrl k o         切换选择块为小写(注意松开k后再按o,ctrl保持按下)
44.ctrl k n         切换选择块为大写(注意松开k后再按n,ctrl保持按下)
45.ctrl shift g     插入guid
46.ctrl shift t     在光标行加入to-do注释
47.ctrl shift y     删除光标之后至本行末尾之间的文本
48.ctrl shift c     编写申明或者补上函数,绝好!!!
49.ctrl shift e     显示explorer
50.ctrl tab         在inspector中切换properties页和events页
51.ctrl shift u     代码整块左移2个空格位置
52.ctrl shift i     代码整块右移2个空格位置
53.ctrl shift ↑    在过程、函数、事件内部, 可跳跃到相应的过程、函数、事
                     件的定义(在interface和implementation之间来回切换)
54.ctrl shift ↓    在过程、函数、事件的定义处, 可跳跃到具体过程、函数、事件内部(同上)
55.tab              在object inspector窗口按tab键将光标移动到属性名区,然后键入属性名的开头
                     字母可快速定位到该属性
56.ctrl alt         按着ctrl alt之后,可用鼠标选择一个矩形块中的代码,
                     并可比它进行复制,粘贴
57.shift ↓、↑、→、← 以1像素单位更改所选控件大小
58.ctrl ↓、↑、→、←以1像素单位更改所选控件位置
59.ctrl e           快速选择(呵呵,试试吧,很好玩的)                                                           
———gexperts中———
60.ctrl alt c       注释块
61.ctrl alt u       取消注释块
62.ctrl alt h       生成头(更详细的设置在gexperts配置的editor experts属性页)
63.ctrl alt o       选择对应分隔符之间的语句
64.ctrl alt v       在对应的分隔符之间来回跳转(与已有快捷键有冲突,请更改)
———gexperts中———

ctrl alt [left arrow] 在gexpert里快速选定begin与其对应end之间所有代码的方法。
alt f11 在出现的窗口中选择一个unit将在当前unit的implementation部分自动uses你刚才选择的unit

Delphi常用字符串函数


一、字符转换函数hmz平坦软件园
1、ascii()hmz平坦软件园
返回字符表达式最左端字符的ascii 码值。在ascii()函数中,纯数字的字符串可不用‘’括起来,但含其它字符的字符串必须用‘’括起来使用,否则会出错。hmz平坦软件园
2、char()hmz平坦软件园
将ascii 码转换为字符。如果没有输入0 ~ 255 之间的ascii 码值,char() 返回null 。hmz平坦软件园
3、lower()和upper()hmz平坦软件园
lower()将字符串全部转为小写;upper()将字符串全部转为大写。hmz平坦软件园
4、str()hmz平坦软件园
把数值型数据转换为字符型数据。hmz平坦软件园
str (<float_expression>[,length[, <decimal>]])hmz平坦软件园
length 指定返回的字符串的长度,decimal 指定返回的小数位数。如果没有指定长度,缺省的length 值为10, decimal 缺省值为0。hmz平坦软件园
当length 或者decimal 为负值时,返回null;hmz平坦软件园
当length 小于小数点左边(包括符号位)的位数时,返回length 个*;hmz平坦软件园
先服从length ,再取decimal ;hmz平坦软件园
当返回的字符串位数小于length ,左边补足空格。hmz平坦软件园
二、去空格函数hmz平坦软件园
1、ltrim() 把字符串头部的空格去掉。

2、rtrim() 把字符串尾部的空格去掉。hmz平坦软件园

三、取子串函数hmz平坦软件园
1、left() hmz平坦软件园
left (<character_expression>, <integer_expression>)hmz平坦软件园
返回character_expression 左起 integer_expression 个字符。
hmz平坦软件园

2、right() hmz平坦软件园
right (<character_expression>, <integer_expression>)hmz平坦软件园
返回character_expression 右起 integer_expression 个字符。
hmz平坦软件园

3、substring()hmz平坦软件园
substring (<expression>, <starting_ position>, length)hmz平坦软件园
返回从字符串左边第starting_ position 个字符起length个字符的部分。
hmz平坦软件园

四、字符串比较函数hmz平坦软件园
1、charindex()hmz平坦软件园
返回字符串中某个指定的子串出现的开始位置。hmz平坦软件园
charindex (<’substring_expression’>, <expression>)hmz平坦软件园
其中substring _expression 是所要查找的字符表达式,expression 可为字符串也可为列名表达式。如果没有发现子串,则返回0 值。hmz平坦软件园
此函数不能用于text 和image 数据类型。
hmz平坦软件园

2、patindex()hmz平坦软件园
返回字符串中某个指定的子串出现的开始位置。hmz平坦软件园
patindex (<’%substring _expression%’>, <column_ name>)其中子串表达式前后必须有百分号“%”否则返回值为0。hmz平坦软件园
与charindex 函数不同的是,patindex函数的子串中可以使用通配符,且此函数可用于char、 varchar 和text 数据类型。
hmz平坦软件园

(**** 转载敬请注明-本文出处:南山古桃(nsgtao)的百度空间:http://hi.baidu.com/nsgtao/ ****)hmz平坦软件园
五、字符串操作函数hmz平坦软件园
1、quotename()hmz平坦软件园
返回被特定字符括起来的字符串。hmz平坦软件园
quotename (<’character_expression’>[, quote_ character]) 其中quote_ character 标明括字符串所用的字符,缺省值为“[]”。hmz平坦软件园
南山古桃 nsgtao 网址:http://hi.baidu.com/nsgtaohmz平坦软件园
2、replicate()hmz平坦软件园
返回一个重复character_expression 指定次数的字符串。hmz平坦软件园
replicate (character_expression integer_expression) 如果integer_expression 值为负值,则返回null 。
hmz平坦软件园

3、reverse()hmz平坦软件园
将指定的字符串的字符排列顺序颠倒。hmz平坦软件园
reverse (<character_expression>) 其中character_expression 可以是字符串、常数或一个列的值。
hmz平坦软件园

4、replace()hmz平坦软件园
返回被替换了指定子串的字符串。hmz平坦软件园
replace (<string_expression1>, <string_expression2>, <string_expression3>) 用string_expression3 替换在string_expression1 中的子串string_expression2。
hmz平坦软件园

4、space()hmz平坦软件园
返回一个有指定长度的空白字符串。hmz平坦软件园
space (<integer_expression>) 如果integer_expression 值为负值,则返回null 。
hmz平坦软件园

5、stuff()hmz平坦软件园
用另一子串替换字符串指定位置、长度的子串。hmz平坦软件园
stuff (<character_expression1>, <start_ position>, <length>,<character_expression2>)hmz平坦软件园
如果起始位置为负或长度值为负,或者起始位置大于character_expression1 的长度,则返回null 值。hmz平坦软件园
如果length 长度大于character_expression1 中 start_ position 以右的长度,则character_expression1 只保留首字符。hmz平坦软件园
六、数据类型转换函数hmz平坦软件园
1、cast()hmz平坦软件园
cast (<expression> as <data_ type>[ length ])
hmz平坦软件园

2、convert()hmz平坦软件园
convert (<data_ type>[ length ], <expression> [, style])
hmz平坦软件园

1)data_type为sql server系统定义的数据类型,用户自定义的数据类型不能在此使用。hmz平坦软件园
2)length用于指定数据的长度,缺省值为30。hmz平坦软件园
3)把char或varchar类型转换为诸如int或samllint这样的integer类型、结果必须是带正号或负号的数值。hmz平坦软件园
4)text类型到char或varchar类型转换最多为8000个字符,即char或varchar数据类型是最大长度。hmz平坦软件园
5)image类型存储的数据转换到binary或varbinary类型,最多为8000个字符。hmz平坦软件园
6)把整数值转换为money或smallmoney类型,按定义的国家的货币单位来处理,如人民币、美元、英镑等。hmz平坦软件园
7)bit类型的转换把非零值转换为1,并仍以bit类型存储。hmz平坦软件园
8)试图转换到不同长度的数据类型,会截短转换值并在转换值后显示“ ”,以标识发生了这种截断。hmz平坦软件园
9)用convert() 函数的style 选项能以不同的格式显示日期和时间。style 是将datatime 和smalldatetime 数据转换为字符串时所选用的由sql server 系统提供的转换样式编号,不同的样式编号有不同的输出格式。hmz平坦软件园
七、日期函数hmz平坦软件园
1、day(date_expression) hmz平坦软件园
返回date_expression中的日期值
hmz平坦软件园

2、month(date_expression)hmz平坦软件园
返回date_expression中的月份值
hmz平坦软件园

3、year(date_expression)hmz平坦软件园
返回date_expression中的年份值
hmz平坦软件园

4、dateadd()hmz平坦软件园
dateadd (<datepart>, <number>, <date>)hmz平坦软件园
返回指定日期date 加上指定的额外日期间隔number 产生的新日期。
hmz平坦软件园

5、datediff()hmz平坦软件园
datediff (<datepart>, <date1>, <date2>)hmz平坦软件园
返回两个指定日期在datepart 方面的不同之处,即date2 超过date1的差距值,其结果值是一个带有正负号的整数值。
hmz平坦软件园

6、datename()hmz平坦软件园
datename (<datepart>, <date>)hmz平坦软件园
以字符串的形式返回日期的指定部分此部分。由datepart 来指定。
hmz平坦软件园

7、datepart()hmz平坦软件园
datepart (<datepart>, <date>)hmz平坦软件园
以整数值的形式返回日期的指定部分。此部分由datepart 来指定。hmz平坦软件园
datepart (dd, date) 等同于day (date)hmz平坦软件园
datepart (mm, date) 等同于month (date)hmz平坦软件园
datepart (yy, date) 等同于year (date)
hmz平坦软件园

(**** 转载敬请注明-本文出处:南山古桃(nsgtao)的百度空间:http://hi.baidu.com/nsgtao/ ****)hmz平坦软件园
8、getdate()hmz平坦软件园
以datetime 的缺省格式返回系统当前的日期和时间
hmz平坦软件园

问:hmz平坦软件园
请教一个sql 2000 server问题:hmz平坦软件园
select * from itemcodehmz平坦软件园
where code like \’40%\’ hmz平坦软件园
如何让code=40101001hmz平坦软件园
变成code=401-01-001hmz平坦软件园
目前有666个code是类似40101001hmz平坦软件园
用什么语句能把它变成401-01-001
hmz平坦软件园

答:hmz平坦软件园
update itemcode set code=replace(code,left(code,8),left(code,3) \’-\’ substring(code,4,2) \’-\’ substring(code,6,3)) where code like \’40%\’
hmz平坦软件园

问:hmz平坦软件园
replace(code,left(code,8),left(code,3) \’-\’ substring(code,4,2) \’-\’ substring(code,6,3))是什么意思?
hmz平坦软件园

答:hmz平坦软件园
拿 code=123456789 做例子hmz平坦软件园
a = left(code,8) = 12345678hmz平坦软件园
b = left(code,3) = 123hmz平坦软件园
c = substring(code,4,2) = 45hmz平坦软件园
d = substring(code,6,3) = 678hmz平坦软件园
e = b \’-\’ c \’-\’ e = 123-45-678hmz平坦软件园
f = replace(code,a,e) = 123-45-6789hmz平坦软件园
配合下边的来看,应该能明白了hmz平坦软件园
1、left(code,8) 取前8位hmz平坦软件园
2、substring(code,4,2) 从第4位开始取2位hmz平坦软件园
3、 是字符串连接符hmz平坦软件园
4、replace(a,str1,str2) 将a中的str1替换为str2
hmz平坦软件园

几问几答下来,我就想着要整理一份完整的sql字符串函数出来,借以学习和方便以后查询。hmz平坦软件园

//字符类型排序hmz平坦软件园
select *hmz平坦软件园
from jxd_wd_stationinfohmz平坦软件园
order by cast(stationcode as int)
hmz平坦软件园

select *hmz平坦软件园
from jxd_wd_stationinfohmz平坦软件园
order by convert(int, stationcode)hmz平坦软件园
hmz平坦软件园

Delphi TStream,TMemoryStream,TFileStream等 详细介绍


tstream对象

  tstream对象是能在各种媒介中存储二进制数据的对象的抽象对象。从tstream 对象继承的对象用于在内存、windows资源文件、磁盘文件和数据库字段等媒介中存储数据。
  stream中定义了两个属性:size和position。它们分别以字节为单位表示的流的大小和当前指针位置。tstream中定义的方法用于在各种流中读、写和相互拷贝二进制数据。因为所有的stream对象都是从tstream中继承来的,所以在tstream中定义的域和方法都能被stream对象调用和访
问。此外,又由于面向对象技术的动态联编功能,tstream为各种流的应用提供了统一的接口,简化了流的使用;不同stream对象是抽象了对不同存储媒介的数据上的操作,因此,tstream的需方法为在不同媒介间的数据拷贝提供了最简捷的手段。

tstream的属性和方法

  1. position属性 
     声明:property position: longint;  
  position属性指明流中读写的当前偏移量。
  2. size属性
  声明:property size: longint;  
     size属性指明了以字节为单位的流的的大小,它是只读的。
  3. copyfrom方法
  声明:function copyfrom(source: tstream; count: longint): longint;  
     copyfrom从source所指定的流中拷贝count个字节到当前流中, 并将指针从当前位置移动count个字节数,函数返回值是实际拷贝的字节数。
  4. read方法
  声明:function read(var buffer; count: longint): longint; virtual; abstract;  
     read方法从当前流中的当前位置起将count个字节的内容复制到buffer中,并把当前指针向后移动count个字节数,函数返回值是实际读的字节数。如果返回值小于count,这意味着读操作在读满所需字节数前指针已经到达了流的尾部。
  read方法是抽象方法。每个后继stream对象都要根据自己特有的有关特定存储媒介的读操作覆盖该方法。而且流的所有其它的读数据的方法(如:readbuffer,readcomponent等)在完成实际的读操作时都调用了read方法。面向对象的动态联编的优点就体现在这儿。因为后继stream对
象只需覆盖read方法,而其它读操作(如readbuffer、readcomponent等)都不需要重新定义,而且tstream还提供了统一的接口。
  5. readbuffer方法
  声明:procedure readbuffer(var buffer; count: longint);  
  readbuffer方法从流中将count个字节复制到buffer 中, 并将流的当前指针向后移动count个字节。如读操作超过流的尾部,readbuffer方法引起ereaderror异常事件。
  6. readcomponent方法
  声明:function readcomponent(instance: tcomponent): tcomponent;  
     readcomponent方法从当前流中读取由instance所指定的部件,函数返回所读的部件。readcomponent在读instance及其拥有的所有对象时创建了一个reader对象并调用它的readrootcomponent方法。
  如果instance为nil,readcomponent的方法基于流中描述的部件类型信息创建部件,并返回新创建的部件。
  7. readcomponentres方法
  声明:function readcomponentres(instance: tcomponent): tcomponent;  
     readcomponentres方法从流中读取instance指定的部件,但是流的当前位置必须是由writecomponentres方法所写入的部件的位置。
  readcomponentres  
首先调用readresheader方法从流中读取资源头,然后调用readcomponent方法读取instance。如果流的当前位置不包含一个资源头。readresheader将引发一个einvalidimage异常事件。在classes库单元中也包含一个名为readcomponentres的函数,该函数执行相同的操作,只不过它基于应
用程序包含的资源建立自己的流。
  8. readresheader方法
  声明:procedure readresheader;  
     readresheader方法从流的当前位置读取windows资源文件头,并将流的当前位置指针移到该文件头的尾部。如果流不包含一个有效的资源文件头,readresheader将引发一个einvalidimage异常事件。
  流的readcomponentres方法在从资源文件中读取部件之前,会自动调用readresheader方法,因此,通常程序员通常不需要自己调用它。
  9. seek方法
  声明:function seek(offset: longint; origin: word): longint; virtual; abstract;  
     seek方法将流的当前指针移动offset个字节,字节移动的起点由origin指定。如果offset是负数,seek方法将从所描述的起点往流的头部移动。下表中列出了origin的不同取值和它们的含义:

                           函数seek的参数的取值
 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  常量       值      seek的起点         offset的取值
   ─────────────────────────────────
 sofrombeginning     0            流的开头              正 数
 sofromcurrent       1              流的当前位置        正数或负数   
 sofromend          2              流的结尾              负 数
 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
                   
    10. write方法
  在delphi对象式管理的对象中有两类对象的方法都有称为write的:stream对象和filer对象。stream对象的write方法将数据写进流中。filer对象通过相关的流传递数据,在后文中会介绍这类方法。
  stream对象的write方法声明如下:

       function write(const buffer; count: longint): longint; virtual; abstract;  

     write方法将buffer中的count个字节写入流中,并将当前位置指针向流的尾部移动count个字节,函数返回写入的字节数。
    tstream的write方法是抽象的,每个继承的stream对象都要通过覆盖该方法来提供向特定存储媒介(内存、磁盘文件等)写数据的特定方法。流的其它所有写数据的方法(如writebuffer、writecomponent)都调用write担当实际的写操作。
  11. writebuffer方法
  声明:procedure writebuffer(const buffer; count: longint);  
  writebuffer的功能与write相似。writebuffer方法调用write来执行实际的写操作,如果流没能写所有字节,writebuffer会触发一个ewriteerror异常事件。
  12. writecomponent方法
  在stream对象和filer对象都有被称为writecomponent的方法。stream对象的writecomponent方法将instance所指定的部件和它所包含的所有部件都写入流中;writer对象的writecomponent将指定部件的属性值写入writer对象的流中。
  stream对象的writecomponent方法声明是这样的:
        procedure writecomponent(instance: tcomponent);  

  writecomponent创建一个writer对象,并调用writer的writerootcomponent方法将instance及其拥有的对象写入流。
  13. writecomponentres方法
  声明:writecomponentres(const resname: string; instance: tcomponent);  
  writecomponentres方法首先往流中写入标准windows 资源文件头,然后将instance指定的部件写入流中。要读由writecomponentres写入的部件,必须调用readcomponentres方法。
  writecomponentres使用resname传入的字符串作为资源文件头的资源名,然后调用writecomponent方法将instance和它拥有的部件写入流。
  14. writedescendant方法
  声明:procedure writedescendant(instance ancestor: tcomponent);  
  stream对象的writedescendant方法创建一个writer对象,然后调入该对象的writedescendant方法将instance部件写入流中。instance可以是从ancestor部件继承的窗体,也可以是在从祖先窗体中继承的窗体中相应于祖先窗体中ancestor部件的部件。
  15. writedescendantres方法
  声明:procedure writedescendantres(const resname: string;
                                         instance, ancestor: tcomponent);
  writedescendantres方法将windows资源文件头写入流,并使用resname作用资源名,然后调用writedescendant方法,将instance写入流。

tstream的实现原理

  tstream对象是stream对象的基础类,这是stream对象的基础。为了能在不同媒介上的存储数据对象,后继的stream对象主要是在read和write方法上做了改进,。因此,了解tstream是掌握stream对象管理的核心。borland公司虽然提供了stream对象的接口说明文档,但对于其实现和应
用方法却没有提及,笔者是从borland delphi 2.0 client/server suite 提供的源代码和部分例子程序中掌握了流式对象技术。
  下面就从tstream的属性和方法的实现开始。
  1. tstream属性的实现
  前面介绍过,tstream具有position和size两个属性,作为抽象数据类型,它抽象了在各种存储媒介中读写数据所需要经常访问的域。那么它们是怎样实现的呢?
  在自定义部件编写这一章中介绍过部件属性定义中的读写控制。position和size也作了读写控制。定义如下:

     property position: longint read getposition write setposition;
     property size: longint read getsize;

  由上可知,position是可读写属性,而size是只读的。
  position属性的实现就体现在getposition和setposition。当在程序运行过程中,任何读取position的值和给position赋值的操作都会自动触发私有方法getposition和setposition。两个方法的声明如下:

     function tstream.getposition: longint;
     begin
       result := seek(0, 1);
     end;

     procedure tstream.setposition(pos: longint);
     begin
       seek(pos, 0);
     end;

     在设置位置时,delphi编译机制会自动将position传为pos。
  前面介绍过seek的使用方法,第一参数是移动偏移量,第二个参数是移动的起点,返回值是移动后的指针位置。
  size属性的实现只有读控制,完全屏蔽了写操作。读控制方法getsize实现如下:

     function tstream.getsize: longint;
     var
       pos: longint;
     begin
       pos := seek(0, 1);
       result := seek(0, 2);
       seek(pos, 0);
     end;

     2. tstream方法的实现
  ⑴ copyfrom方法
  copyfrom是stream对象中很有用的方法,它用于在不同存储媒介中拷贝数据。例如,内存与外部文件之间、内存与数据库字段之间等。它简化了许多内存分配、文件打开和读写等的细节,将所有拷贝操作都统一到stream对象上。
  前面曾介绍:copyfrom方法带source和count两个参数并返回长整型。该方法将count个字节的内容从source拷贝到当前流中,如果count值为0则拷贝所有数据。

     function tstream.copyfrom(source: tstream; count: longint): longint;
     const
       maxbufsize = &f000;
     var
       bufsize, n: integer;
       buffer: pchar;
     begin
       if count = 0 then
       begin
         source.position := 0;
         count := source.size;
       end;
       result := count;
       if count > maxbufsize then bufsize := maxbufsize else bufsize := count;
       getmem(buffer, bufsize);
       try
         while count <> 0 do
         begin
           if count > bufsize then  
             n := bufsize  
           else
             n := count;
           source.readbuffer(buffer^, n);
           writebuffer(buffer^, n);
           dec(count, n);
         end;
       finally
         freemem(buffer, bufsize);
       end;
     end;

  ⑵ readbuffer方法和writebuffer方法
  readbuffer方法和writebuffer方法简单地调用虚拟函数read、write来读写流中数据,它比read和write增加了读写数据出错时的异常处理。

     procedure tstream.readbuffer(var buffer; count: longint);
     begin
       if (count <> 0) and (read(buffer, count) <> count) then
         raise ereaderror.createres(sreaderror);
     end;

     procedure tstream.writebuffer(const buffer; count: longint);
     begin
       if (count <> 0) and (write(buffer, count) <> count) then
         raise ewriteerror.createres(swriteerror);
     end;

  ⑶ readcomponent、readresheader和readcomponentres方法
  readcomponent方法从当前流中读取部件。在实现上readcomponent方法创建了一个tstream对象,并用treader的readrootcomponent方法读部件。在delphi对象式管理中,stream对象和filer对象结合很紧密。stream对象的许多方法的实现需要filer对象的支持,而filer对象的构造函数
直接就以stream对象为参数。在readcomponent方法的实现中就可清楚地看到这一点:

     function tstream.readcomponent(instance: tcomponent): tcomponent;
     var
       reader: treader;
     begin
       reader := treader.create(self, 4096);
       try
         result := reader.readrootcomponent(instance);
       finally
         reader.free;
       end;
     end;

     readresheader方法用于读取windows资源文件的文件头,由readcomponentres方法在读取windows资源文件中的部件时调用,通常程序员不需自己调用。如果读取的不是资源文件readresh := fsize offset;
           end;
           result := fposition;
         end;

  offse代表移动的偏移量。origin代表移动的起点,值为0表示从文件头开始,值为1表示从当前位置开始,值为2表示从文件尾往前,这时offset一般为负数。seek的实现没有越界的判断。
  3. savetostream和savetofile方法
  savetostream方法是将memorystream对象中的内容写入stream所指定的流。其实现如下:

         procedure tcustommemorystream.savetostream(stream: tstream);
         begin
           if fsize <> 0 then stream.writebuffer(fmemory^, fsize);
         end;

  savetostream方法调用了stream的writebuffer方法,直接将fmemory中的内容按fsize字节长度写入流中。
  savetofile方法是与savetostream方法相关的。savetofile方法首先创建了一个filestream对象,然后把该文件stream对象作为savetostream的参数,由savetostream 方法执行写操作,其实现如下:

         procedure tcustommemorystream.savetofile(const filename: string);
         var
           stream: tstream;
         begin
           stream := tfilestream.create(filename, fmcreate);
           try
             savetostream(stream);
           finally
             stream.free;
           end;
         end;

  在delphi 的许多对象的savetostream 和savetofile、loadfromstream和loadfromfile方法的实现都有类似的嵌套结构。

tmemorystream对象

    
tmemorystream对象是一个管理动态内存中的数据的stream对象,它是从tcustommemorystream中继承下来的,除了从tcustommemorystream中继承的属性和方法外,它还增加和覆盖了一些用于从磁盘文件和其它注台读数据的方法。它还提供了写入、消除内存内容的动态内存管理方法。下面
介绍它的这些属性和方法。

tmemorystream的属性和方法

  1. capacity属性
  声明:property copacity: longint;  
     capacity属性决定了分配给内存流的内存池的大小。这与size属性有些不同。size属性是描述流中数据的大小。在程序中可以将capacity 的值设置的比数据所需最大内存大一些,这样可以避免频繁地重新分配。
  2. realloc方法
  声明:function realloc(var newcapacity: longint): pointer; virtual;  
     realloc方法,以8k为单位分配动态内存,内存的大小由newcapacity指定,函数返回指向所分配内存的指针。
  3. setsize方法
  setsize方法消除内存流中包含的数据,并将内存流中内存池的大小设为size字节。如果size为零,是setsize方法将释放已有的内存池,并将memory属性置为nil;否则,setsize方法将内存池大小调整为size。
     4. clear方法
  声明:procedure clear;  
     clear方法释放内存中的内存池,并将memory属性置为nil。在调用clear方法后,size和position属性都为0。
  5. loadfromstream方法
  声明:procedure loadfromstream(stream: tstream);  
     loadfromstream方法将stream指定的流中的全部内容复制到memorystream中,复制过程将取代已有内容,使memorystream成为stream的一份拷贝。
  6. loadfromfile方法
  声明:procedure loadfromfile(count filename: string);  
     loadfromfile方法将filename指定文件的所有内容复制到memorystream中,并取代已有内容。调用loadfromfile方法后,memorystream将成为文件内容在内存中的完整拷贝。

tmemorystream对象的实现原理

  tmemorystream从tcustommemorystream对象直接继承,因此可以享用tcustommemorystream的属性和方法。前面讲过,tcustommemorystream是用于内存中数据操作的抽象对象,它为memorystream对象的实现提供了框架,框架中的内容还要由具体memorystream对象去填充。tmemorystrea
m对象就是按动态内存管理的需要填充框架中的具体内容。下面介绍tmemorystream对象的实? fbuffer := allocmem(fdataset.recordsize);
               frecord := fbuffer;
               if not fdataset.getcurrentrecord(fbuffer) then exit;
               openmode := dbireadonly;
         end else
             begin
               if not (fdataset.state in [dsedit, dsinsert]) then dberror(snotediting);
               openmode := dbireadwrite;
             end;
             check(dbiopenblob(fdataset.handle, frecord, ffieldno, openmode));
           end;
           fopened := true;
           if mode = bmwrite then truncate;
         end;

    该方法首先是用传入的field参数给ffield,fdataset,frecord和ffieldno赋值。方法中用allocmem按当前记录大小分配内存,并将指针赋给fbuffer,用dataset部件的getcurrentrecord方法,将记录的值赋给fbuffer,但不包括blob数据。
  方法中用到的dbiopenblob函数是bde的api函数,该函数用于打开数据库中的blob字段。
  最后如果方法传入的mode参数值为bmwrite,就调用truncate将当前位置指针以后的
数据删除。
  分析这段源程序不难知道:
  ● 读写blob字段,不允许blob字段所在dataset部件有filter,否则产生异常事件
  ● 要读写blob字段,必须将dataset设为编辑或插入状态
  ● 如果blob字段中的数据作了修改,则在创建blob 流时,不再重新调用dbiopenblob函数,而只是简单地将fopened置为true,这样可以用多个blob 流对同一个blob字段读写

  destroy方法释放blob字段和为fbuffer分配的缓冲区,其实现如下:

         destructor tblobstream.destroy;
         begin
           if fopened then
           begin
             if fmodified then ffield.fmodified := true;
             if not ffield.fmodified then
               dbifreeblob(fdataset.handle, frecord, ffieldno);
           end;
           if fbuffer <> nil then freemem(fbuffer, fdataset.recordsize);
           if fmodified then
           try
             ffield.datachanged;
           except
             application.handleexception(self);
           end;
         end;

  如果blob流中的数据作了修改,就将ffield的fmodified置为true;如果ffield的modified为false就释放blob字段,如果fbuffer不为空,则释放临时内存。最后根据fmodified的值来决定是否启动ffield的事件处理过程datachanged。
  不难看出,如果blob字段作了修改就不释放blob字段,并且对blob 字段的修改只有到destroy时才提交,这是因为读写blob字段时都避开了ffield,而直接调用bde api函数。这一点是在应用bde api编程中很重要,即一定要修改相应数据库部件的状态。
  2. read和write方法的实现
  read和write方法都调用bde api函数完成数据库blob字段的读写,其实现如下:
  
         function tblobstream.read(var buffer; count: longint): longint;
         var
           status: dbiresult;
         begin
           result := 0;
           if fopened then
           begin
             status := dbigetblob(fdataset.handle, frecord, ffieldno, fposition,
                                                            count, @buffer, result);
             case status of
               dbierr_none, dbierr_endofblob:
                 begin
                   if ffield.ftransliterate then
                     nativetoansibuf(fdataset.locale, @buffer, @buffer, result);
                   inc(fposition, result);
                 end;
               dbierr_invalidbloboffset:
                 {nothing};
             else
               dbierror(status);
             end;
           end;
         end;

  read方法使用了bde  
api的dbigetblob函数从fdataset中读取数据,在本函数中,各参数的含义是这样的:fdataset.handle代表dataset的bde句柄,freacord表示blob字段所在记录,ffieldno表示blob字段号,fposition表示要读的的数据的起始位置,count表示要读的字节数,buffer是读出数据所占的内存,
result是实际读出的字节数。该bde函数返回函数调用的错误状态信息。
  read方法还调用了nativetoansibuf进行字符集的转换。

         function tblobstream.write(const buffer; count: longint): longint;
         var
           temp: pointer;
         begin
           result := 0;
           if fopened then
           begin
             if ffield.ftransliterate then
             begin
               getmem(temp, count);
               try
                 ansitonativebuf(fdataset.locale, @buffer, temp, count);
                 check(dbiputblob(fdataset.handle, frecord, ffieldno, fposition,
                   count, temp));
               finally
                 freemem(temp, count);
               end;
             end else
               check(dbiputblob(fdataset.handle, frecord, ffieldno, fposition,
                                                       count, @buffer));
             inc(fposition, count);
             result := count;
             fmodified := true;
           end;
         end;

     write方法调用了bde api的dbiputblob函数实现往数据库blob字段存储数据。
     该函数的各参数含义如下:

                  调用函数dbiputblob的各传入参数的含义
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
      参数名           含义
     ──────────────────────────────
       fdatasethandle            写入的数据库的bde句柄
       frecord                   写入数据的blob字段所在的记录
        ffieldno                  blob字段号
        fposition                  写入的起始位置
       count                     写入的数据的字节数
        buffer                     所写入的数据占有的内存地址
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

标志,该标志意味着后面存储有一连串的项目。reader对象,在读这一连串项目时先调用readlistbegin方法读取该标志位,然后用endoflist判断是否列表结束,并用循环语句读取项目。在调用writelistbegin方法的后面必须调用writelistend方法写列表结束标志,相应的在reader对象中
有readlistend方法读取该结束标志。
  5. writelistend方法
  声明:procedure writelistend;  
     writelistend方法在流中,写入项目列表结束标志,它是与writelistbegin相匹配的方法。
  6. writeboolean方法
  声明:procedure writeboolean(value: boolean);  
     writeboolean方法将value传入的布尔值写入流中。
  7. writechar方法
  声明:procedure writechar(value: char);  
     writechar方法将value中的字符写入流中。
  8. writefloat方法
  声明:procedure writefloat(value: extended);  
     writefloat方法将value传入的浮点数写入流中。
  9. writeinteger方法
  声明:procedure writeinteger(value: longint);  
     writeinteger方法将value中的整数写入流中。
  10. writestring方法
  声明:procedure writestring(const value: string);  
     writestring方法将value中的字符串写入流中。
  11. writeident方法
  声明:procedure writeident(const ident: string);  
     writeident方法将ident传入的标识符写入流中。
  12. writesignature方法
  声明:procedure writesignature;  
     writesignature方法将delphi filer对象标签写入流中。writerootcomponent方法在将部件写入流之前先调用writesignature方法写入filer标签。reader对象在读部件之前调用readsignature方法读取该标签以指导读操作。
  13. writcomponent方法
  声明:procedure writecomponent(component: tcomponent);  
     writecomponent方法调用参数component的writestate方法将部件写入流中。在调用writestate之前,writecomponent还将component的componetnstate属性置为cswriting。当writestate返回时再清除cswriting.
     14. writerootcomponent方法
  声明:procedure writerootcomponent(root: tcomponent);  
     writerootcomponent方法将writer对象root属性设为参数root带的值,然后调用writesignature方法往流中写入filer对象标签,最后调用writecomponent方法在流中存储root部件。

Delphi中预编译指令的使用方法


除了使用编译设置对话框对编译器进行设置外,还可以通过编译指令来对编译器进行设置。
对于局部的编译器设置,只有使用编译指令来完成。

对于开关编译指令,通过在编译指令后加入指示开关状态的加号和减号来控制编译器。例如:

{&b } : 打开完全布尔量检查。
{&q-} : 关闭溢出检查。

通常,编译指令的作用域是在编译指令后的代码部分,而对于全程的编译指令应该防在单元接口部分的开头。
编译设置对话框的设置都有与之对应的编译指令用于在代码中对编译器进行设置,如下表所示。

          设置项                       编译指令
                      
       optimizations                     {&o}

       aligned record fields             {&a}

       stack frames                      {&w}

       pentium-safe fdiv                 {&u}

       range checking                    {&r}

       i/o checking                      {&i}

       overflow checking                 {&q} 

       strict var-strings                {&v}

       comlete boolean eval              {&b}

       extended syntax                   {&x}

       typed @ operator                  {&t}

       open parameters                   {&p}

       huge strings                      {&h}

       assertions typed constants        {&j}

       debug information                 {&d}

       local sysnbols                    {&l}

在这些编译指令以外还有一些非常有用的编译指令。
 &r filename  : 这个编译指令是最为常用的编译指令,他是资源文件编译指令,用于指定连接到执行文件和库的资源文件,例如在工程文件

(.dpr)中会有{&r *.res}的编译指令,表明把后缀为 .res的与工程文件同名的资源文件连接入执行文件,也可以指定一个资源文件,资源文件

的使用对于编写windows程序来说是很重要的基础。

 &i filename :这个编译指令功能类似于c语言的#include , 用于指定编译时包括的文件。

**********************************************************************************************

a.3 使用条件编译指令

条件编译指令是非常重要的编译指令,他控制着在不同条件下(例如,不同的操作系统)产生不同的代码。条件编译指令是包含在
注释括号之内的,如下表所示。
             
   条件编译指令      含义

     &define   用于定义一个条件符号,一旦定义,条件符号就为真

     &else     与&ifdef配合使用,如果&ifdef条件为假,则只对源文件&else后一小部分进行编译

     &endif    结束一个以&if开始的条件段
 
     &ifdef    对条件符号进行判断,为真则编译源文件

     &ifndef   对条件符号进行判断,为假则编译源文件

     &ifopt    根据编译开关状态,对源文件编译
 
     &undef    撤消以前的条件符号定义

这些条件编译指令是非常有用的。例如,可以通过开关的状态来控制编译:

     {ifopt r }
         showmessage(\’compiled with range-checking\’);
     {&endif}

也可以通过定义条件符号来控制编译:
    
     {&define s}
     ……
     {&ifdef s}
         showmessage(\’yes\’);
     {&else}
         showmessage(\’no\’);
     {&endif}

他的编译结果是显示\’yes\’,但是如果省去{&define s}则显示\’no\’。
在delphi中已经预定义了一些关键的条件符号,如下表所示。

   条件符号           含义

    verxx    编译器版本,xx表示版本,例如:delphi 1.0 的编译器版本为80、delphi 5.0 的编译器版本为130

    win32    是否win32的运行环境(windows 95.98/nt/2000)

    cpu386   是否intel386以上的处理器

    console  是否控制台程序

delphi的编译器指令除了以上的指令外还有一些,不过最为常用的指令已经全部介绍完了。对于普通的程序,delphi是不需要编程者去添加编

译器指令的,delphi已经自动完成,但是要得到高品质的应用程序或者有特殊的要求的程序就必须熟悉delphi的编译指令。delphi不仅有最快

的编译器而且编译器的功能也非常强大。

2011年计算机等级考试Delphi讲义:文件管理


  文件是同一类型元素的有序集合,是内存与外设间传输数据的渠道。一些外设如显示器、键盘、打印机等都可以看作文件,但最常用的还是磁盘文件,这也是本章我们主要讨论的对象。

  delphi继承了object pascal的文件管理功能,并有很大的发展,其中最主要的是提供了用于文件管理的标准控件,同时也提供了更多的文件管理函数。利用delphi的强大功能,开发一个自己的文件管理系统就成为很容易的事。

  本章首先介绍delphi文件管理的基本概念和标准过程/函数,并提供了一个记录文件的应用实例,这是从我们实际课题开发中提取出来的。而后介绍delphi提供的文件控件的使用方法。最后提供的一个综合例程mdi文件管理器则是对delphi文件管理功能的综合应用。

  6.1 文件类型和标准过程

  delphi同object pascal一样支持三种文件类型,即:文本文件、记录文件、无类型文件。

  6.1.1文本文件

  文本文件类型的变量用如下方法声明:

  var

  textfilevar: text ;

  文本文件是以行为单位进行读、写操作的。由于每一行长度不一定相同,不能计算出给定行在文件中的确切位置,因而只能顺序地读写。而且文本文件只能单独为读或写而打开,在一个打开的文本文件上同时进行读、写操作是不允许的。

  6.1.1.1 文本文件的打开、关闭

  文本文件的打开需要两个步骤:(1). 文件变量与文件名关联;(2). 初始化读写。

  联文件变量与文件名调用assignfile标准过程:

  assignfile ( textfilevar , filename ) ;

  filename 既可以是全路径名,也可以仅是文件名。对于后者系统将在当前目录下查找。

  assignfile是delphi新提供的一个函数,其功能等价于object pascal中的assign。而assign在delphi中更多地被用作一个方法名。

  初始化读写有三种方式:

  1. reset : 为读打开文件并把文件指针移动到文件首;

  2. rewrite : 为写创建一个新文件;

  3. append : 为写打开存在的文件并把文件指针定位在文件尾。

  当使用reset或append过程而文件不存在时将会引发一个i/o异常。有关i/o异常的处理请参看本章例程和第十二章中的介绍。

  文件的关闭很简单,只须调用closefile过程即可。

  虽然delphi应用程序在退出时会自动关闭所有打开的文件,但自己动手关闭文件可以确保释放文件句柄,并使程序的可移植性增强。

  为保持兼容,delphi也允许用户用assign建立关联,close关闭文件。

  6.1.1.2 文本文件的读写

  从文本文件中读取信息用read、readln两个标准过程。

  当读入数值时,read、readln假定数值是用一个或多个空格分开的,而不是逗号、分号或其它字符。对如下一条语句:

  read ( textfilevar , num1 , num2 , num3 ) ;

  如果文件中的数值是:

  100 200 300

  则能够成功读入,而若文件中的数值是

  100 200, 300

  则read读入“200,”并试图把它转化成一个数值时会引发一个异常。

  当读入字符是字符串时,read、readln过程总是读取尽可能多的字符填充到字符串变量中或一直读到行结束符为止。因此从文本文件中读取格式化的字符串数据,必须声明与其长度相匹配的字符串变量。如果要从文件中读取单词,必须先把文件中的每一行读入字符串,然后再从字符串中逐个分析出单词。或者一次只从文本文件中读入一个字符并测试每个字符后是否是单词断开处。

  格式化字符串之间的分隔符应读入到一个临时变量中,而字符串与数值、数值与数值间的分隔符读入时会自动识别剔除。对如下一行数据:

  mon 12:10 40 50

  定义

  var

  day: string[3] ;

  time: string[5] ;

  num1, num2: integer ;

  则须用如下的read 语句读入:

  read ( textfilevar , day , c , time , num1 , num2 ) ;

  c为一个临时字符变量。

  6.1.1.3 文本文件的编辑

  在delphi中实现对一个文本文件的编辑,只须让其与一个tmemo控件建立关联即可:

  memo1.lines.loadfromfile ( textfilename ) ;

  这样在tmemo上所做的一切修改当调用memo部件的savetofile方法后都会反映到文件中去。

  6.1.2 记录文件

  记录文件是一种操作更为灵活的文件类型。它允许同时为读和写打开,而且由于记录文件中每条记录的长度固定,所以可随机存取。

  记录文件的类型变量可如下声明:

  var

  recordfilevar: file of recordtype;

  recordtype是一个自定义的记录类型。

  有关记录文件的操作我们将在下一节中结合例程进行讨论。

  6.1.3 无类型文件

  无类型文件提供了底层的i/o通道,可用于存取可变长度记录的文件。经常用于文件的复制操作中。由于delphi提供了更好的方法(见第四节),所以无类型文件很少使用。有兴趣的读者可参看blockread、blockwrite两个联机帮助主题。

  6.1.4 delphi的文件管理标准过程

  根据功能我们把标准过程划分为十一类进行介绍。

  6.1.4.1 文件的打开与关闭

  assignfile :把一个外部文件名和一个文件变量相关联

  reset :打开一个存在的文件

  rewrite :创建并打开一个新文件(或覆盖原有文件)

  append :以添加方式打开一个文件(只适用于文本文件)

  closefile : 关闭一个打开的文件

  fileopen :打开一个特定的文件并返回文件句柄

  filecreate :创建一个给定文件名的文件并返回文件句柄

  fileclose :关闭一个特定句柄的文件

  后边三个文件主要供系统内部使用,在文件复制的编程中也往往会用到。它们操作的对象是文件句柄而不是文件变量。

  6.1.4.2 文件定位

  seek :把文件当前位置移到指定部分

  filepos : 返回文件的当前位置

  eoln : 返回行结束标志

  eof : 返回文件结束标志

  fileseek : 改变当前文件指针的位置

  seek与fileseek的区别是:1. seek仅用于记录文件;2. fileseek的参数是文件句柄、偏移量、起始位置。其中起始位置有文件首、当前位置、文件尾三种选择。seek的参数是文件变量、偏移量,偏移量是从文件首开始定位的。3. fileseek的偏移量以字节数来计算,而seek是根据记录号进行移动。

  seek、filepos仅用于记录文件。但任何文件都可以看作是基于字节的记录文件。下面一段程序表示了它们的用法。

  { 该例子的设计界面为一个包含topendialog部件的窗体。}

  uses dialogs;

  var



Powered By wordpress Copyright delphi教程 © 2009-2012 版权所有