对于大多数学生来说,Windows 依然是首选。用Mac的要么是土豪,早就习惯了,换不了。要么是土屌,苹果的东西就是好,一台的价钱顶别人两三台,配置还很低,显得尊贵。用Linux的呢,要么是大神,我在研究内核呢。要么是发烧友,我是装Ubuntu呢还是Fedora呢还是Debian呢?听说Unix也不错,要不试试FreeBSD吧。要么是跟风狗,Linux就是好,问他哪里好,却答不出个所以然,也不见他用得有多溜,美其名曰:学习中,我学一两年达到你们这些windows狗一辈子都无法瞻仰的境界。过几星期再看吧,又切回win了,原来linux上面没法LOL没法dota连QQ都不怎么好用.
所以,平凡的你用着熟悉的win学习程序设计。那么本次我们就整理一些比较有用的API吧。
“句柄” 是windows程序设计中一个很重要的概念,其类型为HANDLE,HANDLE通常为一个(void*)类型,用以标识所有类型句柄资源。你需要知道的是,它的形式是一个对你来说无意义的整数。
“窗口句柄”,顾名思义,是“句柄”这个类型里面的一种,类型通常为HWND,也是一个指针不过类型不再是void*了,而是定义窗口参数的结构体的指针,具体是啥这里不讨论(自行搜索),否则就偏题了。
涉及到的简单概念大致就是窗口句柄了。我们在显示器上看到的每个窗口,都必然有其窗口句柄。这个句柄的值可以使用微软提供的工具Spy++查看。使用Spy++获取这个句柄的值(前面说了,是一个整数)以后,就可以使用相关API进行修改了,比如隐藏窗口这个小小的功能,就是一个耳熟能详的功能——boss key. 要实现一个boss key,除了杂七杂八的界面实现,核心就一个函数,ShowWindow。比如 N 是你通过 Spy++获取到的某窗口的句柄值,你只需要写下: ShowWindow(N, SW_HIDE); 这样的代码,那个窗口就消失不见了。这种小儿科的东西这里也不再讨论,下面进入正题。
首先是如何获取窗口句柄。 如果你只知道编写windows下的控制台程序(就是小黑框那种),那么你必须明白,小黑框那也是窗口,所以必然是有其句柄的。可以通过一个隐藏函数GetConsoleWindow 的函数直接获取当前控制台窗口句柄。所谓“隐藏函数”就是说我们并不能直接include一下windows.h就能用了,还必须从kernel32模块里面加载。代码如下:
1 2 3 4 5 |
#include <windows.h> typedef HWND (WINAPI *PROCGETCONSOLEWINDOW)(); HMODULE hKernel32 = GetModuleHandle("kernel32"); PROCGETCONSOLEWINDOW GetConsoleWindow = (PROCGETCONSOLEWINDOW)GetProcAddress(hKernel32,"GetConsoleWindow"); //后面你就可以使用如 HWND hwnd = GetConsoleWindow(); 这样的代码来获取当前控制台窗口的句柄了。 |
如果你以前关注过这个问题,那么你可能知道还有个“万能”的蠢办法,先把窗口名改为一串特定的字符,然后通过FindWindow来获取。如果你觉得大丈夫,那你就用吧。如果程序本身就是窗口程序,相信不用我废话,你不会不知道的。
首先便是很多新手关注过的,控制台下的某个类似于 gotoxy 的功能。
我们知道,一般的控制台的输出,通常都是从左往右从上到下的顺序,就算用了”\b”之类的特殊符号,也最多回到行首,回不到上一行的。那万一想实现控制台游戏甚至控制台动画的话,就只好使用如 system(“cls”) 之类的蠢代码来强制刷新了。但这样做有个问题,就是屏幕闪烁很严重。聪明一点的人肯定不会采用此方法。整屏输出显然是更好的选择。至少解决了闪烁问题。
windows的控制台窗口默认大小为80 * 25个字符,你只要每次输出25行,那么用户每次都只能看到你最新的结果,你就可以做字符动画或者游戏了。
但是还有个问题,万一用户比较讨厌,拉了一下窗口呢?
这里就要说一下这个 gotoxy了, 这个是过时的Turbo-C所支持的函数,其函数原型为:
void gotoxy(int x, int y)
作用为:将输出的那个光标的位置定位到第y行第x列。 如果有这个函数,只要每一帧调用 gotoxy(0, 0),就可以继续从最前面开始重绘而不用担心闪烁或者用户拉了窗口输出错乱的问题了。
使用Windows API 实现如下
1 2 3 4 5 6 |
#include <windows.h> void gotoxy(int x, int y) { COORD coord = {x, y}; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); } |
用例: 我很久以前写过一个windows下的字符动画程序地址: https://code.google.com/p/wysaid/downloads/detail?name=bad%20apple.rar&can=1&q=
不过这个代码并没有附上源码,但是由于这个exe文件包含了debug信息,所以被某无聊网友逆向出来了,有兴趣的可以看看: http://tieba.baidu.com/p/1394998210
然后也是很多初学者关心过的问题,给控制台文字上色
很多人在网上搜索,如何给控制台文字上色,这里把这些整理一下。
上面是我很久以前帮高中室友做的课程设计。本来没有要我弄这些花样的,但是为了故作高深,硬是给这个控制台程序上了色。
换颜色靠的是windows.h提供的函数 SetConsoleTextAttribute
百科上已经有比较详细的解释,想了解的话请点击上面蓝色的字。 下面给出一个不怎么完整的例子以供参考(含代码):
https://code.google.com/p/wysaid/downloads/detail?name=%E6%B8%A3%E7%B3%BB%E7%BB%9F.rar&can=2&q=
最后是一系列对话框了。
MessageBox这种无聊透顶的玩意就不解释了。
不知道你有没有想过这样的场景:你做了一个小demo,需要读取一张图片,然后根据图片内容进行一系列处理,得到个什么东西。那么问题来了。如何得到这张图片。如果是一般的控制台程序,你大概会写一句 getline或者 gets(或fgets) 来让”用户”手动输入。但是”用户”很可能输错了一两个字符,如果你不想你的程序crash或者直接结束,你还必须进行容错,并让”用户”重新输入。这个”用户”很有可能是验收你的作业的老师。当然,你也可以给老师说:不要手动输入呀,用鼠标把文件直接拖到黑框框里面就得到路径了。
不过,更好的方法来了。类似下面的对话框,其实可以一句话得到的:
为了方便使用,我封装了一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
/* * utilityDlgs.h * * Created on: 2013-12-10 * Author: wysaid * Blog: blog.wysaid.org */ #ifndef _UTILITY_H_ #define _UTILITY_H_ #include <windows.h> enum UTL_FileFilter{ utlFILTER_ALL = 0, utlFILTER_IMAGE, utlFILTER_TEXT, utlFILTER_AUDIO, utlFILTER_VIDEO, utlFilter_ERROR }; bool getFolderPathDlg(HWND hwnd, LPSTR folderPath, LPCSTR title); bool saveFolderPathDlg(HWND hwnd, LPSTR folderPath, LPCSTR title); int readMultiFileNameDlg(HWND hwnd, char* filenames, const char* title, UTL_FileFilter filter); int readMultiFileNameDlg(HWND hwnd, char* filenames, const char* title, const char* filter = NULL); bool readFileNameDlg(HWND hwnd, LPSTR filename, LPCSTR title, UTL_FileFilter filter); bool readFileNameDlg(HWND hwnd, LPSTR filename, LPCSTR title, LPCSTR filter = NULL); bool saveFileNameDlg(HWND hwnd, LPSTR filename, LPCSTR title, UTL_FileFilter filter); bool saveFileNameDlg(HWND hwnd, LPSTR filename, LPCSTR title, LPCSTR filter = NULL); #endif |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
/* * utilityDlgs.cpp * * Created on: 2013-12-11 * Author: wysaid * Blog: blog.wysaid.org */ #include "utilityDlgs.h" #ifndef BUFFER_SIZE #define BUFFER_SIZE 1024 #endif #include <windows.h> #include <Commdlg.h> #include <ShlObj.h> #ifndef BIF_USENEWUI #define BIF_USENEWUI 0x0050 #endif #ifndef BIF_UAHINT #define BIF_UAHINT 0x0100 #endif const static char *s_defaultFilters[] = { "All Files(*.*)\0*.*\0\0", "Image Files(*.jpg;*.png;*.bmp;*.gif)\0*.jpg;*.jpeg;*.png;*.bmp;*.gif\0All Files(*.*)\0*.*\0\0", "Text Files(*.txt)\0*.txt\0All Files(*.*)\0*.*\0\0", "Audio Files(*.mp3;*.wav;*.wma;*.3ga)\0*.mp3;*.wav;*.wma;*.3ga\0All Files(*.*)\0*.*\0\0", "Video Files(*.mp4;*.avi;*.flv;*.rmvb)\0*.mp4;*.avi;*.flv;*.rmvb\0All Files(*.*)\0*.*\0\0" }; bool getFolderPathDlg(HWND hwnd, LPSTR folderPath, LPCSTR title) { BROWSEINFOA bfo; memset(&bfo, 0, sizeof(bfo)); bfo.hwndOwner = hwnd; bfo.lpszTitle = title; bfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_UAHINT; LPITEMIDLIST lpDlist = SHBrowseForFolderA(&bfo); if(lpDlist == NULL) return false; SHGetPathFromIDListA(lpDlist, folderPath); return true; } bool saveFolderPathDlg(HWND hwnd, LPSTR folderPath, LPCSTR title) { BROWSEINFOA bfo; memset(&bfo, 0, sizeof(bfo)); bfo.hwndOwner = hwnd; bfo.lpszTitle = title; bfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_UAHINT; LPITEMIDLIST lpDlist = SHBrowseForFolderA(&bfo); if(lpDlist == NULL) return false; SHGetPathFromIDListA(lpDlist, folderPath); return true; } int readMultiFileNameDlg(HWND hwnd, char* filenames, const char* title, UTL_FileFilter filter) { if(filter >= utlFilter_ERROR || filter < utlFILTER_ALL) return filter = utlFILTER_ALL; return readMultiFileNameDlg(hwnd, filenames, title, s_defaultFilters[filter]); } int readMultiFileNameDlg(HWND hwnd, char* filenames, const char* title, const char* filter) { *filenames = 0; OPENFILENAMEA ofn; memset(&ofn, 0, sizeof(ofn)); ofn.hwndOwner = hwnd; ofn.lStructSize = sizeof(ofn); ofn.Flags = OFN_EXPLORER | OFN_ALLOWMULTISELECT; ofn.lpstrFile = filenames; ofn.nMaxFile = BUFFER_SIZE; if(filter == NULL) ofn.lpstrFilter = s_defaultFilters[utlFILTER_ALL]; else ofn.lpstrFilter = filter; ofn.lpstrTitle = title; if(!GetOpenFileNameA(&ofn)) return -1; return ofn.nFileOffset; } bool readFileNameDlg(HWND hwnd, LPSTR filename, LPCSTR title, UTL_FileFilter filter) { if(filter >= utlFilter_ERROR || filter < utlFILTER_ALL) filter = utlFILTER_ALL; return readFileNameDlg(hwnd, filename, title, s_defaultFilters[filter]); } bool readFileNameDlg(HWND hwnd, LPSTR filename, LPCSTR title, LPCSTR filter) { OPENFILENAMEA ofna; *filename = 0; memset(&ofna, 0, sizeof(OPENFILENAMEA)); ofna.lStructSize = sizeof(OPENFILENAMEA); ofna.hwndOwner = hwnd; ofna.hInstance = NULL; if(filter == NULL) ofna.lpstrFilter = s_defaultFilters[utlFILTER_ALL]; else ofna.lpstrFilter = filter; ofna.nMaxFile = MAX_PATH; ofna.lpstrDefExt = "txt"; ofna.lpstrFile = filename; ofna.lpstrTitle = title; ofna.Flags = OFN_HIDEREADONLY | OFN_CREATEPROMPT; return !!GetOpenFileNameA(&ofna); } bool saveFileNameDlg(HWND hwnd, LPSTR filename, LPCSTR title, UTL_FileFilter filter) { if(filter >= utlFilter_ERROR || filter < utlFILTER_ALL) filter = utlFILTER_ALL; return saveFileNameDlg(hwnd, filename, title, s_defaultFilters[filter]); } bool saveFileNameDlg(HWND hwnd, LPSTR filename, LPCSTR title, LPCSTR filter) { OPENFILENAMEA ofna; *filename = 0; memset(&ofna, 0, sizeof(OPENFILENAMEA)); ofna.lStructSize = sizeof(OPENFILENAMEA); ofna.hwndOwner = hwnd; ofna.hInstance = NULL; if(filter == NULL) ofna.lpstrFilter = s_defaultFilters[utlFILTER_ALL]; else ofna.lpstrFilter = filter; ofna.nMaxFile = MAX_PATH; ofna.lpstrFile = filename; ofna.lpstrTitle = title; ofna.Flags = OFN_OVERWRITEPROMPT; return !!GetSaveFileNameA(&ofna); } |
上面的代码,只要复制下来,分别保存为utilityDlgs.h和utilityDlgs.cpp 就可以直接加到你的小程序里面用了。
讲一下如何使用吧。参数类型为HWND的就是最前面提到的你的窗口句柄,虽然也可以直接写为NULL,但我认为你应当写上。第二个参数为文件名,需要传递一个已经分配好的地址。用户选择的文件名将被写入。第三个参数为title,顾名思义,指定对话框的窗口。有几个函数有第四个参数,是指定文件类型的。代码中有相关写法,你可以自己参考。如果懒得看,那么写为NULL就行了
其中有函数名为 readMultiFileNameDlg, 它的意思是一次读取多个文件,我想一般情况下是用不上的。它获取到的文件名跟其他几个稍微不同,最前面是文件的路径,后面依次跟了N个文件名,表示该目录下的多个文件。这里不好描述其排列,如果要使用,请直接输出一下得到的filenames或者调试状态查看一下,相信你很快就能明白。
***未完待续***