WIN32 SDK API的基础问题(窗口显示)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WIN32 SDK API的基础问题(窗口显示)相关的知识,希望对你有一定的参考价值。

在WinMain入口点后,程序要做设置窗口,注册窗口,更新窗口,显示窗口,
然后启动消息循环泵来处理WINXP给这个窗口的操作

有个问题,如果在这个程序消息循环的过程时候,
用户做了一个操作,把窗口拖曳起来(改变形状大小?)
那么是否重新做去到WinMain入口点,再做“设置窗口,注册窗口,更新窗口,显示窗口”这些动作?
消息循环泵要中断?

WinProc那边的情况又是怎么样的呢?

1. 消息循环过程的时候,不管移动窗口还是调整窗口大小,程序都在消息循环中执行,程序不会再去执行WinMain()的入口点,也不会再去重复设置窗口类,注册窗口类;因为在WinMain()中基本上是按顺序执行的,直到抵达while()循环,在开始执行消息泵;况且WinMain()中没有任何往前跳转的语句所以不会程序不会再去执行WinMain()入口点,不会再去重复设置窗口类,注册窗口类。

以下简略程序;
WinMain()

......
WNDCLASSEX win;
RegisterClassEx(&win);
......
CreateWindowEx();
......
while(true)

PeekMessage(&msg,NULL,0,0);

if(msg.message==WM_QUIT)
break;

TranslateMessage(&msg);
DispatchMessage(&msg);



LRESULT CALLBACK Winproc()

switch(msg)

case WM_PAINT:

break;
default:break;

............

至于更新窗口和显示窗口这几个是有可能执行的。如果Winproc()里包含显示窗口和更新窗口的响应,则他们就会执行。

2. DispatchMessage()函数将消息发送给Winproc(),然后Winproc()根据消息类型作出响应.(严格的讲消息是DispatchMessage()先将消息传递给操作系统,再由系统传给Winproc(),过程比较复杂,不必深究)
参考技术A 不需要的,消息一直在循环,改变大小有WM_SIZE消息发过去的,需要做的可能就是重画一次,响应一下WM_PAINT吧

Win32编程API 基础篇 -- 3.消息处理 根据英文教程翻译

消息处理

  例子:窗口点击

  好的,现在我们已经得到一个窗口了,但我们什么也做不了除了DefWindowProc()允许窗口大小被调整,最大最小化等。。。这不是很激动人心啊

  在接下来的一小节中我将向你展示如何修改现有的程序,让它做一些新的事情,这样我就可以告诉你,“处理消息然后这样做。。。”,我会明白我的意思是什么并且在不需要看完完整的栗子的基础上完成它。所以不管怎样,集中注意力

  OK,对初学者来说拿最近的一个窗口程序的代码,保证编译通过并且正常运行,然后你就可以在这份代码的基础上进行一些小修改,或者把代码复制到新的一个项目中进行修改。

  我们要添加一项功能,只要用户点击窗口,就显示我们程序的名字。这不是很激动人心,基本上是通过获取处理消息的句柄实现的,让我们看看WndProc()中有什么

 

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;
}

 

  如果我们想处理鼠标点击事件,我们需要添加一个WM_LBUTTONDOWN的处理器(或者 WM_RBUTTONDOWN, WM_MBUTTONDOWN分别对应鼠标右击和中击)

  如果我或其他人提到处理一个消息,意思是在窗口类的WndProc()中添加相应的处理消息处理程序,如下所示:

 

 1 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 2 {
 3    switch(msg)
 4    {
 5       case WM_LBUTTONDOWN:    // <-
 6                               // <-    我们只是添加了这个东西
 7       break;                  // <-
 8       case WM_CLOSE:
 9          DestroyWindow(hwnd);
10       break;
11       case WM_DESTROY:
12          PostQuitMessage(0);
13       break;
14       default:
15          return DefWindowProc(hwnd, msg, wParam, lParam);
16    }
17    return 0;
18 }

 

  你处理消息的顺序是很重要的,确保你有在每个case结尾加上break;,你可以看到我们在switch()中添加了另一个case,现在我们希望当代码跑到这个的时候有些事情发生。

  首先我会展示我们要添加的代码(向用户显示我们程序的文件名),然后我会把这些代码集合到我们的程序中。接下来我可能只会向你展示这些代码并且让你自己集合到程序中去,这对我来说是好的因为不需要说太多废话,对你来说也是好的,这样你就可以把代码嵌入到任何你想加入的程序中去而不仅仅只是我呈现给大家的这个栗子。如果你不确定怎么做,看看ZIP样例代码文件中关于这一小节的内容。

 

