1. 程式人生 > >控制檯與wchar_t

控制檯與wchar_t

       Q:我見到過一些即可以作為控制檯應用程式執行,也可以作為基於Windows的應用程式執行的程式。即:如果你在命令提示符鍵入程式的名字,它就會作為一個普通的基於 Windows 的應用程式執行;如果你輸入一個命令列選項,如:“-batch”,它就會以批處理模式作為一個控制檯應用程式執行,同時所有輸出被定向到控制檯。請問這個功能是怎麼做的? Joe Tadmann       A: 我沒法告訴你其它的應用程式是怎麼這樣做的,因為在 Window 中,總是有多種方法來給作業系統加殼,但是我可以向你展示實現這種功能的一種途徑---通過 Visual Studio.NET 來實現。也許你沒有注意到,你可以在命令提示符鍵入 devenv 來啟動 Visual Studio .NET,這時,Windows 啟動 其圖形使用者介面。但是如果你鍵入 devenv 的同時再加上一個命令列開關,例如,-? 表示幫助,-bulid 表示編譯並生成你的工程,它將以控制檯模式執行,沒有使用者互動介面。例如:
devenv -build MyProject.sln
上面這行命令生成解決方案檔案 MyProject.sln。

  遺憾的是,自從GUI(圖形使用者介面)出現以後,太多的程式設計師已經忘了命令列的強大功能,這些強大的命令列功能使使用者能以批處理模式從指令碼中執行你的應用程式。你可以 確信微軟的那幫傢伙是不會在 Visual Studio 中開啟某個工程來生成他們的產品的!如果你寫了一個確實能實現某些功能的程式,比如,把.wav檔案轉換成.mp3,或者通過計算預測股票, 這些功能適合用批處理介面來實現。如果你的程式沒有人守著就不能成批壓縮收藏的音樂樂曲或分析前一天的股票資訊,那你的程式有什麼好的呢?
  好,現在言歸正傳,你怎麼能夠實現一個組合了使用者介面/控制檯的應用呢?現在幾乎所有的 Windows 程式設計師都知道,Windows 把 EXE(可執行程式)分成兩 大類:控制檯應用和 GUI應用。這種體系結構可以追溯到 Windows 的早期,當時它首先是從 MS_DOS 中發展而來的。如今,你只要給連結 器一個開關:/subsystem:Windows 或者 /subsystem:console,你就能生成你想要的那種應用程式。
  所以,如果你要建立一組合應用,首要問題是:它是一個控制檯應用還是一個GUI應用?最初,你可能認為要建立一個控制檯應用,以後它還能夠作為一個 GUI 程式執行。從來沒有誰說過一個控制檯應用就不能夠建立視窗或處理訊息,控制檯之所以是控制檯,那是因為當控制檯不存在時, Windows 會為控制檯應用建立一個控制檯。但是這裡有一個問題,如果你在 Windows 下通過在其資源管理器(Explorer)中雙擊或快捷方式執行某個控制檯應用程式,Windows 將會建立一個控制檯,你可以通過呼叫 FreeConsole 來銷燬 這個控制檯,但是這個控制檯視窗會暫時一閃而過,告訴整個世界你其實並不知道你自己做了什麼。
  然後,要使它為一個GUI應用。那麼你如何把它寫到控制檯呢?有許多文章解釋了怎樣重新路由 printf 或 cout 到控制檯,但是, 它們都涉及到建立新控制檯視窗的問題,不是使用一個當程式是從命令列被啟動時已經存在的視窗。即使有某個使用現有控制檯的途徑,那你又怎麼知道你的應用程式是通過 Windows Explorer 還是通過命令提示符呼叫的呢?
  有一個新的函式( Windows XP 使用的)正好可以利用:這個函式是 AttachConsole。 它允許你將程式“繫結”到其它程序的控制檯視窗,如果你用了專用的程序 ID:ATTACH_PARENT_CONSOLE,AttachConsole 將繫結到啟動你的程式的控制檯。太好了,但是有兩個問題,第一,AttachConsole 只能在 Windows XP 系統中使用,所以如果你想要你的程式執行在其它版本的 Windows 中,就沒那麼走運了;第二,AttachConsole 工作並不穩定,你可以寫內容到控制檯,但是你的程式退出後,命令提示符就亂七八糟了。
  簡而言之,基於 Windows 的應用程式要麼是控制檯應用,要麼是GUI應用,二者不可兼得。(除非你想寫你自己的啟動程式碼,那已非我力所能及)。但是你知道它 是能夠做到的,因為我已經告訴你 Visual Studio 能行,到底怎樣做呢?
  如果你看一下 Visual Studio 的安裝目錄,你會發現實際上有兩個程式:devenv.exe和 devenv.com。還記得.com 是什麼嗎? 它可不是 Web,而是可執行程式,在很久很久以前,當你還是小孩子的時候,基於 Windows 的程式有三種記憶體模式:大記憶體模式(large)、小記憶體 模式(small)和巨大記憶體模式(huge)。其它的模式都被叫做緊湊模式或微小模式,它們產生不同型別的可執行檔案,這些可執行檔案都以 .com 作為副檔名。(.com檔案是一種在載入時不需要固定地址的直接記憶體映 象,這樣使用起來非常的快,但它們必須很小。) 現在記憶體模式已經沒有這麼多了,大部分可執行檔案都使用PE格式。但是命令直譯器仍然能識別 .com 可執行檔案,並且你可以將任何.exe 程式重新 更名為 .com 程式,如把 foo.exe 改成 foo.com,它仍然可以通過輸入名字執行。所以可以用這個技巧去建立兩個程式:foo.com 和 foo.exe。一個是 控制檯應用程式,另一個是基於 Windows 應用程式。

