1. 程式人生 > >不怕萬人阻擋,只怕自己投降

不怕萬人阻擋,只怕自己投降

9.1  上機實驗目的

l 掌握命令解釋程式的設計方法。

學習Windows系統呼叫的使用,瞭解目錄操作、程序控制等相關知識。

培養C/C++語言程式設計技能,提高程式設計和文件編寫能力。

l 鍛鍊團隊成員的交流與合作能力。

9.2  上機實驗要求

本實驗要求實現一個簡單的命令解釋程式,其設計類似於MS-DOSCommand程式。具體要求如下:

(1) 參考Command命令解釋程式,採用控制檯命令列輸入方式,命令列提示符是當前目錄名與提示符“>”,在提示符後輸入命令。命令執行結束後,在控制檯繼續顯示提示符,等待輸入新的命令。

(2) 實現以下內部命令:

l cd <路徑名>  切換當前目錄。

l dir [<路徑名>]  顯示指定目錄下的檔案、子目錄及磁碟空間等相關資訊。

l tasklist  顯示系統當前程序資訊,包括程序識別符號pid、該程序包含的執行緒數、程序名等。

l taskkill <pid>  結束系統中正在執行的程序,須指定程序識別符號pid

l history  顯示控制檯中曾經輸入過的命令。

l help  顯示本程式使用的幫助資訊。

l exit  退出控制檯,結束本命令解釋程式。

(3) 對前臺程序和後臺程序的操作

本實驗設計的命令解釋程式可以將程序放在前臺執行或者後臺執行。

啟動前臺程序的方法是在提示符下輸入命令列:

fp <可執行檔名(含路徑

)>

啟動後臺程序的方法是在提示符下輸入命令列:

bg& <可執行檔名(含路徑)>

在前臺程序執行期間,解釋程式一直等待,直到前臺程序執行結束,才再顯示提示符;而在後臺程序執行期間,解釋程式不必等待,會立即顯示提示符,允許使用者輸入下一條命令。

(4) 命令解釋程式還需要捕獲Ctrl+C組合鍵的訊號來結束前臺正在執行的控制檯程序,並返回使用者輸入介面(顯示提示符),等待新命令輸入。本實驗程式利用系統自備功能,來實現此功能。(注:若前臺程序是圖形介面,則按Ctrl+C並不能使其結束,而是使本實驗的命令解釋程式結束。)

(5) 其他要求

該命令解釋程式應具有相應的出錯提示功能。

程式每次接收使用者輸入的一行命令,在使用者輸入回車鍵

(Enter)後開始執行命令。

若輸入命令時僅輸入回車鍵,則不作任何操作,重新顯示提示符,等待使用者輸入新的命令。

定義空格為分隔符,程式應能處理命令中出現的重複空格符。

9.3  相關基礎知識

9.3.1  命令解釋程式與作業系統核心的關係

命令解釋程式是使用者系統核心之間的介面程式。對於Windows系統來說,由於已經提供了具有良好互動性的圖形使用者介面,傳統的控制檯命令解釋程式已經很少被廣大使用者所瞭解和使用。但是,對於某些應用,例如使用一條命令刪除所有副檔名為tmp的檔案,或者刪除某些具有特殊名字的病毒檔案,在圖形使用者介面下很難甚至不能完成。這需要通過Windows提供的Command命令介面來完成。Command程式是一個命令直譯器,它擁有自己的內部命令集,使用者和其他應用程式都可以通過對Command程式的呼叫完成與系統核心的互動。Command程式與核心的關係如圖9-1所示。

9.3.2  系統呼叫及Win32 API相關函式介紹

作業系統所能完成的每一個特殊功能都有一個函式與其對應,即作業系統把它所能完成的功能以函式的形式提供給應用程式使用。應用程式對這些函式的呼叫叫系統呼叫。這些函式的集合就是Windows作業系統提供給應用程式的程式設計介面(Application Programming Interface),簡稱Windows APIWin32 API。所有在Win32平臺上執行的應用程式都可以呼叫這些函式。

使用Windows API,應用程式可以充分挖掘Windows32位作業系統潛力。Microsoft的所有32位平臺都支援統一的API

Windows的相關API的說明都可以在MSDN(Microsoft Developet Network)中查到,包括定義、使用方法等。下面簡單介紹本實驗中涉及的Windows API