1 GetModuleFileName(hInstance, szFileName, MAX_PATH);
2 MessageBox(hwnd, szFileName, "This program is:", MB_OK | MB_ICONINFORMATION);

 

  现在这段代码不能代表它本身,它不能被插入我们旧代码中的任何位置。我们明确地想让它在用户点击窗口时运行,所以这就是我想整合进我们的骨骼程序中的一点代码。

 

 1 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 2 {
 3     switch(msg)
 4     {
 5         case WM_LBUTTONDOWN:
 6 // BEGIN NEW CODE
 7         {
 8             char szFileName[MAX_PATH];
 9             HINSTANCE hInstance = GetModuleHandle(NULL);
10 
11             GetModuleFileName(hInstance, szFileName, MAX_PATH);
12             MessageBox(hwnd, szFileName, "This program is:", MB_OK | MB_ICONINFORMATION);
13         }
14 // END NEW CODE
15         break;
16         case WM_CLOSE:
17             DestroyWindow(hwnd);
18         break;
19         case WM_DESTROY:
20             PostQuitMessage(0);
21         break;
22         default:
23             return DefWindowProc(hwnd, msg, wParam, lParam);
24     }
25     return 0;
26 }

 

  注意花括号,当我们在switch()中声明一个新的变量的时候就需要加上花括号,这应该是C语言的基础知识,但我想我应该指出你正在做的所有事情。

  所以你添加那段代码,然后编译它,如果它正常运行,点击窗口,然后你会看到一个现实你可执行文件的消息盒。

  你会注意到在这里添加了两个变量,hInstanceszFileName。查阅一下GetModuleFileName()函数,你会发现第一个参数是一个HINSTANCE跟可执行模块相关(我们的程序,.exe可执行文件),我们从哪里得到这个参数呢?答案是通过GetModuleHandle()GetModuleHandle()的参考文献中指明当传入一个NULL时,会返回“创建调用进程的文件的句柄”,这恰好是我们所需要的,刚才提到的那个HINSTANCE。把这些信息都放在一起我们可以得到以下声明:

 

1    HINSTANCE hInstance = GetModuleHandle(NULL);

 

  现在轮到二次个参数,再次转向我们可靠的参考手册,我们可以看到它是“一个指向接收指定的模块的路径和文件的缓冲区的指针”,数据类型是LPTSTR(或者LPSTR如果你的参考手册是老的)。因为LPSTRchar*是相同的我们可以像下面那样声明一个字符数组:

 

1    char szFileName[MAX_PATH];

 

  MAX_PATH是一个通过<windows.h>头文件中定义好的一个宏,这个宏用来定义存储一个win32下的文件名的缓冲区的最大长度,我们也把MAX_PATH传递给GetModuleFileName()这样它就会知道缓冲区的大小。

  在GetModuleFileName()被调用之后,szFileName变量的缓存将会被填充,填充的内容就是我们可执行文件的文件名,我们把这个值传递给MessageBox(),通过这个简单的方式把它显示给用户。

 

  如果你插入代码后不能正常地运行,这里是程序的完整的代码提供参考,跟它进行对比然后看看会你哪些地方犯了错误。

 

 1 #include <windows.h>
 2 
 3 const char g_szClassName[] = "myWindowClass";
 4 
 5 LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 6 {
 7     switch(msg)
 8     {
 9         case WM_LBUTTONDOWN:
10         {
11             char szFileName[MAX_PATH];
12             HINSTANCE hInstance = GetModuleHandle(NULL);
13 
14             GetModuleFileName(hInstance, szFileName, MAX_PATH);
15             MessageBox(hwnd, szFileName, "This program is:", MB_OK | MB_ICONINFORMATION);
16         }
17         break;
18         case WM_CLOSE:
19             DestroyWindow(hwnd);
20         break;
21         case WM_DESTROY:
22             PostQuitMessage(0);
23         break;
24         default:
25             return DefWindowProc(hwnd, msg, wParam, lParam);
26     }
27     return 0;
28 }
29 
30 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
31     LPSTR lpCmdLine, int nCmdShow)
32 {
33     WNDCLASSEX wc;
34     HWND hwnd;
35     MSG Msg;
36 
37     wc.cbSize        = sizeof(WNDCLASSEX);
38     wc.style         = 0;
39     wc.lpfnWndProc   = WndProc;
40     wc.cbClsExtra    = 0;
41     wc.cbWndExtra    = 0;
42     wc.hInstance     = hInstance;
43     wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
44     wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
45     wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
46     wc.lpszMenuName  = NULL;
47     wc.lpszClassName = g_szClassName;
48     wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
49 
50     if(!RegisterClassEx(&wc))
51     {
52         MessageBox(NULL, "Window Registration Failed!", "Error!",
53             MB_ICONEXCLAMATION | MB_OK);
54         return 0;
55     }
56 
57     hwnd = CreateWindowEx(
58         WS_EX_CLIENTEDGE,
59         g_szClassName,
60         "The title of my window",
61         WS_OVERLAPPEDWINDOW,
62         CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
63         NULL, NULL, hInstance, NULL);
64 
65     if(hwnd == NULL)
66     {
67         MessageBox(NULL, "Window Creation Failed!", "Error!",
68             MB_ICONEXCLAMATION | MB_OK);
69         return 0;
70     }
71 
72     ShowWindow(hwnd, nCmdShow);
73     UpdateWindow(hwnd);
74 
75     while(GetMessage(&Msg, NULL, 0, 0) > 0)
76     {
77         TranslateMessage(&Msg);
78         DispatchMessage(&Msg);
79     }
80     return Msg.wParam;
81 }

 

  PS.由于本人英文水平所限,只能翻译到这个程度了,有纰漏还望多多指出,附上本篇翻译的英文原版教程地址:http://www.winprog.org/tutorial/window_click.html

 

以上是关于WIN32 SDK API的基础问题(窗口显示)的主要内容,如果未能解决你的问题,请参考以下文章

c 语言用win32sdk方式如何创建一个没有标题栏和边框的窗口

Win消息机制,SDK编程基础

Win32编程API 基础篇 -- 2.一个简单的窗口 根据英文教程翻译

VC++ win32 API 编程:如何将图像从剪贴板中取出并显示在窗口中?

widnows 使用WIN32 APi 实现修改另一打开程序的窗口显示方式

WPF 通过Win32SDK修改窗口样式