本系統為數據結構課程技能考試開發,採用 C++(含 C 風格系統程式設計) 實現,採雙端架構:
核心創新點:**系統級防作弊機制**——即時檢測 USB 存儲裝置插入、禁止 Word/PPT/記事本等輔助工具運行。
┌─────────────────────┐ 局域網/共享目錄 ┌─────────────────────┐
│ 學生端 │ ◄──────────────────► │ 教師端 │
│ (考試客戶端) │ 提交 .cpp + 加密.txt │ (閱卷管理端) │
├─────────────────────┤ ├─────────────────────┤
│ • 身份驗證 (姓名/學號) │ │ • 掃描考生目錄 │
│ • 防作弊監控執行緒 │ │ • 批次解密資訊檔 │
│ • 學號尾碼抽題演算法 │ │ • 啟動編輯器看代碼 │
│ • 雙階段倒計時 (60+20)│ │ • 錄入分數生成 CSV │
│ • 代碼寫入 .cpp 檔案 │ │ │
│ • 資訊檔 XOR 加密 │ │ │
└─────────────────────┘ └─────────────────────┘
採用 Windows API 實現兩大檢測:
int GetDriverInfo(LPSTR szDrive) {
UINT uDriverType = GetDriveType(szDrive);
return (uDriverType == DRIVE_REMOVABLE) ? 1 : 0; // 可移動裝置
}
int checkUdisk() {
CHAR szLogicDriveStrings[1024];
GetLogicalDriveStrings(1023, szLogicDriveStrings);
PCHAR szDrive = szLogicDriveStrings;
for (int i = 0; i < 7; i++) { // 掃描 A: 到 G: 槽位
if (GetDriverInfo(szDrive)) return 1;
szDrive += (lstrlen(szDrive) + 1); // 移至下一個驅動器字串
}
return 0;
}
原理: GetLogicalDriveStrings 取得所有邏輯磁碟機代號,GetDriveType 判斷是否為 `DRIVE_REMOVABLE`(USB、SD 卡等)。檢測到時強制阻塞並彈窗警告,直到拔除裝置。
int checkTask() {
string cmd = "tasklist | find \"WINWORD\" > 1.txt";
system(cmd.c_str()); // 依次檢查 WINWORD, POWERPNT, notepad
// ... 讀取 1.txt 判斷是否有進程輸出 ...
}
機制: 呼叫系統命令 `tasklist` 管道 `find` 篩選特定進程名,輸出導向暫存檔,讀取檔案長度判斷進程是否存在。發現違規程序立即強制關閉並重顯試題。
int chuti(int a) { // a 為學號後四位 ASCII 碼總和
a = a % 8; // 映射到 0-7
int b = a + 10; // 第二題固定偏移 10(對應題庫 11-18 題)
// 載入題目檔案:Ti\1.txt ~ Ti\18.txt
// 記錄抽題資訊至 xinxi.txt
}
設計意圖: 學號後四位具隨機性,ASCII 碼總和再模 8,實現「同學號固定抽題、不同學號均勻分布」的偽隨機效果。兩題固定間距 10,確保題目類型覆蓋面(如基本操作+綜合應用)。
// 正式考試 60 分鐘
void daojishi(int a, int m[0], int sec[0]) {
m[0] = 59; sec[0] = 59;
while (m[0] >= 0) {
// 每 10 秒執行一次防作弊掃描
if (sec[0] % 10 == 0) {
if (checkUdisk() || checkTask()) {
system("cls");
chongchuti(a); // 重顯試題
}
}
// _kbhit() 檢測鍵盤輸入(學生提前交卷)
Sleep(1000); sec[0]--;
}
}
// 超時容忍 20 分鐘
void chaoshi(int a, int m1[0], int sec1[0]) { ... }
人性化設計: 60 分鐘正式考試 + 20 分鐘緩衝期。緩衝期同樣啟用防作弊掃描,超時自動交卷。每 10 秒掃描一次平衡了即時性與 CPU 佔用。
void jiami() {
FILE *fp = fopen("xinxi.txt", "rb+");
while ((n = fread(buf, 1, NORMAL_SIZ, fp)) > 0) {
for (int i = 0; i < n; i++) buf[i] ^= CHMSK_KEY; // XOR 0xA5
fseek(fp, -n, SEEK_CUR);
fwrite(buf, 1, n, fp);
fflush(fp); // 關鍵:強制刷新緩衝區,確保小檔案也能寫入磁碟
}
fclose(fp);
}
XOR 加密特點: 速度極快、對稱加密(再執行一次即解密)、足夠防止學生直接閱讀純文字資訊檔。`fflush(fp)` 解決了 C 標準庫緩衝區導致小檔案寫入失效的經典問題。
void read(int num) {
system("dir *.cpp /b > cpp1.dat"); // 獲取所有 .cpp 檔案列表
for (int i = 0; i < num; i++) {
fscanf(in, "%s", str1[i]); // 讀取檔名
system(str); // 用關聯程式打開 .cpp(如 VS Code/Dev C++)
// 修改副檔名為 .txt 並解密
str2[j-1] = 't'; str2[j-2] = 'x'; str2[j-3] = 't';
jiemi(str2);
system(str2); // 打開解密後的資訊檔
// 讀取姓名、學號、作答時間等
// 提示教師輸入分數
fprintf(fq, "%s,%s,%.1f\n", name, id, score); // 寫入 CSV
}
}
效率提升: 教師無需手動複製檔案、解壓、打開編輯器、記錄分數。一鍵啟動即可依次瀏覽所有學生代碼與資訊,專注於「閱讀代碼邏輯」與「給分」。
總結: 這套系統雖代碼風格偏 C 風格(大量指標、手動記憶體管理、Win32 API 直接呼叫),但完整展示了**系統程式設計**在實際教學場景中的落地能力:進程管理、檔案操作、加密、進程間通訊(檔案共享)、定時器、硬體監控。對於學習 C/C++ 系統程式設計、Windows API 應用、考試系統架構的開發者,極具參考價值。