1GetCurrentDirectory函式

功能:查詢當前程序的當前目錄,呼叫成功,返回裝載到lpBuffer的位元組數。失敗則返回0

格式

DWORD GetCurrentDirectory ( //DWORD就是unsigned long

DWORD nBufferLength, // 緩衝區的長度

LPTSTR lpBuffer // 指定一個預定義字串,用於裝載當前目錄

) //LPSTR:是指向字串的指標的型別名,即char *

【注】API中涉及的型別名,請參閱配套文件“Win32 Simple Data Types.doc”。

2WaitForSingleObject函式

功能:等待一個事件訊號直至訊號出現或者超時。若等到訊號則返回WAIT_OBJECT_0(0),若等待超過dwMiliseconds時間還是無訊號,則返回WAIT_TIMEOUT(258)。若函式呼叫失敗,則返回WAIT_FAILED (-1)

格式

DWORD WaitForSingleObject (

HANDLE hHandle, // 事件的控制代碼

DWORD dwMilliseconds // 最大等待時間,以ms計時。

)

3SetCurrentDirectory函式

功能:設定當前目錄。返回非0表示成功,返回0表示失敗。

格式

BOOL SetCurrentDirectory (

LPCTSTR lpPathName // 新設定的當前目錄路徑

)

4FindFirstFile函式

功能:用於從一個資料夾(包括子資料夾)中查詢指定檔案,返回找到的檔案控制代碼。若呼叫失敗,則返回INVALID_HANDLE_VALUE (-1)

格式

HANDLE  FindFirstFile (

LPCTSTR  lpFileName, // 檔名字串(可用萬用字元)

LPWIN32_FIND_DATA lpFindFileData // 指向一個用於保護檔案的結構體

)

【注】WIN32_FIND_DATA結構的說明請參看MSDN或本上機實驗指導的配套文件。

5FindNextFile函式

功能:繼續查詢FindFirstFile函式搜尋後的檔案。它返回的檔案控制代碼可以作為引數用於FindNextFile函式。這樣就可方便地枚舉出與lpFileName引數指定的檔名相匹配的所有檔案。呼叫失敗,返回0

格式

HANDLE  FindNextFile (

HANDLE  hFindFile, // 前一個搜素到的檔案控制代碼

LPWIN32_FIND_DATA lpFindFileData // 指向一個用於保護檔案的結構體

)

6GetVolumeInformation函式

功能:用於獲取磁碟相關資訊。執行成功返回非0;失敗,返回0

格式

BOOL  GetVolumeInformation (

LPCTSTR  lpRootPathName, // 磁碟驅動器程式碼字串(具體構成方法參看程式)

LPCTSTR  lpVolumeNameBuffer, // 磁碟驅動器卷標名稱

DWORD  nVolumeNameSize, // 磁碟驅動器卷標名稱長度

LPWORD lpVolumeSerialNumber, // 磁碟驅動器卷標序列號

LPWORD lpMaximunComponentLength, //系統允許的最大檔案長度

LPWORD lpFileSystemFlags, // 檔案系統標識

LPCTSTR lpFileSystemNameBuffer, // 檔案系統名稱

DWORD nFileSystemNameSize // 檔案系統名稱長度

)

7GetDiskFreeSppaceEx函式

功能:獲取與一個磁碟的組織以及剩餘容量有關的資訊。呼叫失敗返回0

格式

HANDLE GetDiskFreeSppaceEx (

LPCTSTR  lpRootPathName, // 不包括卷名的磁碟根路徑名

PULARGE_INTEGER lpFreeBytesAvailableToCaller, // 呼叫者可用的位元組數

PULARGE_INTEGER lpTotalNumberOfBytes, // 磁碟上的總位元組數

PULARGE_INTEGER lpTotalNumberOfFreeBytes // 磁碟上的可用位元組數

)

引數說明:lpRootPathName:根路徑名。例如形式為"C:\\"。使用NULL表示函式使用當前目錄所在的磁碟。

8FileTimeToLocalFileTime函式

功能:將一個FILETIME結構轉換成本地時間。

格式

BOOL  FileTimeToLocalFileTime (

const FILETIME* lpFileTime, // 指向一個包含了UTC時間資訊的結構

LPFILETIME  lpLocalFileTime // 用於裝載轉換過的本地時間的結構體

)

9FileTimeToSystemTime函式

功能:根據一個FILETIME結構的內容,裝載一個SYSTENTIME結構。

格式

BOOL  FileTimeToSystemTime (

const FILETIME* lpFileTime, // 指向一個包含了檔案時間資訊的結構

LPFILETIME  lpSystemTime // 用於裝載系統時間的結構體

)

10CreateToolhelp32Snapshot函式

功能:為指定的程序、程序使用的堆(heap)、模組(module)、執行緒(thread)建立一個快照(snapshot)。快照建立成功則返回快照的控制代碼,失敗則返回INVALID_HANDL_VALUE

格式

HANDLE WINAPI  CreateToolhelp32Snapshot (

DWORD dwFlags, // 指向快照中包含的系統內容

DWORD th32ProcessID // 指定將要快照的程序ID

)

11Process32First函式

功能:是一個程序獲取函式,當使用CreateToolhelp32Snapshot()函式獲得當前執行程序的快照後,可以使用Process32First ( )函式獲得第一個程序的控制代碼。

格式

BOOL WINAPI  Process32First (

HANDLE hSnapshot, // 快照控制代碼

LPPROCESSENTRY32 lppe // 指向一個保護程序快照資訊的LPPROCESSENTRY32結構

)

12Process32Next函式

功能:獲取快照中下一個程序資訊。

格式

BOOL WINAPI  Process32Next (

HANDLE hSnapshot, // Process32First或Process32Next函式獲得的快照控制代碼

LPPROCESSENTRY32 lppe // 指向一個保護程序快照資訊的LPPROCESSENTRY32結構

)

13OpenProcess函式

功能:該函式開啟一個已經存在的程序物件,若成功,返回值是指定程序的開啟控制代碼。若失敗,則返回空值。

格式

HANDLE  OpenProcesst (

DWORD  dwDesiredAccess, // 許可權標識(詳見MSDN

BOOL bInheritHandle, // 指出返回的控制代碼是否能被當前程序建立的新程序繼承,

// TRUE表示可繼承,FALSE表示不能繼承。

DWORD dwProcessID // 程序ID

)

14SetConsoleCtrlHandler函式

功能:新增或刪除一個事件鉤子(Handler)

格式

BOOL  SetConsoleCtrlHandler (

PHANDLER_ROUTINE HandlerRoutine, // 回撥函式的指標

BOOL Add // 表示新增或刪除

)

15CreateProcess函式

此函式已經在前面介紹,請參閱“上機實驗一”的1.2.2節,在此不再贅述。

16GetExitCodeProcess函式

功能:獲取一個已中斷程序的退出程式碼。

格式

BOOL  GetExitCodeProcess (

HANDLE hProcess, // 程序控制代碼

LPDWORD lpExitCode // 指向接受退出碼的變數

)

17TerminateProcess函式

功能:以給定的退出碼終止程序。

格式

BOOL  TerminateProcee (

HANDLE hProcess, // 程序控制代碼

UINT uExitCode // 程序的退出碼

)

9.4  實驗設計

本實驗在WindowsXP+VC++ 6.0環境下實現,利用Windows SDK提供的系統介面(API)完成程式的功能。因為VC++包含了Windows SDK所有工具和定義,所以安裝了VC++就不用再特意安裝SDK了。實驗中所用的API,是作業系統提供的。要使用這些API,需要一些標頭檔案,最常用的就是windows.h。一些特殊的API調研還需要其他的標頭檔案。

9.4.1  重要的資料結構

1.歷史命令迴圈陣列(佇列)

history命令中,用陣列來存放輸入過的歷史命令。程式中假設該陣列的元素個數為20,陣列元素的結構定義如下:

typedef struct ENV_HISTORY {

int start; // 佇列的頭指標

int end; // 佇列的尾指標

char his_cmd[20][128]; // 佇列陣列(順序結構的佇列)

} ENV_HISTORY;

ENV_HISTORY envhis; // 定義佇列變數(為佇列分配記憶體空間)

2.檔案資訊連結串列

程式中,需要把dir命令取得的檔案資訊用連結串列儲存,輸出這些資訊時對連結串列遍歷。

連結串列結點的定義如下:

