📄 delphi3.html
字号:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<meta name="GENERATOR" content="Microsoft FrontPage 3.0">
<title>Delphi实用技巧(三)</title>
</head>
<body background="../../images/background.gif" bgcolor="#628394" text="#FFFFFF"
link="#FFFF00" vlink="#FFFF00">
<p><a name="topofpage"></a></p>
<div align="center"><center>
<table border="1" cellpadding="0" cellspacing="0">
<tr>
<td><strong><em><font face="楷体_GB2312" size="6">Delphi实用技巧(三)</font></em></strong></td>
</tr>
</table>
</center></div>
<p align="center"><a href="../programming.html"><font face="楷体_GB2312" size="4">返回“编程交流”</font></a></p>
<p><img src="delphilogo.gif" alt="Delphi" align="right" border="0" WIDTH="93" HEIGHT="128">
<ul type="square">
<li><a href="#noobject"><font face="楷体_GB2312" size="4">用最原始的方法编制Windows应用程序</font></a></li>
<li><a href="#initmenu"><font face="楷体_GB2312" size="4">“用户选取菜单”事件</font></a></li>
<li><a href="#iunknown"><font face="楷体_GB2312" size="4">IUnknown类的局部变量由谁负责Release</font></a></li>
<li><a href="#whichwnd"><font face="楷体_GB2312" size="4">为全屏幕方式的DirectDraw对象指定窗口Handle</font></a></li>
<li><a href="#hint"><font face="楷体_GB2312" size="4">建立自己的Hint窗口</font></a></li>
</ul>
<hr width="80%">
<p><a name="noobject"></a></p>
<div align="center"><center>
<table border="1" cellpadding="0" cellspacing="0">
<tr>
<td><strong><em><font face="楷体_GB2312" size="5">用最原始的方法编制程序</font></em></strong></td>
</tr>
</table>
</center></div>
<p><font face="楷体_GB2312" size="4"> Delphi将几乎所有我们要用到的东西进行了封装,让程序员以面向对象的方法来快速高效地完成任务。正如使用MFC编写程序,其生成的最终可执行文件要比使用纯C语言编制的程序大得多,Delphi生成的最终程序往往很大。<br>
其实Delphi本质上仍是一种编程语言,只不过它的可视化特性太过强大,使得程序员往往忘了这一点。使用纯C语言编制Windows应用程序的朋友都知道,常规的方法是为Windows提供一个回调函数,在这个回调函数中处理各种消息,而程序的入口是一个名为WinMain的函数。<br>
如果将Delphi看成一个单纯的PASCAL语言,就可以按上述的常规方法编制程序,下面就是这样一个例子,最终生成的可执行程序十分小,只有不到10KB。使用C语言编程的朋友可得会觉得下面的代码很眼熟。</font></p>
<div align="center"><center>
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td width="100%"><pre><font face="Courier New" color="#000000">program MyApp;
uses Windows, Messages;
// </font><font
color="#000000">回调函数
</font><font face="Courier New" color="#000000">function AppWindowProc(
hWnd:HWND; uMsg:UINT;
wParam:WPARAM; lParam:LPARAM):LRESULT; stdcall;
begin
Result := 0;
case uMsg of
WM_DESTROY:begin
PostQuitMessage(0);
Exit;
end;
end;
Result :=
DefWindowProc(hWnd, uMsg, wParam, lParam);
end;
var
wc: TWndClass;
hWnd: Integer;
MSG: TMsg;
begin
// </font><font
color="#000000">程序从这里开始执行
</font><font face="Courier New" color="#000000">wc.style := CS_VREDRAW or CS_HREDRAW;
wc.lpfnWndProc := @AppWindowProc;
wc.cbClsExtra := 0;
wc.cbWndExtra := 0;
wc.hInstance := HInstance;
wc.hIcon := LoadIcon(0, IDI_APPLICATION);
wc.hCursor := LoadCursor(0, IDC_ARROW);
wc.hbrBackground := (COLOR_BTNFACE+1);
wc.lpszMenuName := nil;
wc.lpszClassName := 'My App';
if RegisterClass(wc)=0 then Exit;
hWnd := CreateWindow(
wc.lpszClassName, 'TEST',
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
0, 0, HInstance, nil);
if hWnd=0 then Exit;
ShowWindow(hWnd, SW_SHOWNORMAL);
while GetMessage(MSG, 0, 0, 0) do begin
TranslateMessage(MSG);
DispatchMessage(MSG);
end;
Halt(MSG.wParam);
end.</font></pre>
</td>
</tr>
</table>
</center></div>
<p align="center"><a href="#topofpage">回到页首</a></p>
<hr width="80%">
<p><a name="initmenu"></a></p>
<div align="center"><center>
<table border="1" cellpadding="0" cellspacing="0">
<tr>
<td><strong><em><font face="楷体_GB2312" size="5">“用户选取菜单”事件</font></em></strong></td>
</tr>
</table>
</center></div>
<p><font face="楷体_GB2312" size="4"> Delphi的TMainMenu构件没有提供很丰富的事件处理,这给程序设计带来了一些不便。例如,在一个文本编辑器程序中,可能需要随时侦测系统的剪帖板是否有数据,如果有则将主菜单的Paste条目设为Enabled,反之设为Disabled。检测剪帖板是否有内容的代码应该在用户选取菜单时立即执行,可惜Delphi并未提供相应的事件。<br>
当用户选取菜单时,Windows会向程序发送WM_INITMENU消息,因此可在MainForm中加入处理WM_INITMENU消息的过程,示例如下:</font>
<ul type="circle">
<li><font face="楷体_GB2312" size="4">首先在MainForm声明的private段中加入如下声明</font><br>
<font face="Courier New" color="#000000">procedure WMInitMenu(var msg: TMessage); message
WM_INITMENU;</font></li>
<li><font face="楷体_GB2312" size="4">WMInitMenu消息处理过程的代码如下:</font><div
align="center"><center><table border="0" cellpadding="2">
<tr>
<td><pre><font face="Courier New" color="#000000">procedure TMainForm.WMInitMenu(var msg: TMessage);
begin
if msg.WParam=GetMenu(Handle) then begin
EditPasteItem.Enabled := Clipboard.HasFormat(CF_OEMTEXT);
end else inherited;
end;</font></pre>
</td>
</tr>
</table>
</center></div></li>
</ul>
<p><font face="楷体_GB2312" size="4"> 在上例中,当用户选取菜单时,WMInitMenu立刻获得控制权,并根据剪帖板中是否有文本内容来决定Paste菜单条目的Enabled属性值。注意,无论是在MDI程序还是在非MDI程序中,处理WM_INITMENU消息的过程都应该放在MainForm中。</font></p>
<p align="center"><a href="#topofpage">回到页首</a></p>
<hr width="80%">
<p><a name="iunknown"></a></p>
<div align="center"><center>
<table border="1" cellpadding="0" cellspacing="0">
<tr>
<td width="100%"><font face="楷体_GB2312" size="5" color="#FFFFFF"><strong><em>IUnknown类型的局部变量由谁负责Release</em></strong></font></td>
</tr>
</table>
</center></div>
<p><font face="楷体_GB2312" size="4"> 在C++中,IUnknown是一个抽象类,它拥有三个方法:QueryInterface、AddRef和Release,而在Delphi中,这三个方法分别是QueryInterface、_AddRef和_Release,注意方法名前页的下划线。<br>
编制使用IUnknown类局部变量的程序时应注意,Delphi在一个模块结束之前将会自动调用_Release方法来释放所有的IUnknown局部变量,而这些代码对于程序员来说是不可见的。看下面的代码:</font></p>
<div align="center"><center>
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td width="100%"><pre><font color="#000000">function TestProc: HRESULT;
var
lpDD : IDirectDraw;
begin
Result := DirectDrawCreate(nil, lpDD, nil);
if Result<>0 then raise Exception.Create('Failed create!');
Result := lpDD.QueryInterface(IID_IDirectDraw2, MyDirectDraw);
lpDD._Release; // 这一行代码是多余的,将导致出错
end;</font></pre>
</td>
</tr>
</table>
</center></div>
<p><font face="楷体_GB2312" size="4"> 上面的一段函数建立一个IDirectDraw2类(MyDirectDraw是一个IDirectDraw2类型的全局变量),其中最后一行的lpDD._Release是错误的,在C++中需要这么做,但在Delphi里因为有一段不可见的代码自动完成对lpDD的Release工作,所以会引起一个异常。这段不可见代码是我反汇编后观察汇编源码才得知的。</font></p>
<p align="center"><a href="#topofpage">回到页首</a></p>
<hr width="80%">
<p><a name="whichwnd"></a></p>
<div align="center"><center>
<table border="1" cellpadding="0" cellspacing="0">
<tr>
<td width="100%"><font face="楷体_GB2312" size="5" color="#FFFFFF"><strong><em>为全屏幕方式的DirectDraw对象指定窗口Handle</em></strong></font></td>
</tr>
</table>
</center></div>
<p><font face="楷体_GB2312" size="4"> 在DirectX编程中,IDirectDraw2对象有一个方法名为SetCooperativeLevel,功能是指定该对象的性能级别。调用该方法需要指定一个窗口Handle,在C++中可简单地指定为主程序的窗口Handle,而在Delphi中,如果设定的级别为全屏幕独占方式,则这个Handle一定要是Application.Handle而不是MainForm.Handle,这一点在我所见到的所有关于DirectDraw的封装、构件中均未得见。<br>
在设定为全屏幕独占方式时,还要先使Application.Handle所代表的窗口显示在最前方,否则下一步调用CreateSurface方法就会失败。这一点也是我几经尝试才总结出来的。请见示例代码:</font></p>
<div align="center"><center>
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td width="100%"><pre><font face="Courier New" color="#000000">function CreateFullScreen(out DDrawObj:IDirectDraw2): HRESULT
var
lpDD : IDirectDraw;
begin
Result := DirectDrawCreate(nil, lpDD, nil);
if Result<>DD_OK then
raise Exception.Create('Failed Create!');
Result := lpDD.QueryInterface(IID_IDirectDraw2, DDrawObj):
if Result<>DD_OK then
raise Exception.Create('Failed QueryInterface!');
ShowWindow(Application.Handle, SW_SHOWMAXIMIZED);
Result := DDrawObj.SetCooperativeLevel(
Application.Handle, {</font><font
color="#000000">很重要,一定要是</font><font face="Courier New" color="#000000">Application</font><font
color="#000000">窗口</font><font face="Courier New" color="#000000">}
DDSCL_FULLSCREEN or DDSCL_EXCLUSIVE);
if Result<>DD_OK then
raise Exception.Create('Failed SetCooperativeLevel');
Result := DDrawObj.SetDisplayMode(640,480,8,0,0);
end;</font></pre>
</td>
</tr>
</table>
</center></div>
<p align="center"><a href="#topofpage">回到页首</a></p>
<hr width="80%">
<p><a name="hint"></a></p>
<div align="center"><center>
<table border="1" cellpadding="0" cellspacing="0">
<tr>
<td width="100%"><font face="楷体_GB2312" size="5" color="#FFFFFF"><strong><em>建立自己的Hint窗口</em></strong></font></td>
</tr>
</table>
</center></div>
<p><font face="楷体_GB2312" size="4"> 当鼠标停留在某个控件上时,如果该控件的ShowHint特性为真则会显示出一个黄色的小窗口,这就是Hint窗口。如果从THintWindow派生一个类,并且将新类型赋值给全局变量HintWindowClass,则Delphi的Hint窗口将使用你所建立的新风格。<br>
新类可以重载THintWindow的Paint方法来自己绘制Hint信息,例如下面这段代码将用红色填充整个Hint窗口,然后以黄色来显示Hint信息:</font></p>
<div align="center"><center>
<table border="0" cellpadding="0" cellspacing="0" width="80%">
<tr>
<td width="100%"><pre><font face="Courier New" color="#000000">procedure TNewHintWindow.Paint;
var
R: TRect;
begin
with Canvas do begin
Brush.Color := clRed;
Brush.Style := csClear;
Rectangle(0,0,Width,Height);
end;
R := ClentRect;
Inc(R.Top, 3);
Inc(R.Left, 2);
SetBKMode(Canvas.Handle, TRANSPARENT);
Canvas.Font.Color := clYellow;
DrawText(Canvas.Handle, PChar(Caption),
-1, R, DT_LEFT);
end;</font></pre>
</td>
</tr>
</table>
</center></div>
<p><font face="楷体_GB2312" size="4"> 如果希望Hint窗口是透明的,则可以在新类中加入一个消息捕获过程,使Hint窗口不执行重绘背景的操作:</font></p>
<div align="center"><center>
<table border="0" cellpadding="0" cellspacing="0" width="80%">
<tr>
<td width="100%"><pre><font face="Courier New" color="#000000">type
TNewHintWindow = class(THintWindow);
private
procedure WMEraseBKGND(var Message:TMessage); message WM_ERASEBKGND;
.
.
.
end;
procedure TNewHintWindow.WMEraseBKGND(var Message:TMessage);
begin
Message.Result := 0;
end;</font></pre>
</td>
</tr>
</table>
</center></div>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -