第一个Windows程序
环境:winxp VC++ 6
首先vc6中新建选择Win32 Application,也可以选控制台,没有什么本质区别 ,但是缺什么就要自己添,然后选一个简单的Win32 Application
可以看到会给我们生成一个入口
我们来看看winmain的参数
int WINAPI WinMain(
HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line
int nCmdShow // show state
);
hInstance
在win32里面,只要是h开头的,通常都称为句柄,句柄又有各种各样的类型,比如HANDLE、HWND、HDC,HINSTANCE是模块的句柄,当然还是索引的本质,并且对象是在内核,还是DWORD,四字节,不同的名字是为了可读性更好
hPrevInstance
这个参数永远为空,不用管他
lpCmdLine
之前说创建进程时,任何一个进程都是其他进程创建得,我们用CreateProcess API创建进程得函数,第二个参数是命令行参数,这个参数就会传给这个lpCmdLine
nCmdShow
CreateProcess函数得倒数第二参数,启动信息就会传给nCmdShow参数
输出内存地址
#include "stdafx.h"
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
char OutBuff[0x80];
DWORD dwAddr = (DWORD)hInstance;
sprintf(OutBuff,"模块地址:%x\n",dwAddr);//格式化输出
OutputDebugString(OutBuff);//打印
return 0;
}
这里得函数自行查阅,在头文件添上包含stdio.h
输出了400000
窗口程序代码
// 第一个窗口程序.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
LRESULT CALLBACK WindowProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
//第一步:定义那你的窗口是什么样的?
char OutBuff[0x80];
TCHAR className[] = TEXT("My First Window");//类名字
WNDCLASS wndclass = {0};//初始化
wndclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND;//背景颜色
wndclass.lpszClassName = className;//类名字
wndclass.hInstance = hInstance;//属于当前程序
wndclass.lpfnWndProc = WindowProc;//窗口函数
RegisterClass(&wndclass);//注册窗口类
//第二步:创建并显示窗口
HWND hwnd = CreateWindow(
className, //注册的类名字
TEXT("我的第一个窗口"), //窗口的名字
WS_OVERLAPPEDWINDOW, //
10,
10,
600,
300,
NULL,
NULL,
hInstance,
NULL
);
if(hwnd == NULL) //创建失败
{
sprintf(OutBuff,"Error:%d\n",GetLastError()); //失败原因
OutputDebugString(OutBuff);
return 0;
}
ShowWindow(hwnd,SW_SHOW); //显示窗口
//第三步:接收消息并处理
MSG msg;
BOOL bRet;
while((bRet = GetMessage(&msg,NULL,0,0)) !=0)
{
if(bRet == -1)
{
//handle the error and possibly exit
sprintf(OutBuff,"Error:%d\n",GetLastError());
OutputDebugString(OutBuff);
}
else
{
//转换消息
//TranslateMessage(&msg);
//分发消息
DispatchMessage(&msg);
}
}
return 0;
}
LRESULT CALLBACK WindowProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
第一步:定义那你的窗口是什么样的?
在windows窗口有一个类,叫WNDCALSS,里面得成员有许多,不用去理解每一个成员的含义,只有4个成员是有用的,只要知道是用来创建窗口就行了
wndclass.hbrBackground
背景颜色,可以选择系统已经有的背景色(msdn文档里有),然后这是个画刷类型
wndclass.lpszClassName
给类起一个名字
wndclass.hInstance
属于那个程序
wndclass.lpfnWndProc
窗口函数,用来处理消息后的动作,比如当我点关闭,关闭这个动作就是这个窗口程序完成的,这个格式不是随便起的,在文档中有详细说明
LRESULT CALLBACK WindowProc( HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message paramete
);
那先不管,直接复制过来,WindowProc名称看你自己
注意下面要有对应的函数定义,(代码尾部,可以写return 0后面还要改的),代码顶部,是函数的声明
这里并不是调用函数,只是告诉操作系统,当前窗口对应的消息窗口回调函数在哪里,你只管告诉我函数名就可以了,操作系统会调用这个函数
RegisterClass
把创建好的类告诉给操作系统,好找到他
第二步:创建并显示窗口
CreateWindow
HWND CreateWindow(
LPCTSTR lpClassName, // registered class name
LPCTSTR lpWindowName, // window name
DWORD dwStyle, // window style
int x, // horizontal position of window
int y, // vertical position of window
int nWidth, // window width
int nHeight, // window height
HWND hWndParent, // handle to parent or owner window
HMENU hMenu, // menu handle or child identifier
HINSTANCE hInstance, // handle to application instance
LPVOID lpParam // window-creation data
);
lpClassName
注册的类名字
lpWindowName
窗口名字
dwStyle
风格
有点多就不截完了,可自行查找资料了解风格
int x, int y,
相对于父窗口的坐标
nWidth,nHeight
窗口的宽度,高度
hWndParent
父窗口的句柄
菜单的句柄
hInstance
属于那个模块
lpParam
附加数据
ShowWindow
BOOL ShowWindow(
HWND hWnd, // handle to window
int nCmdShow // show state
);
hWnd
显示那个窗口
nCmdShow
什么形式显示
MSDN里有具体的形式介绍
当创建了窗口对象,就会生成消息队列
第三步:接收消息并处理
GetMessage
BOOL GetMessage(
LPMSG lpMsg, // message information
HWND hWnd, // handle to window
UINT wMsgFilterMin, // first message
UINT wMsgFilterMax // last message
);
lpMsg
out类型参数
hWnd, wMsgFilterMin, wMsgFilterMax
全是过滤参数,比如说,这个线程有100个窗口,消息队列里是所有窗口的信息,你要准确的找自己想要的,就需要这些过滤的参数,空的话就是取所有的消息
如果没有就会阻塞
Return Values
If the function retrieves a message other than WM_QUIT, the return value is nonzero.
If the function retrieves the WM_QUIT message, the return value is zero.
If there is an error, the return value is -1. For example, the function fails if hWnd is an invalid window handle or lpMsg is an invalid pointer. To get extended error information, call [GetLastError](JavaScript:hhobj_1.Click()).
Warning Because the return value can be nonzero, zero, or -1, avoid code like this:
while (GetMessage( lpMsg, hWnd, 0, 0)) ...
The possibility of a -1 return value means that such code can lead to fatal application errors.
TranslateMessage
转换消息
DispatchMessage
分发消息,调用消息处理函数。
使用分发的原因是:这种消息处理函数不是我们调用的,而是由操作系统调用的
目的:我们取得了消息,但我们并不知道,每一个窗口对应的消息处理函数在哪,也就是我不能区分是那个窗口的消息,但是,内核知道,所以要进内核,拿着句柄去比对是那个窗口
最终执行必须要调用一个默认的消息处理函数,即代码末尾的return ……
DefWindowProc
这个函数包含了基本的常用功能,比如,缩小、放大、拖动等,不用我们去实现,只需要去调用就行了
总结
- 知道了窗口程序的大致流程
- 消息队列与窗口的关系
- 为什么要分发消息的原因