上一题下一题
跳转到
 
 
  世界如此多姿,发展如此迅速,窥一斑未必还能知全豹。但正如万花筒一样,每一个管窥都色彩斑斓。  
 
 
  知识通道 | 学习首页 | 教师首页 | PK首页 | 知识创造首页 | 企业首页 | 登录
 
本文对应知识领域
DELPHI语法编程基础(11)
作者:未知 申领版权
2010年12月12日 共有 1945 次访问 【添加到收藏夹】 【我要附加题目
受欢迎度:

    Windows 句柄 
    Delphi从Windows 引入了不少数据类型,其中句柄最重要。这种数据类型名为THandle,该类型在Windows 单元中定义:
    type
    THandle = LongWord;
    句柄数据类型通过数字实现,但并不当数字用。在Windows 中,句柄是一个系统内部数据结构的引用。例如,当你操作一个窗口,或说是一个Delphi 窗体时,系统会给你一个该窗口的句柄,系统会通知你:你正在操作142号窗口,就此,你的应用程序就能要求系统对142号窗口进行操作——移动窗口、改变窗口大小、把窗口极小化为图标,等等。实际上许多Windows API 函数把句柄作为它的第一个参数,如GDI (图形设备接口)句柄、菜单句柄、实例句柄、位图句柄等等,不仅仅局限于窗口函数,。
    换句话说,句柄是一种内部代码,通过它能引用受系统控制的特殊元素,如窗口、位图、图标、内存块、光标、字体、菜单等等。Delphi中很少需要直接使用句柄,因为句柄藏在窗体、位图及其他Delphi对象的内部。当你要调用Delphi不支持的Windows API 函数时,句柄才会有用。
    现在举一个简单的Windows句柄例子,完善这节内容。例WHandle 程序的窗体很简单,只有一个按钮。正如下面主窗体文本所定义的那样,在代码中添加了窗体的OnCreate 事件和按钮的OnClick 事件:
    object FormWHandle: TFormWHandle
    Caption = 'Window Handle'
    OnCreate = FormCreate
    object BtnCallAPI: TButton
    Caption = 'Call API'
    OnClick = BtnCallAPIClick
    end
    end
    窗体一创建,程序就会通过窗体本身的Handle 属性,获取窗体对应的窗口句柄。调用IntToStr ,把句柄数值转换为一个字符串,然后再把它添加到窗体标题中,如图9.1:
    procedure TFormWHandle.FormCreate(Sender: TObject);
    begin
    Caption := Caption   ' '   IntToStr (Handle);
    end;
    因为FormCreate 是窗体类的方法,它可直接访问同类的其他属性和方法。因此,在这个过程中我们能够直接访问窗体的Caption 属性和Handle 属性。
    图 9.1: 例 WHandle 显示窗体句柄,每次运行程序得到的句柄值不同 
    
    如果你多此次执行该程序,通常会获得不同的句柄值。这个值实际上是由Windows 操作系统确定并返回给应用程序的。(句柄从来不是由程序决定的,而且句柄没有预定义值,句柄是由系统决定的,每执行一次程序,产生一个新值。)
    当你单击按钮,程序将调用Windows API 函数SetWindowText,它会根据第一个传递参数改变窗口的标题。更准确地说,所用的API 函数其第一个参数是需要修改窗体的句柄:
    procedure TFormWHandle.BtnCallAPIClick(Sender: TObject);
    begin
    SetWindowText (Handle, 'Hi');
    end;
    这段代码与前面所讲的事件处理程序等效,它通过给窗体的Caption 属性赋一个新值,改变窗体的标题。对上面这种情况,调用一个API 函数没有什么意义,因为用Delphi来做更简单。然而有些API在Delphi中没有相应的函数,就需要直接调用API,这一点你会在后面的高级例子中看到。
    外部声明 
    Windows 编程中涉及的另一个重要元素是外部声明。外部声明原先用于在Pascal代码中连接汇编语言写的外部函数,现在外部声明用于Windows编程,用来调用动态连接库DLL函数。在Delphi的Windows 单元中有许多这种声明:
    // forward declaration
    function LineTo (DC: HDC; X, Y: Integer): BOOL; stdcall;
    
    // external declaration (instead of actual code)
    function LineTo; external 'gdi32.dll' name 'LineTo';
    这段声明表示函数LineTo 的代码同名保存在GDI32.DLL 动态链接库中(最重要的Windows 系统库之一)。实际应用时,外部声明中的函数名与DLL中的函数名可以不同。
    一般你不需要象刚才所例举的那样写声明,因为Windows 单元和一些Delphi 系统单元中已包含了这些声明。只有在调用自定义DLL,或调用Delphi 中未定义的Windows 函数时,你才能需要写外部声明。
    注意:在16位Delphi中,外部声明使用不带扩展名的库名,后面跟name指令(如上所示)或是一个index指令,后面跟DLL中函数的序号。尽管Win32 仍然允许通过序号访问DLL函数,但是微软公司已经声明未来将不支持这种访问方式,这一改变反映了系统库访问方式的改变。还要注意的是:目前Delphi的Windows 单元已取代了16位Delphi的WinProcs 和WinTypes 单元。
    回调函数 
    前面已经了解到Objet Pascal 支持过程类型。过程类型常用于给Windows API函数传递回调函数。
    首先,什么是回调函数呢?回调函数就是能对一系列系统内部元素执行给定操作的API函数,例如能对所有同类窗口进行操作的函数。这种函数也叫枚举函数,它是作为参数传递的函数,代表对所有内部元素执行的操作,该函数或过程的类型必须与给定的过程类型兼容。Windows 回调函数的应用不止上述一种,不过这里仅研究以上简单应用。
    现在考虑 EnumWindows API 函数,它的原型如下(从Win32 帮助文件拷贝而来):
    BOOL EnumWindows(
    WNDENUMPROC lpEnumFunc,  // address of callback function
    LPARAM lParam // application-defined value
    );
    当然,这是个C语言的定义。我们可以查看Windows 单元,从中找到相应的Pascal 语言定义:
    function EnumWindows (
    lpEnumFunc: TFNWndEnumProc;
    lParam: LPARAM): BOOL; stdcall;
    查阅帮助文件,我们发现作为参数传递的函数应该属于下面的类型(也是在C中):
    BOOL CALLBACK EnumWindowsProc (
    HWND hwnd, // handle of parent window
    LPARAM lParam // application-defined value
    );
    这与下面的Delphi 过程类型定义一致:
    type
    EnumWindowsProc = function (Hwnd: THandle;
    Param: Pointer): Boolean; stdcall;
    其中第一个参数是各主窗体的句柄,第二个参数则是调用EnumWindows 函数时所传递的值。实际上,Pascal 中没有相应的TFNWndEnumProc类型定义 ,它只是个指针。这意味着我们需要传递一个带有合适参数的函数,将它用作一个指针,也就是取函数的地址而不是调用它。不幸的是,这也意味着如果某个参数类型出现错误时,编译器不会给予提示。
    每当调用Windows API函数或传递一个回调函数给系统时,Windows 要求程序员遵循stdcall 调用协定。缺省情况下,Delphi使用另一种更高效的调用协定,其关键字为register。
    下面是一个与定义兼容的回调函数,此函数把窗口的标题读到字符串中,然后添加到给定窗体的一个列表框中:
    function GetTitle (Hwnd: THandle; Param: Pointer): Boolean; stdcall;
    var
    Text: string;
    begin
    SetLength (Text, 100);
    GetWindowText (Hwnd, PChar (Text), 100);
    FormCallBack.ListBox1.Items.Add (
    IntToStr (Hwnd)   ': '   Text);
    Result := True;
    end;
    窗体有一个几乎覆盖整个窗体的列表框,窗体顶部有一个小面板,面板上有一个按钮。当按下按钮时,EnumWindows API函数被调用,并且GetTitle 函数作为参数传递给它:
    procedure TFormCallback.BtnTitlesClick(Sender: TObject);
    var
    EWProc: EnumWindowsProc;
    begin
    ListBox1.Items.Clear;
    EWProc := GetTitle;
    EnumWindows (@EWProc, 0);
    end;
    你可以直接调用GetTitle函数,不必先把值保存到过程类型临时变量中,上例这么做是为了使回调过程更清楚。程序运行结果确实很有意思,正如你在图9.2中看到的那样,结果显示了系统中正在运行的所有主窗口,其中大部分是隐藏的,你通常看不到,许多实际上没有标题。
    图 9.2: 例CallBack输出结果--当前所有主窗体,其中包括可见及隐藏的窗体 
    
    Variant变量没有类型 
    一般说来,你可以用Variant 变量存储任何数据类型,对它执行各种操作和类型转换。需要注意的是:这违反了Pascal 语言的一贯原则,有悖于良好的编程习惯。variant 变量的类型检查和计算在运行期间才进行,编译器不会提示代码中的潜在错误,这些错误在进一步测试中才能发现。总之,你可以认为包含variant变量的代码是解释性代码,正如解释性代码一样,许多操作直到执行时才能知道,这对代码运行速度会有很大的影响。
    上面对Variant 类型的使用提出了警告,现在来看看Variant 类型究竟能干什么。基本上说,如果声明了一个variant 变量:
    var
    V: Variant;
    你就可以把各种不同类型的值赋给它:
    V := 10;
    V := 'Hello, World';
    V := 45.55;
    一旦得到一个variant 值,你可以把它拷贝给任何兼容或不兼容的数据类型。如果你把值赋给不兼容的数据类型,Delphi 会力尽所能进行转换,无法转换则颁布一个运行时间错误。实际上,variant变量中不仅包含了数据还包含有类型信息,并允许一系列运行时间操作,这些操作很方便,但运行速度慢且安全性差。
    见例VariTest,它是上面代码的扩展。窗体上有三个编辑框,一对按钮,第一个按钮的OnClick 事件代码如下: 
    procedure TForm1.Button1Click(Sender: TObject);
    var
    V: Variant;
    begin
    V := 10;
    Edit1.Text := V;
    V := 'Hello, World';
    Edit2.Text := V;
    V := 45.55;
    Edit3.Text := V;
    end;
    很有趣是不是?你可以把一个值为字符串的variant 变量赋给编辑框Text 属性,还可以把值为整数或浮点数的variant 变量赋给Text属性。正如你在图10.1中所看到的,一切正常。
    (图10.1)按Assign按钮后,例VariTest的输出结果
    图 10.1: 例 VariTest 的 Assign 按钮 Click 事件输出结果
    
    更糟糕的是:你还可以用variant变量计算数值,从第二个按钮的Click事件代码就可看到这一点:
    procedure TForm1.Button2Click(Sender: TObject);
    var
    V: Variant;
    N: Integer;
    begin
    V := Edit1.Text;
    N := Integer(V) * 2;
    V := N;
    Edit1.Text := V;
    end;
    至少这种代码带有一定危险性,如果第一个编辑框包含了一个数字,那么一切运行正常;如果不是,将会引发异常。这里再重申一遍,如果不到万不得以,不要随便使用Variant 类型,还是应坚持使用传统的Pascal 数据类型和类型检查方法。在Delphi 和 VCL中,variant变量主要是用于 OLE 支持和数据库域的访问。
    Variant类型内部结构 
    Delphi中定义了一个 variant 记录类型,TVarData,它与Variant 类型有相同的内存布局。你可以通过TVarData访问variant变量的实际类型。TVarData 结构中包含了Variant类型信息(由Vtype域表示)、一些保留域及当前值。
    VType域的取值包括OLE 自动化中的所有数据类型,这些类型通常叫OLE 类型或variant 类型。以下是variant 类型的完整列表,按字母顺序排列:
    · varArray 
    · varBoolean 
    · varByRef 
    · varCurrency 
    · varDate 
    · varDispatch 
    · varDouble 
    · varEmpty 
    · varError 
    · varInteger 
    · varNull 
    · varOleStr 
    · varSingle 
    · varSmallint 
    · varString 
    · varTypeMask 
    · varUnknown 
    · varVariant 
    你可以在Delphi 帮助系统的variants 主题下找到这些类型的说明。
    还有许多操作variant 变量的函数,你可以用它们进行特定的类型转换,或通过它们获取variant变量的类型信息(例如VarType 函数),当你用variant变量写表达式时,Delphi会自动调用这些类型转换和赋值函数。另外还有操作variant 数组的例程,你可以通过帮助文件的Variant support routines 主题了解相关内容。

    

 

