怎么玩转windows (如何玩转windows10)

怎么玩转windows,windows窗口移动的操作

写在前面

上一篇写的是关于控制台屏幕缓冲区滚动机制的剖析,费了我老鼻子劲。平时觉得非常简单的知识点,等落笔成文时,才发现非常不容易。找遍全文,都是挂一漏万,没有系统讲解的。微软的MSDN。。。呵呵,大家懂的都懂。新手看不懂,老手不需要看,就是个工具书。

最后虽然写成了,但是非常不满意,本来这个windows控制台的主题,本意是想写的好玩、轻松、有趣。因为主要是面向c和c++的初学者,能让平时枯燥无比的控制台,也能玩出很多花样,没想到写到第六期,竟然越来越复杂,越来越面目可憎。

今天一定要重新开始,写点简单又有意思的小技巧,这样学起来也不累。

移动窗口

前面也陆陆续续的介绍了窗口操作的一些技巧,但是最重要的却遗漏了。就是每次控制台程序启动时,窗口在屏幕上的位置好像都出现过,但是就是没有居中过。特别是对一些强迫症,或者完美主义者来说,是无法接收的。。。

让窗口居中有很多办法。我们先来看看如何移动窗口的。下面是一段把控制台从屏幕上一个位置移动到另一个位置的代码:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
int main(){
    HWND hwnd = GetConsoleWindow();
    RECT rect;
    GetWindowRect(hwnd,&rect);
    int newX,newY,width,height;
    width = rect.right - rect.left + 1;
    height = rect.bottom - rect.top + 1;
    newX = 300,newY = 300;
    getchar();
    MoveWindow(hwnd,newX,newY,width,height,FALSE);
    getchar();
    return  0;
}

下面是程序运行后效果截图:

怎么玩转windows,windows窗口移动的操作

图1

然后再点击回车键,窗口被改变位置:

怎么玩转windows,windows窗口移动的操作

图2

程序比较简单,用到了这样几个函数:

HWND GetConsoleWindow(void);

见名知意,得到当前控制台程序的窗口句柄,返回值是控制台所使用的窗口句柄。

HWND,是Handle Window的缩写,H是handle,句柄的缩写,句柄的概念我们已经介绍多次,初学者就理解为类似指针,用以唯一表示一个对象的标识符即可。WND是window的缩写,表示“窗口对象”,windows程序,就是由一个个“窗口”组成的程序。

实际上HWND就是HANDLE,用HWND表示,就是强调这是一个窗口对象句柄。看看定义便可以知道:

typedef HANDLE HWND;
BOOL GetWindowRect(HWND hWnd,LPRECT lpRect);

见名知意,得到窗口对象的矩形值,返回值为TRUE,表示获取成功。HWND是一个输入参数,绑定一个窗口对象。

第二个参数是一个输出型参数,LPRECT类型中的LP,表示指针,RECT表示一个结构体类型,组合起来LPRECT就是一个指向RECT结构体类型的指针。

RECT结构体和之前文章中讲解的SMALL_RECT类型类似,区别在于成员变量是LONG类型。RECT结构体定义如下:

typedef struct tagRECT {
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT;

通过这个函数可以获取当前控制台窗口的左上角和右下角坐标,这个坐标不是之前文章中说的坐标是相对于屏幕缓冲区的相对位置的坐标,此处是相对于显示器屏幕左上角(0,0)的相对位置的坐标(假定不是子窗口),这样就可以计算出当前窗口的矩形面积的宽width和高height,移动窗体时需要用到。

移动窗体windows提供了一个MoveWindow函数,函数原型如下:

BOOL MoveWindow(HWND hWnd, int X,int Y, int nWidth, int nHeight, BOOL bRepaint);

返回值为TRUE则执行成功,传入一个窗体句柄(HWND类型,尽量不要传入HANDEL类型),X和Y表示要移动到的新位置的左上角的坐标。(之前已经说过X是横坐标,Y是纵坐标,计算机系统以左上角为坐标原点)。

如果在移动窗体的过程中,保持窗体大小不变,那么就要用GetWindowRect函数获取窗体的顶点坐标,并计算出宽和高,然后传入到对应的参数。如果需要改变窗体的大小,可以直接填入新的宽和高。

最后一个参数一般都默认为FALSE,因为还没学到窗体重绘,暂时不展开讲解。

总结一下,首先通过GetConsoleWindow()函数获得当前控制台窗口的HWND窗体句柄值, 通过GetWindowRect函数,获得控制台窗口在屏幕上所占据的矩形面积的宽和高,然后通过MoveWindow函数移动窗体到新的位置。

下面的例子演示了窗体在移动过程中同事调整尺寸的效果:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
int main(){
    HWND hwnd = GetConsoleWindow();
    RECT rect;
    int newX,newY,width,height;
    newX = 300,newY = 300;
    width = 500;
    height = 350;
    MoveWindow(hwnd,newX,newY,width,height,FALSE);
    getchar();
    return  0;
}

程序运行的效果如图:

怎么玩转windows,windows窗口移动的操作

图3

控制台程序运行时,自动调整好窗口的尺寸和位置。

窗口居中

有了前面移动窗口的基础知识,再让窗口居中就非常简单了。

基本思路:窗口的宽度和高度的中心点,一定也是屏幕的宽度和高度的中心点。所以只需要知道屏幕的宽度和高度,然后求出他们的1/2的值,其实就是窗口的宽高的中心点。

实现代码如下:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
int main(){
    HWND hwnd = GetConsoleWindow();
    RECT rect;
    GetWindowRect(hwnd,&rect);
    int newX,newY,width,height;
    width = rect.right - rect.left + 1;
    height = rect.bottom - rect.top + 1;
    int screen_width = GetSystemMetrics(SM_CXSCREEN);
    int screen_height = GetSystemMetrics(SM_CYSCREEN);
    newX = (screen_width - width)/2;
    newY = (screen_height - height) /2;
    MoveWindow(hwnd,newX,newY,width,height,FALSE);
    getchar();
    return 0;
}

程序运行的效果如下:

怎么玩转windows,windows窗口移动的操作

图4

这个程序中,用到了一个很重要的GetSystemMetrics函数。Metrics,指标、配置,System Metrics,即系统相关的指标,配置等参数。函数原型如下:

int GetSystemMetrics( [in] int nIndex);

传入的参数代表不同功能的枚举常量,大部分枚举常量都是与某个图形对象的宽度、高度、粗细有关。下面列举了部分枚举常量,全部的详细信息从MSDN都可以查阅到。

SM_CMONITORS 80
桌面上的显示器数量。

SM_CMOUSEBUTTONS43
鼠标上的按钮数,如果未安装鼠标,则为零。

SM_CXFULLSCREEN16
主显示器上全屏窗口的工作区宽度(以像素为单位)。

SM_CXSCREEN 0
主显示器的屏幕宽度(以像素为单位)。

SM_CYFULLSCREEN17
主显示器上全屏窗口的工作区高度(以像素为单位)。

SM_CYSCREEN1
主显示器的屏幕高度(以像素为单位)。

SM_SWAPBUTTON23
如果鼠标左键和右键的含义互换,则为非零;否则为 0。

下面这个程序是对这个函数的部分枚举常量编写的演示代码:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
int main(){
    int  cnt_monitor = GetSystemMetrics(SM_CMONITORS);
    printf("显示器有 %d 台。\n",cnt_monitor);
    int cnt_mouse = GetSystemMetrics(SM_CMOUSEBUTTONS);
    printf("鼠标有 %d 个按钮。\n",cnt_mouse);
    int height_screen_contain_taskbar = GetSystemMetrics(SM_CYSCREEN);
    printf("包含任务栏的屏幕高度:%d\n",height_screen_contain_taskbar);
    int height_screen_above_taskbar = GetSystemMetrics(SM_CYFULLSCREEN);
    printf("任务栏上面屏幕高度:%d\n",height_screen_above_taskbar);
    int width_full_screen = GetSystemMetrics(SM_CXFULLSCREEN);
    printf("屏幕宽度full mode:%d\n",width_full_screen);
    int width_screen = GetSystemMetrics(SM_CXSCREEN);
    printf("屏幕宽度:%d\n",width_screen);
    int swap_mouse_btn = GetSystemMetrics(SM_SWAPBUTTON);
    if(swap_mouse_btn)
        printf("鼠标按钮功能被互换。\n");
    else{
        printf("鼠标按钮功能正常。\n");
    }
    getchar();
    return 0;
}

程序运行截图如下:

怎么玩转windows,windows窗口移动的操作

图5

注意,运行结果中说有2台显示期,不是指打开的2台显示器,而是必须如下图所示,设置为“扩展”模式的显示器。如果选择“复制“模式,则多台显示器也计为1台。

怎么玩转windows,windows窗口移动的操作

图6

之所以有8个鼠标按钮,是因为无线鼠标、触控板、ThinkPad上的红点加起来一共有8个按钮。

SM_CYSCREEN,表示显示器整个屏幕的垂直高度,CYFULLSCREEN,表示屏幕中未遮挡区域的垂直高度,如果底部有任务栏,那么任务栏就属于遮挡屏幕的区域,它的高度要被扣除,在我的屏幕上,底部任务栏高度 = 960 - 897 (像素)。

SM_CXFULLSCREEN 和 SM_CXSCREEN ,都是获取屏幕的宽度,区别在于,当任务栏在左侧或右侧显示时,SM_CXFULLSCREEN的屏幕宽度,是不包含任务栏区域的屏幕宽度,而SM_CXSCREEN是整个显示器屏幕区域的大小。如果任务栏在屏幕底部,那么这两个的值是相同的,在我的显示器上都是1707像素。

通过GetSystemMetrics函数获取到了屏幕的高度和宽度后,需要计算出当前矩形窗口居中时,左上角的坐标值,然后通过MoveWindow函数,移动到对应的区域,即可实现“窗体居中”的效果。

窗口隐藏和显示

有时候,我们需要控制台程序保持运行,但又不显示在屏幕上,就需要用到“窗口隐藏”。通过ShowWindow函数,就可以灵活的控制窗口的隐藏和各种显示方式。函数原型如下:

BOOL ShowWindow(HWND hWnd,int nCmdShow);

第二个参数通过各种枚举常量来设置窗口的显示状态。部分功能的枚举常量如下:

1.使窗口处于激活状态的显示方式:

SW_HIDE  0
隐藏窗口。

SW_NORMAL 1 
激活并显示窗口。如果窗口被最小化、最大化或排列,系统会将其恢复到其原始大小和位置。

SW_SHOWMINIMIZED  2
 激活窗口并将其显示为最小化窗口。

SW_SHOWMAXIMIZED
 SW_MAXIMIZE  3 
激活窗口并将其显示为最大化窗口。

SW_SHOW 5 
激活窗口并按其当前大小和位置显示窗口。

SW_RESTORE 9 
激活并显示窗口。如果窗口被最小化、最大化或排列,系统会将其恢复到其原始大小和位置。

2.显示但不激活窗口的方式:

SW_SHOWNOACTIVATE  4 
以最近的大小和位置显示窗口。此值与 SW_SHOWNORMAL 值类似,只是窗口未激活。

SW_MINIMIZE  6 
最小化指定的窗口,并按 Z 顺序激活下一个顶级窗口。

SW_SHOWMINNOACTIVE  7 
将窗口显示为最小化窗口。此值与 SW_SHOWMINIMIZED 类似,只是窗口未激活。

SW_SHOWNA 8
以窗口的当前大小和位置显示窗口。此值与 SW_SHOW 值类似,只是窗口未激活。

下面是部分显示方式的示例代码:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
int main(){
    HWND hwnd = GetConsoleWindow();
    RECT rect;
    GetWindowRect(hwnd,&rect);
    int newX,newY,width,height;
    width = rect.right - rect.left + 1;
    height = rect.bottom - rect.top + 1;
    int screen_width = GetSystemMetrics(SM_CXSCREEN);
    int screen_height = GetSystemMetrics(SM_CYSCREEN);
    newX = (screen_width - width)/2;
    newY = (screen_height - height) /2;
    MoveWindow(hwnd,newX,newY,width,height,FALSE);
    Sleep(500);
    ShowWindow(hwnd,SW_HIDE);
    Sleep(500);
    ShowWindow(hwnd,SW_NORMAL);
    Sleep(500);
    ShowWindow(hwnd,SW_SHOWMINIMIZED);
    Sleep(500);
    ShowWindow(hwnd,SW_MAXIMIZE);
    Sleep(500);
    ShowWindow(hwnd,SW_MINIMIZE);
    Sleep(500);
    ShowWindow(hwnd,SW_SHOWNOACTIVATE);
    Sleep(500);
    return 0;
}

因程序界面动态变化,就不截图了。

下面这段代码,演示了控制台程序窗口闪烁的特效:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
int main(){
    HWND hwnd = GetConsoleWindow();
    RECT rect;
    GetWindowRect(hwnd,&rect);
    int newX,newY,width,height;
    width = rect.right - rect.left + 1;
    height = rect.bottom - rect.top + 1;
    int screen_width = GetSystemMetrics(SM_CXSCREEN);
    int screen_height = GetSystemMetrics(SM_CYSCREEN);
    newX = (screen_width - width)/2;
    newY = (screen_height - height) /2;
    MoveWindow(hwnd,newX,newY,width,height,FALSE);
    int flag = 1;
    while(1){
        if(flag){
          ShowWindow(hwnd,SW_HIDE);
          flag = 0;
        }else{
          ShowWindow(hwnd,SW_SHOW);
          flag = 1;
        }
        Sleep(200);
    }
    return 0;
}

窗口闪烁效果如下:

视频加载中...

Sleep函数,是让当前程序执行到此处,暂停规定的毫秒数之后再继续执行下一条指令。参数就是一个具体的数值,代表暂停的毫秒数。建议编译前,一定要把Sleep的毫秒数调大,不然闪烁太快,不容易关闭程序[憨笑][呲牙]

总 结

没有什么可总结的,今天的几个用法,非常简单,实验一下即可,如果感兴趣,也可以在MSDN上看更扩展的关联函数功能。

段誉,2024年2月17日,写于合肥。