struct files_Content {

FILETIME time; // 檔案建立時間

char name[200]; // 檔名

int type; // type=1普通檔案, type=0目錄

int size; // 檔案大小

files_Content *next; // 構成連結串列的連結指標

} ;

9.4.2  程式實現

主程式的流程如圖9-2所示。

1.解析命令

解析命令就是分析輸入的命令列(input陣列),分離命令列中的命令和引數。命令和引數的分隔是由空格符完成的。將命令存入arg[0]指向的字串,將引數存入arg[1]指向的字串中。

2.命令處理

命令出路與執行命令的目的有關,其中系統呼叫是重要的組成部分。

void cd_cmd(char *route)

{

if (!SetCurrentDirectory(route))

{ // 設定當前目錄,若失敗則輸出出錯資訊

cout<<" SetCurrentDirectory failed ";

cout<<GetLastError()<<endl;

}

}

以上是cd命令的處理函式,涉及的Windows API

SetCurrentDirectory( )函式,它的作用是設定當前目錄為指定路徑,若失敗則返回出錯資訊。其中出錯號從另一個API函式GetLastError( )獲得。

其餘命令處理函式結構類似,具體參見下面的原始碼。

9.5  源程式與執行結果

9.5.1  程式原始碼

1WinShell.h

#define BUFSIZE MAX_PATH

#define HISNUM 20 //最多可以儲存20個歷史命令

char buf[BUFSIZE];

//儲存歷史命令的結構

typedef struct ENV_HISTORY {

int start; // 佇列的頭指標

int end; // 佇列的尾指標

char his_cmd[20][128]; // 佇列陣列(順序結構的佇列)

} ENV_HISTORY;

ENV_HISTORY envhis; // 定義佇列變數(為佇列分配記憶體空間)

//說明:因envhis是全域性變數(屬靜態變數),故其成員star,end有初值0

//儲存檔案或目錄相關資訊的結構

struct files_Content {

FILETIME time; // 檔案建立時間

char name[200]; // 檔名

int type; // type=1普通檔案, type=0目錄

int size; // 檔案大小

files_Content *next; // 構成連結串列的連結指標

} ;

2WinShell.cpp

#define _Win32_WINNT 0x0501

#include <stdlib.h> //atoi()

#include <iostream.h>

#include <windows.h> //DWORD;HANDLE...其中還有許多標頭檔案

#include <tlhelp32.h> //CreateToolhelp32Snapshot()

#include <string.h>

#include "WinShell.h"

// 以下兩個函式在主函式開頭聲明後放在主函式後面不行,故將它們移

// mian()的前面,原因可能是它們的引數型別分別是FILETIMEDWORD

// ***************** 時間處理函式 ******************

void ftime(FILETIME filetime)

{

SYSTEMTIME systemtime;

if (filetime.dwLowDateTime==-1) // Win32時間的低32

cout<<"Never Expires\n";

else

{

//UTC(Universal Time Coordinated)檔案時間轉換成本地檔案時間

if (FileTimeToLocalFileTime(&filetime,&filetime)!=0)

{

//64位時間轉化成系統時間

if (FileTimeToSystemTime(&filetime,&systemtime)!=0)

{

//以一定能格式輸出時間

cout.fill('0'); //不足指定寬度是用0填充

cout<<dec<<systemtime.wYear<<'-';

cout.width(2);cout<<systemtime.wMonth<<'-'; //月份用2位顯示,下類似

cout.width(2);cout<<systemtime.wDay<<"  ";

cout.width(2);cout<<systemtime.wHour<<':';

cout.width(2);cout<<systemtime.wMinute;

}

else

cout<<"FileTimeToSystemTime failed\n";

}

else

cout<<"FileTimeToLocalFileTime failed\n";

}

cout.fill(' '); //恢復空格填充

}

// ************ 回撥函式 ************

BOOL WINAPI ConsoleHandler(DWORD CEvent)

{ // 此函式不做什麼,由系統處理事件,包括按下Ctrl+C

switch(CEvent)

{

case CTRL_C_EVENT:

break;

case CTRL_BREAK_EVENT:

break;

case CTRL_CLOSE_EVENT:

break;

case CTRL_LOGOFF_EVENT:

break;

case CTRL_SHUTDOWN_EVENT:

break;

}

return TRUE;

}

// **************** 主函式 ****************

void main()

