Use API to Create A Window In Delphi
前言:
感谢龙芯发表的用API创建一个窗体的代码,可是......怎么没有注释&讲解啊,这对入门的人来说可是一场大的人祸,于是激发我写一篇
关于Delphi下用API创建窗体的文章,我想把他作为连载,主要是因为最近我的哥们来北京了,得陪他好好玩两天,所以只能利用晚上零碎的时
间来赶文章,如果有什么不正确的地方还希望大家指正!
一、从龙芯的代码开始
首先我们把龙芯提供的代码(http://www.cstc.net.cn/docs/docs.php?id=90)去除所有实现部分,只保留框架,于是这个程序的结构就清
清楚楚了。
#include <windows.h>
//声明一个回调函数 WndProc
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
//windows程序的入口,相当于C程序中的main
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,LPSTR CmdLine,int nCmdShow)
{
//...
}
//回调函数WndProc的具体实现
LRESULT CALLBACK WndProc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)
{
//...
}
现在大家一定有很多问题了,为什么我不讲Object Pascal反倒先讲起C++代码呢?为什么要回调函数呢?函数中的参数又是什么意思?别
急,我一个一个的讲解给你们听。
(1)为什么先讲C++代码
看过MSDN的资料吗?发现里面的函数声明都是C++的格式吗,例程也是VC++的?嘿嘿,所以说一定要能看懂C++代码,他是我们走向Delphi高
手的有利工具。好了,废话不多说,开始进入正题。如果你看不懂,没有关系,只要你能明白运作过程就可以了!
(2)函数功能
C语言编程至少有一个主程序,其名字是main()。Windows程序则至少两个主程序,一个是WinMain(),另一个是窗口过程函数WndProc(),其
中WinMain函数为应用程序的入口点,它的名字一定要是WinMain。
在Windows中,应用程序通过要求Windows完成指定操作,而承担这项通信任务的API函数就是Windows的相应窗口函数WndProc。在dos里,
程序能直接控制事件的发生顺序,结果等。而在Windows里,应用程序不直接调用任何窗口函数,而是等待Windows调用窗口函数,请求完成任
务或返回信息。为保证Windows调用这个窗口函数,这个函数必须先向Windows登记(注册),然后在Windows实施相应操作时回调,所以窗口函数
又称为回调函数。WndProc是一个主回调函数,Windows至少有一个回调函数。
再看每个函数的参数。
WinMain函数:hInstance: 应用程序当前事例的句柄
hPreInstance:应用程序的前事例的句柄。对于32位程序,该参数总为NULL
CmdLine:指向应用程序命令行的空字符串的指针,不包括函数名。
nCmdShow:指明窗口如何显示。该参数可以是下列值之一:
WndProc函数:hWnd:指向窗口的句柄
Msg: 指定消息类型
wParam:指定其余的、消息特定的信息。该参数的内容与UMsg参数值有关
lParam:指定其余的、消息特定的信息。该参数的内容与uMsg参数值有关
通过上面两个函数,一个Windows程序的框架就建立起来了!
今天就先到这,下一讲我会详细分析各个函数的具体功能了,可能有点复杂,希望大家耐心,我会尽量的讲解清楚!
二、函数功能的实现
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,LPSTR CmdLine,int nCmdShow)
{
char szClassName[]="BasicWin32";//窗口的类名
WNDCLASSEX wc;
//窗口类的结构类型,接下来就要用你设计的窗口属性的信息填充结构变量wc的域
wc.cbClsExtra=0;//窗口类附加数据
wc.cbSize=sizeof(wc);//wc的数据大小
wc.cbWndExtra=0;//窗口附加数据
wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);//用来着色窗口背景的刷子颜色
wc.hCursor=LoadCursor(NULL,IDC_ARROW);//光标的外观
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);//窗口图标
wc.hIconSm=LoadIcon(NULL,IDI_APPLICATION);//窗口图标
wc.hInstance=hInstance;//拥有窗口类的实例句柄
wc.lpfnWndProc=WndProc;//指定窗口的回调函数
wc.lpszClassName= szClassName;//窗口类名
wc.lpszMenuName=NULL; //窗口菜单名,因为本程序无彩单所以为NULL
wc.style=CS_VREDRAW|CS_HREDRAW;//窗口类风格
//刚才的填充过程就相当于你在Delphi中给窗口设置属性一样
RegisterClassEx(&wc);//向Windows注册窗口
HWND hWnd;
//CreateWindowEx函数用来建立窗口的实例
hWnd=CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, //指定窗口的扩展风格
szClassName, //指定了窗口的类名
"This is the caption", //指定窗口标题
WS_OVERLAPPEDWINDOW|WS_HSCROLL|WS_VSCROLL,//指定创建窗口的风格
CW_USEDEFAULT,//窗口的X坐标
CW_USEDEFAULT,//窗口的Y坐标
CW_USEDEFAULT,//窗口宽
CW_USEDEFAULT,//窗口高
NULL, //父窗口的句柄
NULL, //指定窗口的菜单句柄
hInstance, //窗口句柄
NULL); //参数指针
//显示窗口
ShowWindow(hWnd,nCmdShow);
//创建完成窗口后Windows并没有将其显示到显示器上,只是在内存中保存了这 //个窗口的全部信息,要显示
这个窗口,需要使用 ShowWindow(hwnd,iCmdShow) //函数.这个函数有两个参数,第一个参数是用
CreateWindow()函数创建窗口的 //时候返回的窗口句柄,第二个参数是WinMain()函数的第三个参数。
UpdateWindow(hWnd);//为了保证窗口能够正常的显示到显示器上还要调用这个函数。
MSG Msg;
//建立窗口消息循环
while(GetMessage(&Msg,NULL,0,0))//GetMessage函数从线程的消息队列里接收消息信息
{
TranslateMessage(&Msg);//该函数将虚拟键消息转换为字符消息。字符消息被寄送到调用线程的消息队列里
DispatchMessage(&Msg); //该函数调度一个消息给窗口程序。通常调度从GetMessage取得的消息
}
UnregisterClass(szClassName,hInstance); //注销窗口的注册
return 0;
}
//========================
LRESULT CALLBACK WndProc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)
{
LPCTSTR str1="Pressed Enter";
LPCTSTR str2="An simple win32 application";
PAINTSTRUCT ps;
HDC hDC=GetDC(hWnd);
//该函数检索一指定窗口的客户区域或整个屏幕的显示设备上下文环境的句柄
//以后可以在GDI函数中使用该句柄来在设备上下文环境中绘图
switch(Msg)//对消息进行处理
{
case WM_PAINT://绘制消息
BeginPaint(hWnd,&ps);//开始绘制
//此处可添加绘制代码
TextOut(hDC,0,20,str2,lstrlen(str2)); //输出文字
EndPaint(hWnd,&ps);//结束绘制
break;
case WM_SIZE://窗口大小更改
InvalidateRect(hWnd,NULL,false);,
//视图在收到WM_SIZE消息后需要重绘这一区域
break;
case WM_KEYDOWN://键盘被按下
if(wParam==VK_RETURN) //判断按下的是否为回车键
TextOut(GetDC(hWnd),0,0,str1,lstrlen(str1));
if(wParam==VK_ESCAPE) //判断按下的是否为ESC键
PostQuitMessage(WM_QUIT); //发送退出消息
break;
case WM_DESTROY:
PostQuitMessage(WM_QUIT);
break;
default:
return DefWindowProc(hWnd,Msg,wParam,lParam);
//窗口不处理的消息全部委托给DefWindowProc函数处理
}
return 0;
}
函数中使用的API我没有做更加详细的解说,担心这样做会让读者分心从而忽略主要流程,如果各位有兴趣可以上MSDN查找或者以后我再开帖子
解说。大家暂时只需要照葫芦画瓢就是了!接下来我们可以进入Delphi的世界了。
三、开始Delphi的旅程
各位可以打开你们的Delphi,在File->New->Other的New页中选择Console Application。
接下来删除代码编辑框中的 {$APPTYPE CONSOLE} 这一行,它的功能是告诉编译器,这是个控制台程序(你也可以试一下不删除会是怎么
样的状况)。完整的代码如下:
program MinWindow;
uses Windows, Messages; //注意使用的文件
function WindowProc(hwnd : HWND; uMsg : Cardinal; wParam : WPARAM;
lParam : LPARAM) : LResult; stdcall;
begin
Result := 0;
case uMsg of
// 鼠标左键按下消息,弹出一个MessageBox
WM_RBUTTONDOWN : MessageBox(hwnd, 'Hello!', '这是用API建立的窗体!', MB_ICONINFORMATION);
// 关闭窗口消息,当用户关闭窗口后,通知主消息循环结束程序
WM_CLOSE : PostMessage(hwnd, WM_QUIT, 0, 0);
else
Result := DefWindowProc(hWnd, uMsg, wParam, lParam);
end;
end;
var //主函数中的变量声明
wndcls : WNDCLASS; // 窗口类的记录(结构)类型
hWnd : THandle;
Msg : tagMSG;
begin //主函数开始
wndcls.style := CS_DBLCLKS;
wndcls.cbClsExtra := 0;
wndcls.cbWndExtra := 0;
wndcls.lpfnWndProc := @WindowProc;
wndcls.hInstance := hInstance;
wndcls.hIcon := 0;
wndcls.hCursor := LoadCursor(hInstance, 'IDC_ARROW');
wndcls.hbrBackground := COLOR_WINDOWFRAME;
wndcls.lpszMenuName := nil;
wndcls.lpszClassName := 'WindowClassDemo';
if RegisterClass(wndcls) = 0 then Exit;
hWnd := CreateWindow('WindowClassDemo',
'WindowDemo',
WS_BORDER or WS_CAPTION or WS_SYSMENU, // 窗口类型
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
Integer(CW_USEDEFAULT),
0,
0,
hInstance,
nil);
if hWnd = 0 then Exit;
// 显示窗口
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
while GetMessage(Msg, hWnd, 0, 0) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end. //主函数结束
程序运行后,在生成的窗口上点击鼠标左键,就会弹出一个MessageBox。
就这个例子来说,在Delphi中编写,只是将C++语言转换成了Object Pascal语言,不牵扯到其他的太多问题。需要说明的是,由于C语言和
PASCAL语言的不同,在PASCAL语言中没有明显的main函数这一程序入口点,但是他仍然是有自己的main函数。
大家在Turbo Pascal中写以下代码:
begin
{...}
end.
然后编译,是不是可以正常通过?按照要求,这个代码严谨的写法应该是:
program MinCode;
begin
{...}
end.
所以通常你可以这样理解,在含有保留字program的单元中begin 和 end. 之间包含的就是主函数。
有了VC++的代码为基础和参照,相信大家看懂这个代码难度不大!
连载到这就该结束了。在这个连载中,讲解原理我们用了两章,但是最后写Delphi代码却很简单,验证VC++确实是帮助我们走向Delphi成
功的必修基础!

浙公网安备 33010602011771号