nodejs process.nextTick 同步代码异步执行

原文:nodejs 官方文档链接:http://nodejs.org/api/process.html#process_process_nexttick_callback

process.nextTick(callback)

On the next loop around the event loop call this callback. This is not a simple alias to setTimeout(fn, 0), it’s much more efficient. It typically runs before any other I/O events fire, but there are some exceptions. See process.maxTickDepth below.

在事件循环的下一次循环中调用 callback 回调函数。这不是 setTimeout(fn, 0) 函数的一个简单别名,因为它的效率高多了。该函数能在任何 I/O 事前之前调用我们的回调函数。但是这个函数在层次超过某个限制的时候,也会出现瑕疵,详细见 process.maxTickDepth。

process.nextTick(function() {
    // 回调函数内容
    console.log('nextTick callback');
});

This is important in developing APIs where you want to give the user the chance to assign event handlers after an object has been constructed, but before any I/O has occurred.

如果你想要在【对象创建】之后而【I/O 操作】发生之前执行某些操作,那么这个函数对你而言就十分重要了。

function MyThing(options) {
  this.setupOptions(options);

  process.nextTick(function() {
    this.startDoingStuff();
  }.bind(this));
}

var thing = new MyThing();
thing.getReadyForStuff();

// thing.startDoingStuff() gets called now, not before.

It is very important for APIs to be either 100% synchronous or 100% asynchronous. Consider this example:

【注意!!】保证你的函数一定是同步执行或者一定是异步执行,这非常重要!!参考如下的例子:

// WARNING!  DO NOT USE!  BAD UNSAFE HAZARD!
function maybeSync(arg, cb) {
  if (arg) {
    cb();
    return;
  }

  fs.stat('file', cb);
}

博主注:在上面的例子中,如果 arg 参数未定义或者为假,那么这个函数就会直接执行传入的回调函数 cb 就变成了同步执行,而 arg 参数未真 就会执行 fs.stat 这样一个 I/O 操作变成了异步执行。这样你的操作就不能确定到底是同步还是异步。

This API is hazardous. If you do this:

这样执行是很危险。如果你还不清楚上述行为的危害请看下面的例子:

maybeSync(true, function() {
  foo();
});
bar();

then it’s not clear whether foo() or bar() will be called first.

那么,使用刚才那个不知道是同步还是异步的操作,在编程的时候你就会发现,你不能确定到底是 foo() 先执行,还是 bar() 先执行。

This approach is much better:

用下面的方法就可以更好的解决:

function definitelyAsync(arg, cb) {
  if (arg) {
    process.nextTick(cb);
    return;
  }

  fs.stat('file', cb);
}

博主注:使用 process.nextTick 来调用 cb 会把这个调用变成和文件操作一样的异步操作,这样不论 arg 是真还是假,这个函数都是一个明确的异步操作了。

Windows SDK 教程(三) 一些细节以及动态创建控件

茵蒂克丝

资源编辑器一些小細節

1.如何指定窗口標題

找到窗口的属性面板,在【caption】处可以修改

2.如何設置窗口出現的位置

找到窗口的属性面板,在【位置】下可以设置【center】为【true】则能让窗口居中显示,x 和 y 则是窗口出现的横纵坐标。(横纵坐标起点为屏幕左上角)

3.如何添加圖片

在对话框的编辑界面,打开工具箱的面板,先在资源视图处添加一张图片资源(注意要是位图 *.bmp 格式),找到【pictrue control】拖到对话框上,接着打开【pictrue control】的属性面板,设置【Type】为【bitmap】接着到【Image】处,选择刚刚添加的图片ID即可。

4.如何設置菜單

在资源视图出添加【Menu】资源类型,双击可以编辑菜单项的文字,也可以右键【属性】在属性面板上编辑【caption】,顶级菜单项的ID不可编辑。触发事件的方式与前几件的按钮等类似。注意菜单编辑好了之后,要到对话框【属性面板】的【Menu】选项中添加。

5.如何设置窗口图标

如果用模板创建要用到 SendMessage, 将在后几讲中介绍