{

//宣告程式中用到的函式

void cd_cmd(char *dir); // cd命令處理函式

void dir_cmd(char *dir); // dir命令處理函式

void GetProcessList(); // 獲得系統當前程序列表

void history_cmd(); // 獲得最近輸入的命令

void add_history(char *); // 將輸入命令列新增到命令歷史中

HANDLE process(int,char[]); // 建立程序

BOOL killProcess(char *); // kill程序

void help(); // 顯示幫助資訊

char c,*input,*arg[2],path[BUFSIZE];

int input_len=0,is_bg=0,i,j,k;

HANDLE hprocess; // 程序執行結束,返回程序控制代碼

DWORD dwRet;

while (true)//顯示提示符,等待使用者輸入命令是個無限迴圈過程

{

// 將指向輸入命令的指標陣列初始化

for (i=0;i<2;i++)

arg[i]=NULL;

// 獲得當前目錄並存入path中,BUFSIZE是最多能夠儲存的路徑名長度

dwRet=GetCurrentDirectory(BUFSIZE,path);//返回目錄資料實際長度存於dwRet

if (dwRet==0) // 返回當前目錄失敗,輸出出錯資訊

cout<<"GetCurrentDirectory failed "<<GetLastError()<<endl;

else if (dwRet>BUFSIZE)// BUFSIZE長度小於返回目錄資料的長度,輸出出錯資訊

cout<<"GetCurrentDirectory failed (buffer too small; need "<<dwRet<<"bytes)\n";

else

cout<<path<<'>'; // 顯示提示符(當前目錄名+'>')

// *********** 鍵盤輸入 ************

input_len=0;

// 將命令開頭的無用字元過濾掉

while ((c=cin.get())==' ' || c=='\t' || c==EOF) ;

if (c=='\n') //輸入為空命令(僅輸入回車符)

continue; //結束本次迴圈,回到迴圈開頭,重新顯示提示符

while (c != '\n')

{

buf[input_len++]=c;

c=cin.get();

}

buf[input_len++]='\0'; // 加上串結束符

// 分配動態儲存空間,將命令從快取複製到input

input=new char[input_len];

strcpy(input,buf); //為了便於後邊的處理,將命令行復制到input

// *********** 解析命令 ************

for (i=0,j=0,k=0; i<input_len && k<2; i++)//k<2是限制只處理1個命令引數

{   //arg[0]為命令,arg[1]為引數

if (input[i]==' ' || input[i]=='\0')

{

if (j==0) // 去掉連在一起的多個空格

continue;

else

{

buf[j++]='\0';

arg[k]=new char[sizeof(char)*(j+1)];

strcpy(arg[k++],buf); // 將命令或引數複製到arg

j=0; // 準備取下一個引數

}

}

else//不是' ''\0'字元,則存入buf[]

buf[j++]=input[i];

}

add_history(input); // 將輸入命令新增到歷史命令佇列中

// ****************** 命令處理 ******************

if (strcmp(arg[0],"cd") == 0) // **** cd命令 ****

{

if (arg[1] != NULL)

{

cd_cmd(arg[1]);

delete []arg[1];

}

else

cout<<"cd命令必須指定路徑名!\n";

delete []input;

delete []arg[0];

continue; //返回迴圈開頭,重新顯示提示符

}

if (strcmp(arg[0],"dir")==0) // **** dir命令 ****

{

char *route;

if (arg[1]==NULL) // dir命令無引數,則對當前目錄操作

{

route=path; // 取當前目錄

dir_cmd(route);

}

else

{

dir_cmd(arg[1]);

delete []arg[1];

}

delete []input; // 釋放堆空間

delete []arg[0];

continue;

}

if (strcmp(arg[0],"tasklist")==0) // **** tasklist命令 ****

{

GetProcessList();// 該函式通過呼叫若干API函式,獲取系統當前程序列表

delete []input;

delete []arg[0];

if (arg[1] != NULL)//防止使用者誤輸入命令引數

delete arg[1];

continue;

}

if (strcmp(arg[0],"fp")==0) // *** fp命令(前臺程序) ***

{

if (arg[1]==NULL)

{

cout<<"沒有指定可執行檔案\n";

delete []input;

delete []arg[0];

continue;

}

is_bg=0; // 後臺標誌置0(不是後臺程序)

hprocess=process(is_bg,arg[1]); //建立程序,返回新程序的控制代碼

// 等待新程序執行完畢(INFINTE表示等待無限制)

if (WaitForSingleObject(hprocess,INFINITE)==WAIT_OBJECT_0)

{

//如果程序執行完畢,釋放控制檯

delete []input;

delete []arg[0];

delete []arg[1];

}

continue;

}

if (strcmp(arg[0],"bg&")==0) // *** bg&命令(後臺程序) ***

{

if (arg[1]==NULL)

{

cout<<"沒有指定可執行檔案\n";

delete []input;

delete []arg[0];

continue;

}

is_bg=1; // 後臺標誌置1()

process(is_bg,arg[1]); //為可執行檔案arg[1]建立後臺程序

delete []input;

delete []arg[0];

delete []arg[1];

continue;

}

if (strcmp(arg[0],"taskkill")==0) // ***** kill程序 *****

{

BOOL success;

if (arg[1]!=NULL)

{

success=killProcess(arg[1]); // arg[1]指向程序ID

if (!success) // 若撤銷程序失敗,則顯示出錯資訊

cout<<"kill process failed!\n";

delete []arg[1];

}

else

cout<<"taskkill命令必須指定程序ID!"<<endl;

delete []input;

delete []arg[0];

if (arg[1] != NULL)//防止使用者誤輸入命令引數

delete arg[1];

continue;

}

if (strcmp(arg[0],"history")==0) // **** 顯示歷史命令 ****

{

history_cmd();

delete []input;

delete []arg[0];

if (arg[1] != NULL)//防止使用者誤輸入命令引數

delete arg[1];

continue;

}

if (strcmp(arg[0],"help")==0) // **** help命令 ****

{

help();

delete []input;

delete []arg[0];

if (arg[1] != NULL)//防止使用者誤輸入命令引數

delete arg[1];

continue;

}

if (strcmp(arg[0],"exit")==0) // **** exit命令 ****

{

cout<<"\nBye bye!\n\n";

delete []input;

delete []arg[0];

if (arg[1] != NULL)//防止使用者誤輸入命令引數

delete arg[1];

break; // 退出死迴圈,結束程式

}

else // 輸入命令不正確,給出出錯資訊

{

cout<<"please input correct commmand!\n";

delete []input;

if (arg[0])

delete []arg[0];

if (arg[1])

delete []arg[1];

continue;

}

}

} // **** 主函式結束 ****