Figure 2 在對話方塊裡顯示程序列表

Figure 2 在對話方塊裡顯示程序列表

  為了示範它的工作原理,我修改了我在2002年7月專欄文章裡的程式lp(列舉程序)(編者注:中文譯文參見線上雜誌第14期文章——“如何獲取某個程序的主視窗以及建立程序的程式名?”),它既可作為 GUI 程式執行,也可作為控制檯程式執行。如果你輸入 ListProc 而不用引數,它會在對話方塊中列出程序,如 Figure 2 所示。如果你鍵入 ListProc -c,它會以控制檯模式執行並列出程序,如 Figure 3 所示。ListProc 有兩個主程式檔案:ListProc.cpp 是通常的 MFC 應用實現,ListProc-cons.cpp 是控制檯應用實現。這兩個程式都呼叫相同的模組—— EnumProc,實際的程序列表正是由它產生的。ListProc-cons 處理命令列並顯示控制檯資訊,沒有用命令列引數的程式通過呼叫 ShellExecute 啟動程式的 GUI 版本。
// 將 myself.com 改為 myself.exe 並執行
TCHAR lpExeName[_MAX_FNAME];
GetModuleFileName(NULL, lpExeName, _MAX_FNAME);
LPTSTR ext = lpExeName + _tcslen(lpExeName) - 3;
_tcscpy(ext,_T("exe"));
ShellExecute(NULL, _T("open"), 
  lpExeName, NULL, NULL, SW_SHOWNORMAL);

Figure 3 在在控制檯應用中列出程序表
Figure 3 在控制檯的程序列表

Figure 4 是 ListProc-cons 的全部程式碼,Visual Studio 的解決方案包含兩個工程:ListProc和 ListProc-cons。後者有一個定製編譯步驟,是重新命名輸出檔案 ListProc-cons.exe 為ListProc.com (參見 Figure 5)。當你安裝程式時,要保證把 .com 和 .exe 都放在了相同的目錄下,並且確保你建立的任何快捷方式都指向 .exe 檔案。那樣,從 Windows 呼叫會直接執行.exe 檔案,而從控制檯呼叫則執行 .com 檔案(如果 .com 和 .exe 都存在於使用者的路徑下, Windows 首選 .com 檔案執行)。明白了嗎?

Figure 5 將 .exe 改為 .com 檔案


Figure 5 將 .exe 檔案重新命名為 .com
 