动态创建控件

动态创建窗口

#include <windows.h>
 
// 5. 窗口过程处理
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{   
    switch(msg)
    {
        case WM_CLOSE:
            DestroyWindow(hwnd);
        break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}
 
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;  // 更多详细都可以去百度的 http://baike.baidu.com/view/1750396.htm
    HWND hwnd;
    MSG Msg;
    char text[30];
 
    const char szClassName[] = "myWindowClass";
 
    // 1. 设置注册窗口结构体
    wc.cbSize        = sizeof(WNDCLASSEX);              // 注册窗口结构体的大小
    wc.style         = 0;                               // 窗口的样式
    wc.lpfnWndProc   = WndProc;                         // 指向窗口处理过程的函数指针
    wc.cbClsExtra    = 0;                               // 指定紧跟在窗口类结构后的附加字节数
    wc.cbWndExtra    = 0;                               // 指定紧跟在窗口事例后的附加字节数
    wc.hInstance     = hInstance;                       // 本模块的实例句柄
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION); // 图标的句柄
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);     // 光标的句柄
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);        // 背景画刷的句柄
    wc.lpszMenuName  = NULL;                            // 指向菜单的指针
    wc.lpszClassName = szClassName;                     // 注册窗口的类名
    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION); // 和窗口类关联的小图标
 
    // 2. 使用【窗口结构体】注册窗口
    if(!RegisterClassEx(&wc))
    {
        MessageBox(NULL, TEXT("窗口注册失败!"), TEXT("错误"), MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }
 
    // 3. 创建窗口
    hwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,       // 窗口的扩展风格
        szClassName,            // 控件的类名
        TEXT("窗口标题"),       // 指向窗口名称的指针
        WS_OVERLAPPEDWINDOW,    // 窗口风格
        CW_USEDEFAULT, CW_USEDEFAULT, 350, 200, // 窗口的 x,y 坐标以及宽高
        NULL,                   // 父窗口的句柄
        NULL,                   // 菜单的句柄
        hInstance,              // 应用程序实例的句柄
        NULL                    // 指向窗口的创建数据
        );
 
    if(hwnd == NULL)
    {
        MessageBox(NULL, TEXT("窗口创建失败"), TEXT("错误"),MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }
 
    // 4. 显示窗口
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
 
    // 6. 消息循环
    while(GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}

wc.lpszClassName 本质上确实是指向类名称的指针,不过博主没睡好,大清早又重录了一遍,录视频的时候再看到这种容易让人误会的说法就爆了粗口,这个意外 XD

动态创建BUTTON

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{   
    switch(msg)
    {
    case WM_CREATE:
        // 动态创建button
        CreateWindowEx(
                0,              // 拓展样式
                "BUTTON",       // 创建的控件的类名
                "我的按钮",     // 控件显示的文字
                WS_CHILD | WS_VISIBLE,  // 控件的样式
                0, 0,           // 空间出现的 x y 坐标
                100, 30,        // 控件的宽高
                hwnd,           // 父窗口的句柄
                NULL,           // ID
                hgInstance,     // 当前实例的句柄
                NULL            // 传递给控件的参数 默认是NULL
            );
        break;

    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

处理动态 BUTTON 的点击

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{   
    switch(msg)
    {
    case WM_CREATE:
        // 动态创建button
        CreateWindowEx(
                0,              // 拓展样式
                "BUTTON",       // 创建的控件的类名
                "我的按钮",     // 控件显示的文字
                WS_CHILD | WS_VISIBLE,  // 控件的样式
                0, 0,           // 空间出现的 x y 坐标
                100, 30,        // 控件的宽高
                hwnd,           // 父窗口的句柄
                (HMENU)1234,    // ID
                hgInstance,     // 当前实例的句柄
                NULL            // 传递给控件的参数 默认是NULL
            );
        break;

    case WM_COMMAND:
        switch(wParam)
        {
        case 1234:
            echo("按钮被按下");
            break;
        }
        break;

    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

动态创建 EDIT

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{   
    switch(msg)
    {
    case WM_CREATE:
        CreateWindowEx(
                0,
                "EDIT",
                "",
                WS_CHILD | WS_VISIBLE,
                0, 50,
                100, 30,
                hwnd,
                NULL,
                hgInstance,
                NULL
            );
        break;

    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

获取动态 EDIT 中的文字并显示

#include <windows.h>

#define IDC_BUTTON_1 1234

HINSTANCE hgInstance;

void echo(LPSTR str)
{
    MessageBox(NULL, str, TEXT("提示信息"), MB_OK);
}

// 5. 窗口过程处理
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{   
    char text[50];
    switch(msg)
    {
    case WM_CREATE:
        // 动态创建 BUTTON
        CreateWindowEx(
                0,              // 拓展样式
                "BUTTON",       // 创建的控件的类名
                "我的按钮",     // 控件显示的文字
                WS_CHILD | WS_VISIBLE,  // 控件的样式
                0, 0,           // 空间出现的 x y 坐标
                100, 30,        // 控件的宽高
                hwnd,           // 父窗口的句柄
                (HMENU)IDC_BUTTON_1,    // ID
                hgInstance,     // 当前实例的句柄
                NULL            // 传递给控件的参数 默认是NULL
            );

        // 动态创建 EDIT 控件
        CreateWindowEx(
                0,
                "EDIT",
                "",
                WS_CHILD | WS_VISIBLE,
                0, 50,
                100, 30,
                hwnd,
                (HMENU)1000,
                hgInstance,
                NULL
            );
        break;

    case WM_COMMAND:
        switch(wParam)
        {
        // 当按钮被点击
        case IDC_BUTTON_1:
            // 获取Edit 控件中的字符串
            GetDlgItemText(hwnd, 1000, text, sizeof(text));
            // 输出字符串
            echo(text);
            break;
        }
        break;

    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;  // 更多详细都可以去百度的 http://baike.baidu.com/view/1750396.htm
    HWND hwnd;
    MSG Msg;
    char text[30];

    const char szClassName[] = "myWindowClass";

    // 1. 设置注册窗口结构体
    wc.cbSize        = sizeof(WNDCLASSEX);              // 注册窗口结构体的大小
    wc.style         = 0;                               // 窗口的样式
    wc.lpfnWndProc   = WndProc;                         // 窗口处理过程的函数指针
    wc.cbClsExtra    = 0;                               // 指定紧跟在窗口类结构后的附加字节数
    wc.cbWndExtra    = 0;                               // 指定紧跟在窗口事例后的附加字节数
    wc.hInstance     = hInstance;                       // 本模块的实例句柄
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION); // 图标的句柄
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);     // 光标的句柄
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+2);        // 背景画刷的句柄
    wc.lpszMenuName  = NULL;                            // 指向菜单的指针
    wc.lpszClassName = szClassName;                     // 注册窗口的类名
    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION); // 和窗口类关联的小图标

    // 2. 使用【窗口结构体】注册窗口
    if(!RegisterClassEx(&wc))
    {
        MessageBox(NULL, TEXT("窗口注册失败!"), TEXT("错误"), MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    // 3. 创建窗口
    hwnd = CreateWindowEx(
        0,                      // 窗口的扩展风格
        szClassName,            // 注册的类名
        TEXT("窗口标题"),       // 指向窗口名称的指针
        WS_OVERLAPPEDWINDOW,    // 窗口风格
        CW_USEDEFAULT, CW_USEDEFAULT, 350, 200, // 窗口的 x,y 坐标以及宽高
        NULL,                   // 父窗口的句柄
        NULL,                   // 菜单的句柄
        hInstance,              // 应用程序实例的句柄
        NULL                    // 指向窗口的创建数据
        );

    hgInstance = hInstance;

    if(hwnd == NULL)
    {
        MessageBox(NULL, TEXT("窗口创建失败"), TEXT("错误"),MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    // 4. 显示窗口
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    // 6. 消息循环
    while(GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}

文章索引百度网盘
上一讲:Windows SDK 教程(二)简单的计算器
下一讲:Windows SDK 教程(四)记事本与 SendMessage

c 屏幕铺满星星

snow_small

#include <Windows.h>
#include<time.h>

HDC hScreenDC;		// 屏幕画图设备句柄
POINT pData[500];	// 存储星星坐标
long pColor[500];	// 存取原来坐标的颜色值
int Vx;				// vx星星整体水平漂移速度
int Vy;				// vy星星总体垂直下落速度
int PVx;			// pvx单个星星实际水平速度
int PVy;			// pvy单个星星实际垂直速度
int timeCount;		// 计时循环次数

const int StarNum = 500;// 星星数量为500
const int ID_TIMER = 1; // 定时器标识符

const long   StarColor = 0xFEFFFE;  // 星星颜色
const long StarColDown = 0xFFFFFF;  // 积雪星星颜色
const long StarColDuck = 0xFFDDDD;  // 深色积雪颜色

const int ScrnWidth = GetSystemMetrics ( SM_CXSCREEN ); // 全屏宽度
const int ScrnHight = GetSystemMetrics ( SM_CYSCREEN ); // 全屏高度

long Abs(long num)
{
    if (num >= 0)
		return (num);
    else
		return (-num);
}

int Random(int max)
{
    return (rand() % max);
}

void InitPoint(int i)
{
    pData[i].x = Random(ScrnWidth);
    pData[i].y = Random(5);
    pColor[i]  = GetPixel(hScreenDC, pData[i].x, pData[i].y);
}

long GetContrast(int i)
{
    long ColorCmp;	//存取作对比点的颜色值
	long tempR;		//存取ColorCmp的红色值
	long tempG;		//同理
	long tempB;		//同理
	int  Slope;		//存取星星飘落的方向

    if (PVy != 0) 
		Slope = PVx / PVy; // 若pvx/pvy在-1和1之间则slope=0 就取正下方的的像素点
    else // 若pvx/pvy>1,取右下方的点 pvx/pvy<-1 则取左下方
		Slope = 2; // 根据星星飘落方向决定取哪一点做对比点

    if (Slope == 0)
		ColorCmp = GetPixel(hScreenDC, pData[i].x, pData[i].y + 1);
    else if (Slope > 1)
		ColorCmp = GetPixel(hScreenDC, pData[i].x + 1, pData[i].y + 1);
    else
		ColorCmp = GetPixel(hScreenDC, pData[i].x - 1, pData[i].y + 1);

    //确定当前位置没有与另一个星星重叠,重叠就返回0;用于防止由于不同星星重叠造成星星乱堆
    if (ColorCmp == StarColor)
		return 0;

    //分别获取ColorCmp与对比点的蓝,绿,红的差值
    tempB = Abs((ColorCmp >> 16) & 0xff - (pColor[i] >> 16) & 0xff);
    tempG = Abs((ColorCmp >> 8) & 0xff - (pColor[i] >> 8) & 0xff);
    tempR = Abs((ColorCmp) & 0xff - (pColor[i]) & 0xff);

    return ( (tempR + tempG + tempB) / 3 ); //返回对比度
}

void DrawPoint(void)
{
    int i;
    for (i = 0; i < StarNum; i++) //防止星星重叠造成干扰
    {
        if (pColor[i] != StarColor && pColor[i] != -1)
		{
            SetPixel(hScreenDC, pData[i].x, pData[i].y, pColor[i]); //还原上一个位置的颜色
		}

        //设置新的位置,i%3用于将星星分为3类采用不同的速度,以便形成参次感
        PVx = Random(2) - 1 + Vx * (i % 3);
        PVy = Vy * (i % 3 + 1);
        pData[i].x = pData[i].x + PVx; //+pvx与前一点形成的距离差,形成速度的差别
        pData[i].y = pData[i].y + PVy; //同理

        //取的新位置的原始点的颜色值,用于下一步星星飘过时恢复此处的颜色
        pColor[i] = GetPixel(hScreenDC, pData[i].x, pData[i].y);

        //如果获取颜色值失败,表明星星已经飘出屏幕,重新初始化,GetPixel如果获取失败就返回-1.
        if (pColor[i] == -1)
		{
            InitPoint(i);
		}
        else
        {
            //如果星星没有重叠,若对比度较小(即不能堆积)就画出星星
            //Random(16)>5用于防止某些连续而明显的边界截获所有的星星
            if (pColor[i] != StarColor)
            {
				//GetContrast(i)<50判断是否超出对比度50这个值就认为是边缘就会堆积星星
                if (Random(16) > 5 || GetContrast(i) < 50)
                {
                    SetPixel(hScreenDC, pData[i].x, pData[i].y, StarColor);
                } else
                {
				//否者表明找到明显的边界,画出堆积的学,并初始化以便画新的的星星
                    SetPixel(hScreenDC, pData[i].x, pData[i].y - 1, StarColDuck);
                    SetPixel(hScreenDC, pData[i].x - 1, pData[i].y, StarColDuck);
                    SetPixel(hScreenDC, pData[i].x + 1, pData[i].y, StarColDown);
                    InitPoint(i);
                }
            }
        }
    }
}

int main()
{
	MSG msg;
	HWND hwnd = GetActiveWindow(); // 获取当前窗口的句柄

	int i;
	srand(time(0));
	Vx = Random(4) - 2;
	Vy = Random(2) + 2;

	// 随机获取屏幕上一点
	for (i = 0; i < StarNum; i++)
	{
		// 保存坐标
		pData[i].x = Random(ScrnWidth);
		pData[i].y = Random(ScrnHight);
		// 保存颜色
		pColor[i] = GetPixel(hScreenDC, pData[i].x, pData[i].y);
	}

	// 设置计时器
	SetTimer(hwnd, ID_TIMER, 5, NULL);

	// 获得整个屏幕画图设备
	hScreenDC = GetDC(0);

	// 计时
	timeCount = 0;

	// 循环获取操作系统发来的消息
	while (GetMessage(&msg, NULL, 0, 0) != 0)
	{
		switch(msg.message)
		{
		case WM_TIMER: // 时钟信号
			{
				if (timeCount > 200)
				{
					timeCount = 0;
					Vx = Random(4) - 2;
					Vy = Random(2) + 2;
				}
				else
				{
					timeCount += 1;
				}
				DrawPoint();
			}
			break;
		}
	}
}

先随机取的一个屏幕的坐标值(放在pData[i]),对应点的颜色存储在 pcolor[i] 中。
然后将其设置成星星的颜色值 #HFEFFFE(就是画出星星),

到下一个计时器触发时先根据原来存储的 pDate[i],恢复原来点的颜色,再随机取新的坐标值,画新的颜色。反复重复······从而形成星星飘动的效果

在此基础上加上判断屏幕边缘(用自定义的对比度函数 GetContrast,它返回与点 pDate[i]下面相邻的点的颜色值各 RGB 分量之差的和。当这个差大于某一个值,比喻程序用50是认为此处达到边缘)随机,风向等。星星颜色值是 Starcol = #HFEFFFE ,之所以取这一个值,是因为发现有时候2个星星重叠会混乱(如上下2个星星相邻,这样下一个星星会干扰上一个星星取得屏幕原来的颜色),
所以将星星设置成这种接近白色当不如白色FFFFFF常见颜色,当遇到取得的屏幕原始值是 StarColor 就认为出现重叠,就跳过这一个计时器事件不进行屏幕操作

Windows API 教程(八) 注册快捷键

茵蒂克丝

注册快捷键与hook监听键盘的区别

快捷键注册是针对某一个窗口的,注册之后当这个窗口收到快捷键,操作系统就会发送 WM_HOTKEY 消息来通知该窗口。所以快捷键注册是没办法监听全局的,比如你要定义一个快捷键 Ctrl + ` 在桌面上也能调出你的窗口,可是这个消息是发给桌面这个程序去了没办法触发你的程序。 快捷键收到消息之后消息会被转发给注册的hwnd 相当于截断了消息,如果要做全局监听的话还是推荐用 hook。

控制台注册快捷键


#include 
#include 

int main()
{
	MSG msg = {0};
	HWND hConsole = GetActiveWindow(); // 获取当前显示窗口的句柄(即控制台这个窗口的句柄)

	RegisterHotKey(
		hConsole,	// 注册快捷键的窗口句柄
		1,			// 热键标识符
		MOD_CONTROL | MOD_NOREPEAT, // Ctrl 键  No Repeat 不重复发送
		'A'			// A
	);	// Ctrl + A

	RegisterHotKey(hConsole, 2, MOD_CONTROL | MOD_NOREPEAT,'B');// Ctrl + B
	RegisterHotKey(hConsole, 3, MOD_ALT | MOD_NOREPEAT,'A');	// Alt + A
	RegisterHotKey(hConsole, 4, MOD_ALT | MOD_NOREPEAT,'B');	// Alt + B
	RegisterHotKey(hConsole, 5, MOD_NOREPEAT,'S');				// 直接按 S

	
	// 循环获取操作系统发来的消息
	while (GetMessage(&msg, NULL, 0, 0) != 0)
	{
		// 当收到快捷键消息时
		if (msg.message == WM_HOTKEY)
		{
			switch(msg.wParam)
			{
				case 1:
					printf("Ctrl + A 被按下! \n");
				break;

				case 2:
					printf("Ctrl + B 被按下! \n");
				break;

				case 3:
					printf("Alt + A 被按下! \n");
				break;
				
				case 4:
					printf("Alt + B 被按下! \n");
				break;

				case 5:
					printf("S 被按下! \n");
				break;
			}
		}
	}
}

hotkey_console

窗口注册快捷键

在window窗体开发中,窗口过程函数中实际上还可以通过 WM_KEYUP WM_KEYDOWN 等消息获取到用户的按键,大家可以通过如下类似的代码来查看窗口收到的消息(其实控制台下也是可以这样拿的)

case WM_KEYDOWN:
	wsprintf(text, "wParam : 0x%x  lParam : 0x%x", wParam, lParam);
	echo(text);
break;

不过这种的缺点也是很明显的,就是复合键等快捷键定义没有使用 WM_HOTKEY 方便

#include 

void echo(LPSTR str)
{
	MessageBox(NULL, str, TEXT("提示"), MB_OK);
}

// 5. 窗口过程处理
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{	
	CHAR text[50];
	switch(msg)
	{
	// 窗口创建消息
	case WM_CREATE:
		RegisterHotKey(
			hwnd,	// 注册快捷键的窗口句柄
			1,		// 热键标识符避免热键冲突
			MOD_CONTROL | MOD_NOREPEAT, // Ctrl 键  No Repeat 不重复发送
			'A'		// A
			); // Ctrl + A
		RegisterHotKey(hwnd, 2, MOD_CONTROL | MOD_NOREPEAT,'B');// Ctrl + B
		RegisterHotKey(hwnd, 3, MOD_ALT | MOD_NOREPEAT,'A');	// Alt + A
		RegisterHotKey(hwnd, 4, MOD_ALT | MOD_NOREPEAT,'B');	// Alt + B
		RegisterHotKey(hwnd, 5, MOD_NOREPEAT,'S');				// 直接按 S
	break;

	// 快捷键消息
	case WM_HOTKEY:

		switch(wParam)
		{
			case 1:
				printf("Ctrl + A 被按下! \n");
			break;

			case 2:
				printf("Ctrl + B 被按下! \n");
			break;

			case 3:
				printf("Alt + A 被按下! \n");
			break;
			
			case 4:
				printf("Alt + B 被按下! \n");
			break;

			case 5:
				printf("S 被按下! \n");
			break;
		}		
	break;

	case WM_CLOSE:
		DestroyWindow(hwnd);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hwnd, msg, wParam, lParam);
	}
	return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPSTR lpCmdLine, int nCmdShow)
{
	WNDCLASSEX wc;
	HWND hwnd;
	MSG Msg;
	char text[30];

	const char szClassName[] = "myWindowClass";

	// 1. 设置注册窗口结构体
	wc.cbSize        = sizeof(WNDCLASSEX);				// 注册窗口结构体的大小
	wc.style         = 0;								// 窗口的样式
	wc.lpfnWndProc   = WndProc;							// 指向窗口处理过程的函数指针
	wc.cbClsExtra    = 0;								// 指定紧跟在窗口类结构后的附加字节数
	wc.cbWndExtra    = 0;								// 指定紧跟在窗口事例后的附加字节数
	wc.hInstance     = hInstance;						// 本模块的实例句柄
	wc.hIcon         = LoadIcon(hInstance, IDI_APPLICATION);	// 图标的句柄
	wc.hCursor       = LoadCursor(NULL, IDC_ARROW);		// 光标的句柄
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);		// 背景画刷的句柄
	wc.lpszMenuName  = NULL;							// 指向菜单的指针
	wc.lpszClassName = szClassName;						// 指向类名称的指针
	wc.hIconSm       = LoadIcon(hInstance, IDI_APPLICATION);	// 和窗口类关联的小图标

	// 2. 使用【窗口结构体】注册窗口
	if(!RegisterClassEx(&wc))
	{
		MessageBox(NULL, TEXT("窗口注册失败!"), TEXT("错误"), MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}

	// 3. 创建窗口
	hwnd = CreateWindowEx(
		WS_EX_CLIENTEDGE,		// 窗口的扩展风格
		szClassName,			// 指向注册类名的指针
		TEXT("窗口标题"),		// 指向窗口名称的指针
		WS_OVERLAPPEDWINDOW,	// 窗口风格
		CW_USEDEFAULT, CW_USEDEFAULT, 350, 200, // 窗口的 x,y 坐标以及宽高
		NULL,					// 父窗口的句柄
		NULL,					// 菜单的句柄
		hInstance,				// 应用程序实例的句柄
		NULL					// 指向窗口的创建数据
		);

	if(hwnd == NULL)
	{
		MessageBox(NULL, TEXT("窗口创建失败"), TEXT("错误"),MB_ICONEXCLAMATION | MB_OK);
		return 0;
	}

	// 4. 显示窗口
	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	// 6. 消息循环
	while(GetMessage(&Msg, NULL, 0, 0) > 0)
	{
		TranslateMessage(&Msg);
		DispatchMessage(&Msg);
	}
	return Msg.wParam;
}

hotkey_window

Ctrl + Alt 等复杂组合键

还是在控制台下编写例子,其实没有差多少


#include 
#include 

int main()
{
	MSG msg = {0};
	HWND hConsole = GetActiveWindow();

	// Ctrl + D
	RegisterHotKey(hConsole,1,MOD_CONTROL | MOD_NOREPEAT,'D');
	// Win键 + Z
	RegisterHotKey(hConsole,2,MOD_WIN | MOD_NOREPEAT,'Z');
	// Ctrl + Alt + S
	RegisterHotKey(hConsole,3,MOD_CONTROL | MOD_ALT | MOD_NOREPEAT,'S');
	// Ctrl + Alt + Shift + A
	RegisterHotKey(hConsole,4,MOD_CONTROL | MOD_ALT | MOD_SHIFT | MOD_NOREPEAT,'A');
	
	// 循环获取操作系统发来的消息
	while (GetMessage(&msg, NULL, 0, 0) != 0)
	{
		// 当收到快捷键消息时
		if (msg.message == WM_HOTKEY)
		{
			printf("收到 WM_HOTKEY 快捷键消息n");
			printf("wParam : 0x%x  lParam : 0x%x n", msg.wParam, msg.lParam);

			switch(msg.wParam)
			{
				case 1:
					printf("Ctrl + D 被按下! \n");
				break

				case 2:
					printf("Win + Z 被按下! \n");
				break

				case 3:
					printf("Ctrl + Alt + S 被按下! \n");
				break

				case 4:
					printf("Ctrl + Alt + Shift + A 被按下! \n");
				break;
			}
		}
	}
}

hotkey_complex

文章索引
上一讲: Windows API 教程(七) hook 监听
下一讲: Windows API 教程(九) 网络编程