// ************* 相關命令出路函式 **************

void cd_cmd(char *route) // **** cd命令實現函式 ****

{

if (!SetCurrentDirectory(route)) //設定當前目錄,若失敗則返回出錯資訊

cout<<"SetCurrentDirectory failed "<<GetLastError()<<endl;

}

// ***************** dir命令實現函式 *****************

void dir_cmd(char *route)

{

WIN32_FIND_DATA FindFileData; //將找到的檔案或目錄以WIN32_FIND_DATA結構返回

files_Content head,*p,*q; //定義指定檔案結構體的頭結點和指標

HANDLE hFind=INVALID_HANDLE_VALUE; // 控制代碼變數初值為“非法控制代碼值”

DWORD dwError; // 定義32位整數

char volume_name[256],str[22];

int file=0,dir=0; //檔案數和目錄數初始值為0

_int64 sum_file=0; //總檔案大小為0位元組,其值較大儲存為64位整數

_int64 l_user,l_sum,l_idle; //呼叫者可用空間,總容量,磁碟總可用空間

unsigned long volume_number; //卷序列號

char *DirSpec[4];

head.next=NULL;

DirSpec[0]=new char[2];

strncpy(DirSpec[0],route,1);

DirSpec[0][1]='\0'; //DirSpec[0]為驅動器名

DirSpec[1]=new char[4];

strcpy(DirSpec[1],DirSpec[0]);

strncat(DirSpec[1],":\\",3); //DirSpec[1]用於獲取驅動器資訊

DirSpec[2]=new char[strlen(route)+2];

DirSpec[3]=new char[strlen(route)+5];

strcpy(DirSpec[2],route); //DirSpec[2]dir命令的目錄名

strcpy(DirSpec[3],route);

int len=strlen(route);

if (route[len-1]!='\\')

strncat(DirSpec[2],"\\",2);

strncat(DirSpec[3],"\\*.*",5); //DirSpec[3]用於查詢目錄中的所有檔案

//搜素DirSpec[3]指定的檔案,檔案資訊存於FindFileData變數中,返回找到的檔案控制代碼

hFind=FindFirstFile(DirSpec[3],&FindFileData);

if (hFind==INVALID_HANDLE_VALUE) //查詢控制代碼返回為無效值,查詢失敗

cout<<"Invalid file handle, Error is "<<GetLastError()<<endl;

else

{

//獲取卷的卷名(存於volume_name),卷序列號(存於volume_number)

GetVolumeInformation(DirSpec[1],volume_name,50,&volume_number,NULL,NULL, NULL,10);

if (strlen(volume_name)==0)

cout<<"\n\n驅動器"<<DirSpec[0]<<"中的卷沒有標籤。"<<endl;

else

cout<<"\n\n驅動器"<<DirSpec[0]<<"中的卷是 "<<volume_name<<endl;

cout<<"卷的序列號是 "<<hex<<volume_number<<dec<<endl<<endl;;

cout<<DirSpec[2]<<" 的目錄\n\n";

head.time=FindFileData.ftCreationTime;//獲得的檔案建立時間,存入檔案結構體head

strcpy(head.name,FindFileData.cFileName);//獲得的檔名,存入檔案結構體head

// 若資料屬性是目錄,則置type0

if (FindFileData.dwFileAttributes==FILE_ATTRIBUTE_DIRECTORY)

{

head.type=0;

dir++;

}

else

{

//如果資料屬性是檔案,type位為1

head.type=1;

head.size=FindFileData.nFileSizeLow; //將檔案大小存入結構體head

file++; //檔案數增1

sum_file += FindFileData.nFileSizeLow; //將檔案大小(位元組數)累加

}

p=&head; // p指向頭結點head

//如果還有下一個資料,繼續查詢

while (FindNextFile(hFind,&FindFileData) != 0)

{ // 第二個結點開始,分配動態空間

q=new files_Content[sizeof(files_Content)];

q->next=NULL;

q->time=FindFileData.ftCreationTime; // 儲存檔案建立時間

strcpy(q->name,FindFileData.cFileName); // 儲存檔名

if (FindFileData.dwFileAttributes==FILE_ATTRIBUTE_DIRECTORY)

{

q->type=0; // 找到的是目錄

dir++; // 目錄數增1

}

else // 否則,找到的是檔案

{

//如果資料屬性是檔案,type位為1

q->type=1;

q->size=FindFileData.nFileSizeLow; //將檔案大小存入結構體

file++; //檔案數增1

sum_file += FindFileData.nFileSizeLow; //將檔案大小累加

}

p->next=q; // 構成單鏈表

p=q; // p指向新結點

}

p->next=NULL; // 連結串列尾結點的next指標須置為NULL

//將結構體中資料的建立時間、型別、大小、名稱等資訊依次輸出

p=&head; // 從連結串列頭結點開始

while (p != NULL)

{

ftime(p->time); // 按規定格式顯示檔案建立時間

if (p->type==0) // 若是目錄,則顯示“<DOR>

cout<<"\t<DIR>\t\t";

else

{ // 若是檔案,則按寬度為9的格式顯示檔案大小(位元組數)

cout<<"\t\t";cout.width(9);

cout<<dec<<(unsigned)p->size;

}

cout<<'\t'<<p->name<<endl; // 顯示檔名

p=p->next; // 準備顯示下一個目錄項(檔案或目錄)

}

//顯示檔案和目錄總數以及磁碟空間相關資訊

cout.width(15);

cout<<file<<" 個檔案\t\t\t";

//printf()使用格式符“%I64d”可以輸出64位整數,但“cout<<”只支援32位整數

//故此處先將64位整數sum_file轉換成以10進位制形式的字串後再輸出

_i64toa(sum_file,str,10); //64位整數sum_file轉換成10進位制字串存於str

cout<<str<<" 位元組"<<endl;

GetDiskFreeSpaceEx(DirSpec[1],(PULARGE_INTEGER)&l_user,

(PULARGE_INTEGER)&l_sum,(PULARGE_INTEGER)&l_idle);

cout.width(15);

cout<<dir<<" 個目錄\t\t\t";

_i64toa(l_idle,str,10); //64位整數l_idle轉換成10進位制字串存於