c++控制檯應用程式 懸賞分:50 - 解決時間:2009-9-19 12:38
設計一個控制檯應用程式XXXPersion,其中包含一個描述人的抽象類PersionClass,利用繼承的方式派生學生類StudentClass,教師類TeacherClass和歌手類SingerClass等,在PersionClass類中要有必要的資料如:姓名、性別、年齡等,在派生類StudentClass中要有所在學校等與學生有關的資料的描述;在TeacherClass類中要有職稱等與教師有關的相關資料的描述,在SingerClass中要有住址、身高等與歌手有關的資料的描述。總的要求:要有必要的獲得資料的手段,如獲得學生、教師或歌手的姓名、性別、年齡,學生所在的學校、教師的職稱、歌手的住址等,儘量將題目設計完整,功能完善,同時設計測試類對所設計的類及包含的功能進行測試。
評分標準:
1. 正確合理設計PersionClass類             10分
2. 正確使用繼承設計StudentClass類                10分
3. 正確使用繼承設計TeacherClass類                10分
4. 正確使用繼承設計SingerClass類               10分
5. 能夠正確設計測試類並進行合理測試           10分
6. 程式整體效果及執行                         10分 
問題補充:
學校的一次模擬,大家幫幫忙,對了,後面的題目的分數!!
最佳答案
#include <stdio.h>
#include <iostream>

using namespace std;

class XXXPerson{
public:
 char name[20]; //姓名
 char sex[2]; //性別
 int age; //年齡
public:
 XXXPerson(){
  memset(name, 0, 20);
  memset(sex, 0,2);
  age = 0;
 }
 XXXPerson(char n[], char s[], int i){  
  strcpy(name, n);  
  strcpy(sex, s);
  age = i;
 }
 virtual void displayInfo(){
  cout << "Person--> name: "<<name << "/t sex: " << sex << "/t age: " << age <<"." << endl;
 }
};


class StudentClass:public XXXPerson{
public:
 char school[50]; //所在學校
 char otherinfo[200]; //其它資訊
public: 
 StudentClass(char n[], char s[], int age, char sch[], char oi[]){
  strcpy(name, n);  
  strcpy(sex, s);
  this->age = age;
  strcpy(school, sch);
  strcpy(otherinfo, oi);
 }
 virtual void displayInfo(){
  cout << "學生 "<<name << "  的資訊:" << endl;
  cout<< "/t性別:/t/t" << sex << endl;
  cout << "/t年齡:/t/t" << age << endl;
  cout << "/t所在學校:/t" << school << endl;
  cout << "/t附加資訊:/t" << otherinfo << endl;
 }
};


class TeacherClass:public XXXPerson{
public:
 char tlevel[50]; //職稱
 char otherinfo[200]; //其它資訊
public: 
 TeacherClass(char n[], char s[], int age, char sch[], char oi[]){
  strcpy(name, n);  
  strcpy(sex, s);
  this->age = age;
  strcpy(tlevel, sch);
  strcpy(otherinfo, oi);
 } 
 virtual void displayInfo(){
  cout << "老師 "<<name << "  的資訊:" << endl;
  cout<< "/t性別:/t/t" << sex << endl;
  cout << "/t年齡:/t/t" << age << endl;
  cout << "/t職稱:/t/t" << tlevel << endl;
  cout << "/t附加資訊:/t" << otherinfo << endl;
 }
};

class SingerClass:public XXXPerson{
public:
 int stature; //身高
 char address[200]; //住址
 char otherinfo[200]; //其它資訊
public: 
 SingerClass(char n[], char s[], int age,int t, char sch[], char oi[]){
  stature = t;
  strcpy(name, n);  
  strcpy(sex, s);
  this->age = age;
  strcpy(address, sch);
  strcpy(otherinfo, oi);
 } 
 virtual void displayInfo(){
  cout << "歌手 "<<name << "  的資訊:" << endl;
  cout<< "/t性別:/t/t" << sex << endl;
  cout << "/t年齡:/t/t" << age << endl;
  cout << "/t身高:/t/t" << stature << endl;
  cout << "/t住址:/t/t" << address << endl;
  cout << "/t附加資訊:/t" << otherinfo << endl;
 }
};

int main(void){
 StudentClass p1("小明", "男", 19, "XX大學", "學生測試。");
 SingerClass p2("劉德華", "男", 40, 180,"保密不公開。", "歌手測試。");
 TeacherClass p3("李老師", "女", 39, "高階教師", "老師測試");
 
 XXXPerson *p[3] = {&p1, &p2, &p3};
 for(int i=0; i<3; i++)
  p[i]->displayInfo();
 
}