對Windows 2000, Windows XP, Windows Server 2003操作系統下的“命令提示符程序 (cmd.exe)”了解稍多的人都會知道“命令提示符”有幾個很好用的功能。一、支持命令歷史記錄,可以用上下鍵來切換以前輸入的命令;二、支持快捷鍵功能(F1~F9)[具體每個鍵的功能請讀者自己試吧];三、支持目錄/文件名的自動補齊,這樣可以快速輸入目錄/文件名。
這些功能對於用戶來講是非常友好的,可以讓用戶更方便的輸入和編輯命令。我也相信任何一個寫控制台程序的人都希望在自己的程序當中能夠實現這樣的功能。
那麼如何讓自己的程序有如上的功能呢?
對於上面提到前兩個功能,操作系統本身已經提供了,你只需要簡單的調用ReadConsole這個API就可以了。這個API會跟據你輸入時的按鍵來執行這些功能。
對於第三個功能,MSDN中未曾提及,ReadConsole API的說明中任何一點都和此功能無關。實際上自動補齊功能要用到ReadConsole的一個公開的功能,而且只有Unicode版本的ReadConsoleW提供了該功能,ANSI版本的ReadConsoleA並不支持。
我們先看看ReadConsole這個API的原型(from MSDN)
BOOL ReadConsole(
HANDLE hConsoleInput, // handle to console input buffer
LPVOID lpBuffer, // data buffer
DWord nNumberOfCharsToRead, // number of characters to read
LPDWord lpNumberOfCharsRead, // number of characters read
LPVOID lpReserved // reserved
);
在MSDN中提到參數lpReserved這個參數必須為NULL值,當然MSDN中是這麼提的,但對於ReadConsoleW來講就不是了,因為自動補齊這個功能要靠ReadConsoleW的lpReserved參數了。
該參數不為NULL時,可以指向一個如下的結構體
struct read_console_param
{
DWord cbSize;
DWord dwInitLen;
DWord dwWakeMask;
DWord dwUnknown;
};
其中每個成員變量的意義如下
cbSize - 該結構體的長度,16字節
dwInitLen – 指出lpBuffer中已有字符的數目,這樣ReadConsole在等待用戶輸入時,也會把lpBuffer中已有的內容算進去。
dwWakeMask – 指出ReadConsole在接收到哪些Ctrl序列後返回,其中bit0對應^@, bit1對應^A, bit2對應^B,bit3對應^C,以此類推。當用戶輸入時,輸入了dwWakeMask中指定的任何一個Ctrl按鍵,ReadConsole將返回。
dwUnknown具體什麼意義暫不清楚,最初設為0即可
了解了ReadConsoleW的這個新功能後(其實該功能早就有了,只不過很多人不知道),那麼就可以很容易地實現自動補齊了。
以下是我給的一個簡單例子:
#include
#include
#include
#include
#include
#define Ctrl(x) ((x) & 0x37)
struct read_console_param
{
DWord nLength;
DWord dwInitLen;
DWord dwWakeMask;
DWord dwUnknown;
};
int main (void)
{
HANDLE hInput, hOutput;
WCHAR buf [0x100];
read_console_param param;
setlocale (LC_ALL, ".ACP");
memset (¶m, 0, sizeof (param));
param.nLength = sizeof (param);
hInput = GetStdHandle (STD_INPUT_HANDLE);
hOutput = GetStdHandle (STD_OUTPUT_HANDLE);
buf [0] = 0;
while (wcscmp (buf, L"quit") != 0)
{
DWord read, written;
printf (" $");
param.dwInitLen = 0;
param.dwUnknown = 0;
// 我們使用^F和^D來進行自動補齊
param.dwWakeMask = (1 << Ctrl ('F'))
| (1 << Ctrl ('D'));
again:
if (ReadConsoleW (hInput, buf, 0x100, &read, ¶m))
{
if (buf [read-1] == Ctrl ('F'))
{ // 用戶按下了^F鍵, 自動補齊字符串"fff",
// 然後繼續等待輸入
wcscpy (buf+read-1, L"fff");
WriteConsoleW (hOutput, L"fff", 3,
&written, NULL);
param.dwInitLen = read - 1 + 3;
goto again;
}
else if (buf [read-1] == Ctrl ('D'))
{ // 用戶按下了^D鍵, 自動補齊字符串"ddd",
// 然後繼續等待輸入
wcscpy (buf+read-1, L"ddd");
WriteConsoleW (hOutput, L"ddd", 3,
&written, NULL);
param.dwInitLen = read-1+3;
goto again;
};
// 去掉回車換行
if (buf [read-1] == ' ')
--read;
if (buf [read-1] == ' ')
--read;
buf [read] = 0;
printf ("you inputed: [%S] ", buf);
}
else
{
printf ("ReadConsole failed with error %d ",
GetLastError ());
break;
};
};
};
這個例子是個最簡單的例子,目前只支持在輸入字符串的末尾進行自動補齊,無法在輸入字符串的中間進行自動補齊;如果要寫出cmd.exe那樣的效果,還需要加很多的代碼。
最後祝所有的程序員都能在Windows下寫出支持自動補齊的控制台程序!