关键词语:
message based, event driven, multitasking, multithreading, console programming
消息基础, 事件驱动,多任务,多线程,控制台程序
以消息为基础,以事件驱动之(message based, event driven)
每一个Windows 程序都应该有一个回路如下:
MSG msg;
while (GetMessage(&msg, NULL, NULL, NULL)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
typedef struct tagMSG
{
HWND hwnd;
UINT message; // WM_xxx,例如WM_MOUSEMOVE,WM_SIZE...
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG;

接受并处理消息的主角就是窗口。每一个窗口都应该有一个函数负责处理消息,程序员
必须负责设计这个所谓的「窗口函数」(window procedure,或称为window function)。
如果窗口获得一个消息,这个窗口函数必须判断消息的类别,决定处理的方式。

WinMain(hInst, hPrev, ...)
{
  MSG msg;
  RegisterClass(...);
  CreateWindow(...);
  ShowWindow(...);
  UpdateWindow(...);
  while(GetMessage(&msg...)) {
  TranslateMessage(...);
  DispatchMessage(...);
}
return msg.wParam;
}

//窗口函数
WndProc(hwnd, msg, wParam, lParam)
{
  switch (msg) {
    case WM_CREATE: ...
    case WM_COMMAND: ...
    case WM_LBUTTONDOWN: ...
    case WM_PAINT: ...
    case WM_CLOSE: ...
    case WM_DESTROY: ...
    default: return DefWindowProc(...);
}
  return(0);
}


1.程序进入点WinMain
main 是一般C 程序的进入点:
int main(int argc, char *argv[ ], char *envp[ ]);
{
...
}
WinMain 则是Windows 程序的进入点:
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow)
{
...
}
// 在Win32 中CALLBACK 被定义为__stdcall,是一种函数调用习惯,关系到
// 参数挤压到堆栈的次序,以及处理堆栈的责任归属。其它的函数调用习惯还有
// _pascal 和_cdecl

2.窗口
窗口产生之前,其属性必须先设定好。所谓属性包括窗口的「外貌」和「行为」,一个窗口
的边框、颜色、标题、位置等等就是其外貌,而窗口接收消息后的反应就是其行为(具
体地说就是指窗口函数本身)。程序必须在产生窗口之前先利用API 函数RegisterClass
设定属性(我们称此动作为注册窗口类别)。RegisterClass 需要一个大型数据结构WNDCLASS 做为参数,CreateWindow 则另需要11 个参数。
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)WndProc; //窗口函数
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, "jjhouricon");  //窗口图标(icon)
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "GenericMenu";  //菜单(menu)
wc.lpszClassName = "Generic";  //窗口类別名称


RegisterClass(&wc);
HWND hWnd;

hWnd = CreateWindow(
"Generic",
"Generic Sample Application",  //窗口标題(caption)
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, // left
CW_USEDEFAULT, // top
CW_USEDEFAULT, // width
CW_USEDEFAULT, // height
NULL,
NULL,
hInstance,
NULL
);

jjhouricon ICON DISCARDABLE "jjhour.ico"  //窗口图标(icon)

GenericMenu MENU DISCARDABLE
BEGIN
POPUP "&File"
...
POPUP "&Edit"
...
POPUP "&Help"  //}菜单(menu)
...
END

//窗口函数
LRESULT CALLBACK WndProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
...
}

注意,CreateWindow 只产生窗口,并不显示窗口,所以稍后我们必须再利用ShowWindow 将之显示在屏幕上。又,我们希望先传送个WM_PAINT 给窗口, 以驱动窗口的绘图动作, 所以调用UpdateWindow

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
  if (!hPrevInstance)
  if (!InitApplication(hInstance))
    return (FALSE);
  if (!InitInstance(hInstance, nCmdShow))
    return (FALSE)
  ...
}
//--------------------------------------------------
BOOL InitApplication(HINSTANCE hInstance)
{
  WNDCLASS wc;
  ...
  return (RegisterClass(&wc));
}
//--------------------------------------------------
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
  _hWnd = CreateWindow(...);
  ...
}

3.消息循环
初始化工作完成后,WinMain 进入所谓的消息循环:
while (GetMessage(&msg,...)) {
TranslateMessage(&msg); // 转换键盘消息
DispatchMessage(&msg); // 分派消息
}
其中的TranslateMessage 是为了将键盘消息转化,DispatchMessage 会将消息传给窗口函数去处理。没有指定函数名称,却可以将消息传送过去,岂不是很玄?这是因为消息发生之时,操作系统已根据当时状态,为它标明了所属窗口,而窗口所属之窗口类别又已经明白标示了窗口函数(也就wc.lpfnWndProc 所指定的函数),所以DispatchMessage自有脉络可寻。窗口函数通常利switch/case 方式判断消息种类,以决定处置方式。由于它是被Windows 系统所调用的(我们并没有在应用程序任何地方调用此函数),所以这是一种call back 函数,意思是指「在你的程序中,被Windows 系统调用」的函数。这些函数虽然由你设计,但是永远不会也不该被你调用,它们是为Windows 系统准备的。

5.console 程序
如果程序是以main 为进入点,调用C runtime 函数和「不牵扯GUI」的Win32 API 函数,那么就是一个console 程序,console窗口将成为其标准输入和输出装置(cin 和cout)

posted on 2007-04-25 13:12  左左右右  阅读(552)  评论(0编辑  收藏  举报