回调函数,简单来说,就是当某一事件发生,触发某一函数的执行,那么,这个函数可以看作回调函数。
我们知道,在Windows应用程序中,程序的入口是WinMain,为什么是WinMain呢?因为WinMain可以看作Windows定义的一个接口,Windows对写应用程序的程序员说:你在WinMain函数中写程序,一点击“运行”按钮(注意这个事件),WinMain函数就会被Windows调用,因此WinMain函数可以看作一个最简单的回调函数,如下:
#include <windows.h>
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd
)
{
return 0;
}
实际上,Windows和应用程序可以看作两个不同的软件模块,在这两个模块中,存在相互调用的关系,见如下程序:
#include <windows.h>
int WINAPI WinMain( // Windows调用WinMain函数
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd
)
{
// WinMain函数调用Windows的API函数:MessageBox
MessageBox(0, "最近有暴雨!", "温馨提示:", 0);
return 0;
}
下面,我们来看看稍微复杂一点的情况:
#include <windows.h>
// 窗口模子对应的回调函数MyFun,由写应用程序的程序员完成
// 一旦Windows感觉到鼠标或键盘的动作,便会调用MyFun函数
LRESULT CALLBACK MyFun(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
// uMsg即为鼠标或键盘对应的消息
switch(uMsg)
{
case WM_CLOSE: // 关闭窗口的消息
if(IDYES == MessageBox(hwnd, "要关闭窗口么?", "message", MB_YESNO))
{
DestroyWindow(hwnd);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd
)
{
WNDCLASS wnd;
wnd.cbClsExtra = 0;
wnd.cbWndExtra = 0;
wnd.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wnd.hCursor = LoadCursor(NULL, IDC_CROSS);
wnd.hIcon = LoadIcon(NULL, IDI_ERROR);
wnd.hInstance = hInstance;
// 指定窗口模子对应的回调函数为MyFun,一旦鼠标或键盘在窗口上操作
// Windows便会感知到该操作(形成消息),并触发回调函数MyFun的执行
wnd.lpfnWndProc = MyFun;
wnd.lpszClassName = "ClassName";
wnd.lpszMenuName = NULL;
wnd.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wnd);
HWND hwnd;
hwnd = CreateWindow("ClassName", "温馨提示:", WS_OVERLAPPEDWINDOW,
500, 500, 200, 200, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, SW_SHOWNORMAL);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
运行该程序,便有了一个窗口,当鼠标在该窗口上移动时,会调用MyFun这个函数。你看看,鼠标移动,函数MyFun得到执行,MyFun不就是回调函数么? 值得一提的是:虽然WinMain函数和MyFun函数都是回调函数,但WinMain函数只能由Windows调用(写应用程序的程序员不可以调用),而MyFun函数通常是由Windows调用(写应用程序的程序员也可以调用, 但不多见)。
那么,如何知道鼠标移动后,MyFun函数得到了执行呢?用printf在MyFun中打印一下就知道了,如下:
#include <windows.h>
#include <stdio.h>
#pragma comment( linker, "/subsystem:\"console\" /entry:\"WinMainCRTStartup\"")
LRESULT CALLBACK MyFun(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
static int flag = 0;
// 从控制台中可以看到MyFun函数的调用情况
printf("调用MyFun函数的次数:%d\n", ++flag);
switch(uMsg)
{
case WM_CLOSE:
if(IDYES == MessageBox(hwnd, "要关闭窗口么?", "message", MB_YESNO))
{
DestroyWindow(hwnd);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd
)
{
WNDCLASS wnd;
wnd.cbClsExtra = 0;
wnd.cbWndExtra = 0;
wnd.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wnd.hCursor = LoadCursor(NULL, IDC_CROSS);
wnd.hIcon = LoadIcon(NULL, IDI_ERROR);
wnd.hInstance = hInstance;
wnd.lpfnWndProc = MyFun;
wnd.lpszClassName = "ClassName";
wnd.lpszMenuName = NULL;
wnd.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wnd);
HWND hwnd;
hwnd = CreateWindow("ClassName", "温馨提示:", WS_OVERLAPPEDWINDOW,
500, 500, 200, 200, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, SW_SHOWNORMAL);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}