相关新闻

每一个程序员要遵守的一些优秀编程风格
代词it, that, one, the one 的辨析
《VB程序设计基础》选择题
Is Family Life Changing
设计模式之Iterator
设计模式之Visitor
设计模式之Interpreter(解释器)
设计模式之Mediator(中介者)
设计模式之Strategy(策略)
设计模式之State

您可能对这些感兴趣  

第7讲 综合分析BIM在运用与推广中的障碍问题
考核学生质量的困难
日本福岛核电站事故初步分析
论科技异化与科技人化
中国科技计划项目管理现状与对策
财政科技资金的审计重点和内容
软科学研究机构在政府决策中的功效、困境及对策研究
打造超级DOS系统(下)
DOS命令基础应用(6)
打造超级DOS系统(上)

题目筛选器
日期:
类型:
状态:
得分: <=
分类:
作者:
职业:
关键字:
搜索

 
 
 
  焦点事件
 
  知识体系
 
  职业列表
 
 
  最热文章
 
 
  最多引用文章
 
 
  最新文章
 
 
 
 
网站介绍 | 广告服务 | 招聘信息 | 保护隐私权 | 免责条款 | 法律顾问 | 意见反馈
版权所有 不得转载
沪ICP备 10203777 号 联系电话:021-54428255
  帮助提示    
《我的太学》是一种全新的应用,您在操作中遇到疑问或者问题,请拨打电话13564659895,15921448526。
《我的太学》