中國象棋小游戲(C版)
|
freeflydom
2025年6月2日 9:25
本文熱度 305
|
! 此文僅展示此游戲的最簡單版本,可以實現中國象棋雙人對戰的基本功能。更多功能體驗可訪問上方鏈接。
說明: #include<graphics.h> 一個在 C/C++ 中用于圖形編程的頭文件,主要用于創建和操作圖形界面。具有繪制圖形、設置顏色、鼠標和鍵盤時間處理等功能。#include<conio.h> 提供了對控制臺輸入/輸出的簡單操作,如字符讀取和屏幕刷新。#include<windows.h> 包含了 Windows API 的各種函數和數據結構定義,允許程序直接調用 Windows 操作系統的功能。可用于窗口創建與管理、進程和線程操作、文件操作、系統信息獲取等。#include<mmsystem.h> 提供了多媒體功能,包括音頻和定時器功能。同時需要鏈接winmm.lib 庫,通過#pragma comment(lib,"winmm.lib") 實現
整體思路- 創建圖形窗口,繪制中國象棋棋局圖案
- 定義棋子,并在棋局上繪制初始化棋子
- 實現游戲控制功能。即可通過鼠標操作實現棋子的移動以及紅黑雙方的交換操作
- 添加棋子的走法規則,將軍的判定以及勝負判定
- 添加背景音樂、走棋音效等
- 添加紅黑雙方計時功能
- 打包軟件
實現過程創建圖形窗口,繪制中國象棋棋局圖案。
我們先看此步驟完成后的結果。

只要對graphics.h 有所了解,創建圖形窗口并不困難。
我們首先在主函數中直接初始化圖形窗口。
#include<stdio.h>
#include<graphics.h>
int main(){
initgraph(800,800);
}
接下來我們需要定義一個繪制游戲的函數。首先我們可以定義一個行數、列數、間隔以及棋盤格子大小
#define INTERVAL 50
#define CHESS_GRID_SIZE 70
#define ROW 10
#define COL 9
接下來繪制棋局的格子
void GameDraw() {
setbkcolor(RGB(252, 215, 162));
cleardevice();
setlinecolor(BLACK);
setlinestyle(PS_SOLID, 2);
setfillcolor(RGB(252, 215, 162));
fillrectangle(INTERVAL - 5, INTERVAL - 5, CHESS_GRID_SIZE * 8 + INTERVAL + 5, CHESS_GRID_SIZE * 9 + INTERVAL + 5);
for (int i = 0;i < 10;i++) {
line(INTERVAL, i * CHESS_GRID_SIZE + INTERVAL, CHESS_GRID_SIZE * 8 + INTERVAL, i * CHESS_GRID_SIZE + INTERVAL);
if (i < 9) {
line(i * CHESS_GRID_SIZE + INTERVAL, INTERVAL, i * CHESS_GRID_SIZE + INTERVAL, 9 * CHESS_GRID_SIZE + INTERVAL);
}
}
}
接著顯示“楚河漢界”文字
void GameDraw(){
fillrectangle(INTERVAL, 4 * CHESS_GRID_SIZE + INTERVAL, 8 * CHESS_GRID_SIZE + INTERVAL, 5 * CHESS_GRID_SIZE + INTERVAL);
settextcolor(BLACK);
settextstyle(50, 0, "楷體");
char river[25] = "楚 河 漢 界";
int twidth = textwidth(river);
int theight = textheight(river);
twidth = (8 * CHESS_GRID_SIZE - twidth) / 2;
theight = (CHESS_GRID_SIZE - theight) / 2;
outtextxy(INTERVAL + twidth, 4 * CHESS_GRID_SIZE + theight + INTERVAL, river);
}
最后畫米字完成第一步。
void GameDraw(){
line(3 * CHESS_GRID_SIZE + INTERVAL, INTERVAL, 5 * CHESS_GRID_SIZE + INTERVAL, 2 * CHESS_GRID_SIZE + INTERVAL);
line(5 * CHESS_GRID_SIZE + INTERVAL, INTERVAL, 3 * CHESS_GRID_SIZE + INTERVAL, 2 * CHESS_GRID_SIZE + INTERVAL);
line(3 * CHESS_GRID_SIZE + INTERVAL, 7 * CHESS_GRID_SIZE + INTERVAL, 5 * CHESS_GRID_SIZE + INTERVAL, 9 * CHESS_GRID_SIZE + INTERVAL);
line(5 * CHESS_GRID_SIZE + INTERVAL, 7 * CHESS_GRID_SIZE + INTERVAL, 3 * CHESS_GRID_SIZE + INTERVAL, 9 * CHESS_GRID_SIZE + INTERVAL);
}
定義棋子,并在棋局上繪制初始化棋子
我們先看此步驟完成后的結果。

根據中國象棋游戲規則,棋子分為紅黑雙方,不同棋子有不同的走法和不同的過河標準。
于是我們首先需要定義紅黑雙方的棋子名稱,這里使用兩個指向常量的指針數組分別代表紅方棋子和黑方棋子。(用指針數組存儲每個棋子名稱字符串的起始地址,且是只讀模式。更靈活高效且易于修改和維護)
const char* redChess[7] = { "車","馬","相","仕","帥","炮","兵" };
const char* blackChess[7] = { "車","馬","象","士","將","砲","卒" };
每個棋子的位置由橫縱坐標確定,且每個棋子都有名稱、坐標、紅黑方、是否過河等屬性。于是我們定義一個棋子結構體如下:
struct Chess {
char name[4];
int x;
int y;
char type;
bool flag;
}map[ROW][COL];
接下來就需要對結構體進行初始化,在相應的位置放入相應的棋子。
void GameInit() {
for (int i = 0;i < ROW;i++) {
int temp = 0, temp1 = 0, temp2 = 1;
for (int k = 0;k < COL;k++) {
char chessname[4] = "";
char mcolor = 'B';
if (i <= 4) {
if (i == 0) {
if (temp <= 4) temp++;
else {
temp1 = 4 - temp2;
temp2++;
}
sprintf(chessname, "%s", blackChess[temp1]);
temp1++;
}
if (i == 2 && (k == 1 || k == 7)) {
strcpy(chessname, blackChess[5]);
}
if (i == 3 && (k % 2 == 0)) {
strcpy(chessname, blackChess[6]);
}
}
else {
mcolor = 'R';
if (i == 9) {
if (temp <= 4) temp++;
else {
temp1 = 4 - temp2;
temp2++;
}
sprintf(chessname, "%s", redChess[temp1]);
temp1++;
}
if (i == 7 && (k == 1 || k == 7)) {
strcpy(chessname, redChess[5]);
}
if (i == 6 && (k % 2 == 0)) {
strcpy(chessname, redChess[6]);
}
}
map[i][k].type = mcolor;
strcpy(map[i][k].name, chessname);
map[i][k].flag = false;
map[i][k].x = k * CHESS_GRID_SIZE + INTERVAL;
map[i][k].y = i * CHESS_GRID_SIZE + INTERVAL;
}
}
}
最后,我們在繪制圖形函數中繼續添加關于繪制棋子的代碼,同時這里以圓圈代表一個棋子。
void DameDraw(){
settextstyle(40, 0, "楷體");
for (int i = 0;i < ROW;i++) {
for (int k = 0;k < COL;k++) {
if (strcmp(map[i][k].name, "") != 0) {
if (map[i][k].type == 'B') {
settextcolor(BLACK);
setlinecolor(BLACK);
}
else {
settextcolor(RED);
setlinecolor(RED);
}
fillcircle(map[i][k].x, map[i][k].y, 30);
outtextxy(map[i][k].x - 20, map[i][k].y - 20, map[i][k].name);
}
}
}
}
實現游戲控制功能。即可通過鼠標操作實現棋子的移動以及紅黑雙方的交換操作。
我們先看此步驟完成后的結果。

在實現此功能前我們需要了解#include<windows.h> 頭文件。 MOUSEMSG :是該頭文件下的一個結構體,有以下成員
成員變量 | 類型 | 描述 |
---|
uMsg | uint | 鼠標消息類型,例如 WM_MOUSEMOVE (鼠標移動)、WM_LBUTTONDOWN (左按下鍵)、WM_RBUTTONDOWN (右鍵按下)等。 | x | int | 鼠標事件發生時的 X 坐標。 | y | int | 鼠標事件發生時的 Y 坐標。 | time | uint | 鼠標事件發生時的時間戳。 | dwExtraInfo | uint | 額外信息,通常用于區分相同消息的不同實例。 |
我們首先做出如下定義:
POINT begin = { -1,-1 }, end = { -1,-1 };
MOUSEMSG msg;
bool isRedTurn = true;
接下來實現游戲控制功能。
首先是檢測鼠標的點擊
if (MouseHit) {
msg = GetMouseMsg();
if (msg.uMsg == WM_LBUTTONDOWN) {
}
}
再轉換鼠標坐標為棋盤坐標以及檢查點擊位置是否合法
int k = (msg.x - INTERVAL + CHESS_GRID_SIZE / 2) / CHESS_GRID_SIZE;
int i = (msg.y - INTERVAL + CHESS_GRID_SIZE / 2) / CHESS_GRID_SIZE;
if (k < 0 || k >= COL || i < 0 || i >= ROW) return;
接下來是第一次點擊選擇棋子
if (begin.x == -1) {
if ((isRedTurn && map[i][k].type == 'R') || (!isRedTurn && map[i][k].type == 'B')) {
begin.x = k;
begin.y = i;
}
}
最后是第二次點擊移動棋子
else {
end.x = k;
end.y = i;
int flagg = 0;
if (strcmp(map[end.y][end.x].name, "") == 0) flagg++;
strcpy(map[end.y][end.x].name, map[begin.y][begin.x].name);
map[end.y][end.x].type = map[begin.y][begin.x].type;
map[end.y][end.x].flag = map[begin.y][begin.x].flag;
strcpy(map[begin.y][begin.x].name, "");
isRedTurn = !isRedTurn;
begin.x = -1;
}
完整的函數如下:
void GameControl() {
if (MouseHit {
msg = GetMouseMsg();
if (msg.uMsg == WM_LBUTTONDOWN) {
int k = (msg.x - INTERVAL + CHESS_GRID_SIZE / 2) / CHESS_GRID_SIZE;
int i = (msg.y - INTERVAL + CHESS_GRID_SIZE / 2) / CHESS_GRID_SIZE;
if (k < 0 || k >= COL || i < 0 || i >= ROW) return;
if (begin.x == -1) {
if ((isRedTurn && map[i][k].type == 'R') || (!isRedTurn && map[i][k].type == 'B')) {
begin.x = k;
begin.y = i;
}
}else {
end.x = k;
end.y = i;
int flagg = 0;
if (strcmp(map[end.y][end.x].name, "") == 0) flagg++;
strcpy(map[end.y][end.x].name, map[begin.y][begin.x].name);
map[end.y][end.x].type = map[begin.y][begin.x].type;
map[end.y][end.x].flag = map[begin.y][begin.x].flag;
strcpy(map[begin.y][begin.x].name, "");
isRedTurn = !isRedTurn;
begin.x = -1;
}
}
}
}
最后,我們繼續在繪制函數中添加對棋子的選中效果
void GameDraw(){
if (i == begin.y && k == begin.x) {
setlinecolor(BLUE);
circle(map[i][k].x, map[i][k].y, 30);
circle(map[i][k].x, map[i][k].y, 32);
}
}
添加棋子的走法規則,將軍的判定以及勝負判定
這一步是整個象棋游戲的關鍵步驟。我們需要定義一個檢驗函數,對每一個棋子進行走法檢驗。
首先定義該檢驗函數:
bool CheckMove(int fromI, int fromK, int toI, int toK) { }
第一步需要規定禁止吃己方棋子:
這個不能實現,只需要當判斷目標位置存在棋子且該棋子的陣營屬性和移動的棋子相同,則禁止移動即可。
struct Chess fromChess = map[fromI][fromK];
struct Chess toChess = map[toI][toK];
if (toChess.type == fromChess.type && strcmp(toChess.name, "") != 0)
return false;
第二步檢驗車的走法 - 車的走法就是必須橫向或縱向移動(
fromI == toI 或 fromK == toK ) - 路徑上所經過的格子都必須為空即可。
if (strcmp(fromChess.name, "車") == 0 || strcmp(fromChess.name, "車") == 0) {
if (fromI != toI && fromK != toK) return false;
int step = (fromI == toI) ? (toK > fromK ? 1 : -1) : (toI > fromI ? 1 : -1);
int distance = (fromI == toI) ? abs(toK - fromK) : abs(toI - fromI);
for (int i = 1; i < distance; i++) {
int x = (fromI == toI) ? fromI : fromI + step * i;
int y = (fromI == toI) ? fromK + step * i : fromK;
if (strcmp(map[x][y].name, "") != 0) return false;
}
return true;
}
第三步檢驗馬的走法 - 馬的移動方式是日字格(橫向1格,縱向2格,或橫向2格,縱向1格)。
- 判斷其移動是否存在蹩馬腳的情況(即相鄰格子必須為空)
else if (strcmp(fromChess.name, "馬") == 0 || strcmp(fromChess.name, "馬") == 0) {
int dx = abs(toK - fromK);
int dy = abs(toI - fromI);
if (!((dx == 1 && dy == 2) || (dx == 2 && dy == 1))) return false;
int blockX = fromI + (toI - fromI) / 2;
int blockY = fromK + (toK - fromK) / 2;
if (strcmp(map[blockX][blockY].name, "") != 0) return false;
return true;
}
第四步檢驗象的走法 - 象的移動是田字格(橫向和縱向均移動2格)
- 田字中間必須為空。
- 象無法過河。
else if (strcmp(fromChess.name, "相") == 0 || strcmp(fromChess.name, "象") == 0) {
int dx = abs(toK - fromK);
int dy = abs(toI - fromI);
if (dx != 2 || dy != 2) return false;
int centerX = (fromI + toI) / 2;
int centerY = (fromK + toK) / 2;
if (strcmp(map[centerX][centerY].name, "") != 0) return false;
if (fromChess.type == 'B' && toI > 4) return false;
if (fromChess.type == 'R' && toI < 5) return false;
return true;
}
第五步檢驗士的走法 - 士在己方九宮格內移動
- 每次只能斜向移動一格(橫向和縱向均移動1格)
else if (strcmp(fromChess.name, "仕") == 0 || strcmp(fromChess.name, "士") == 0) {
if (fromChess.type == 'B') {
if (toI > 2 || toK < 3 || toK > 5) return false;
} else {
if (toI < 7 || toK < 3 || toK > 5) return false;
}
return (abs(toK - fromK) == 1 && abs(toI - fromI) == 1);
}
第六步檢驗將的走法 else if (strcmp(fromChess.name, "帥") == 0 || strcmp(fromChess.name, "將") == 0) {
if (fromChess.type == 'B') {
if (toI > 2 || toK < 3 || toK > 5) return false;
} else {
if (toI < 7 || toK < 3 || toK > 5) return false;
}
if ((abs(toK - fromK) + abs(toI - fromI)) != 1) return false;
return true;
}
第七步檢驗炮的走法 - 必須直線移動
- 若目標為空,路徑上不能有任何子
- 若目標位敵方棋子,路徑上必須恰好有一個棋子
else if (strcmp(fromChess.name, "砲") == 0 || strcmp(fromChess.name, "炮") == 0) {
if (fromI != toI && fromK != toK) return false;
int count = 0;
int step = (fromI == toI) ? (toK > fromK ? 1 : -1) : (toI > fromI ? 1 : -1);
int distance = (fromI == toI) ? abs(toK - fromK) : abs(toI - fromI);
for (int i = 1; i < distance; i++) {
int x = (fromI == toI) ? fromI : fromI + step * i;
int y = (fromI == toI) ? fromK + step * i : fromK;
if (strcmp(map[x][y].name, "") != 0) count++;
}
if (strcmp(toChess.name, "") == 0) {
return (count == 0);
} else {
return (count == 1);
}
}
第八步檢驗兵的走法 else if (strcmp(fromChess.name, "卒") == 0 || strcmp(fromChess.name, "兵") == 0) {
int direction = (fromChess.type == 'B') ? 1 : -1;
if (!fromChess.flag) {
if (toI != fromI + direction || toK != fromK) return false;
if ((fromChess.type == 'B' && toI >= 5) || (fromChess.type == 'R' && toI <= 4)) {
map[fromI][fromK].flag = true;
}
} else {
bool valid = false;
if (toI == fromI + direction && toK == fromK) valid = true;
if (toI == fromI && abs(toK - fromK) == 1) valid = true;
return valid;
}
return true;
}
綜上,我們將CheckMove() 函數插入游戲控制函數中,當且僅當移動合法時可對棋子進行移動。
接下來定義一個將軍狀態檢測函數。即當一方走棋后,檢驗接下來走棋一方是否處于被將軍狀態。
bool CheckGeneral() {
POINT generalPos = { -1,1 };
char targetType = isRedTurn ? 'B' : 'R';
const char* generalName = (targetType == 'B') ? "將" : "帥";
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL; ++j) {
if (map[i][j].type == targetType &&
strcmp(map[i][j].name, generalName) == 0) {
generalPos.x = j;
generalPos.y = i;
break;
}
}
}
char enemyType = isRedTurn ? 'R' : 'B';
for (int i = 0; i < ROW; ++i) {
for (int j = 0; j < COL; ++j) {
if (map[i][j].type == enemyType &&
strcmp(map[i][j].name, "") != 0) {
struct Chess temp = map[generalPos.y][generalPos.x];
strcpy(map[generalPos.y][generalPos.x].name, map[i][j].name);
bool canAttack = CheckMove(i, j, generalPos.y, generalPos.x);
map[generalPos.y][generalPos.x] = temp;
if (canAttack) return true;
}
}
}
return false;
}
接下來我們進行勝利條件判斷。
當將帥面對面或者將帥被吃時,游戲結束。
bool CheckWin() {
bool redExist = false, blackExist = false;
POINT redGeneral = { -1, -1 }, blackGeneral = { -1, -1 };
for (int i = 0; i < ROW; i++) {
for (int k = 0; k < COL; k++) {
if (strcmp(map[i][k].name, "帥") == 0) {
redExist = true;
redGeneral.x = k;
redGeneral.y = i;
}
if (strcmp(map[i][k].name, "將") == 0) {
blackExist = true;
blackGeneral.x = k;
blackGeneral.y = i;
}
}
}
if (!redExist) {
MessageBox(GetHWnd(), "黑方勝利!", "游戲結束", MB_OK);
return true;
}
if (!blackExist) {
MessageBox(GetHWnd(), "紅方勝利!", "游戲結束", MB_OK);
return true;
}
if (redGeneral.x == blackGeneral.x) {
int minY = (redGeneral.y < blackGeneral.y) ? redGeneral.y : blackGeneral.y;
int maxY = (redGeneral.y > blackGeneral.y) ? redGeneral.y : blackGeneral.y;
bool hasBlock = false;
for (int y = minY + 1; y < maxY; y++) {
if (strcmp(map[y][redGeneral.x].name, "") != 0) {
hasBlock = true;
break;
}
}
if (!hasBlock) {
const char* winner = isRedTurn ? "黑方" : "紅方";
char message[50];
sprintf(message, "%s 勝利!將帥對面!", winner);
MessageBox(GetHWnd(), message, "游戲結束", MB_OK);
return true;
}
}
return false;
}
綜上,我們將這些函數插入游戲控制函數內即可。
添加背景音樂、走棋音效等
這一步很簡單,直接給出代碼。
MCI_OPEN_PARMS openBGM;
DWORD bgmld;
bool isMusicPlaying = false;
void SoundInit() {
mciSendString("open \"./sounds/bgm1.wav\" type mpegvideo alias bgm", NULL, 0, NULL);
mciSendString("open \"./sounds/click.wav\" alias click", NULL, 0, NULL);
mciSendString("open \"./sounds/move.wav\" alias move", NULL, 0, NULL);
mciSendString("open \"./sounds/eat.wav\" alias eat", NULL, 0, NULL);
mciSendString("open \"./sounds/check.wav\" alias check", NULL, 0, NULL);
}
void PlayBGM() {
mciSendString("play bgm repeat", NULL, 0, NULL);
mciSendString("setaudio bgm volume to 1000", NULL, 0, NULL);
isMusicPlaying = true;
}
void PlaySoundEffect(const char* alias) {
char cmd[50];
sprintf(cmd, "play %s from 0", alias);
mciSendString(cmd, NULL, 0, NULL);
}
添加紅黑雙方計時功能
我們先看此步驟完成后的結果。

這里我們只是實現最簡單的計時功能,即雙方步時60秒,總時長10分鐘。
首先我們需要定義時間結構體包括總時間和當前步時。
struct Timer {
int totalTime;
int stepTime;
}redTimer, blackTimer;
DWORD lastUpdateTime = 0;
const int INIT_TOTAL_TIME = 600;
const int INIT_STEP_TIME = 60;
接著我們在游戲初始化函數中添加初始化計時器。
void GameInit(){
redTimer.totalTime = INIT_TOTAL_TIME;
redTimer.stepTime = INIT_STEP_TIME;
blackTimer.totalTime = INIT_TOTAL_TIME;
blackTimer.stepTime = INIT_STEP_TIME;
lastUpdateTime = GetTickCount();
}
在游戲控制函數中添加雙方交換時步時的重置
void GameControl(){
if (isRedTurn) {
redTimer.stepTime = INIT_STEP_TIME;
}
else {
blackTimer.stepTime = INIT_STEP_TIME;
}
}
然后我們在游戲繪制函數中將時間顯示在棋盤右側。
void GameGraw(){
settextstyle(20, 0, "楷體");
settextcolor(BLACK);
settextstyle(20, 0, "楷體");
settextcolor(BLACK);
char redTimeStr1[50], redTimeStr2[50],blackTimeStr1[50], blackTimeStr2[50];
sprintf(redTimeStr1, "紅方: 總 %02d:%02d",
redTimer.totalTime / 60, redTimer.totalTime % 60);
sprintf(redTimeStr2, " 步時: %02d",redTimer.stepTime);
sprintf(blackTimeStr1, "黑方: 總 %02d:%02d",
blackTimer.totalTime / 60, blackTimer.totalTime % 60);
sprintf(blackTimeStr2, " 步時: %02d", blackTimer.stepTime);
outtextxy(650, 650, redTimeStr1);
outtextxy(650, 675, redTimeStr2);
outtextxy(650, 50, blackTimeStr1);
outtextxy(650, 75, blackTimeStr2);
}
最后,在主函數中實現對時間的更新。
再結合之前的函數,我們寫出主函數即可實現中國象棋的最簡化版。
int main() {
initgraph(800, 800, SHOWCONSOLE);
SoundInit();
PlayBGM();
GameInit();
while (true) {
GameControl();
DWORD currentTime = GetTickCount();
DWORD elapsed = currentTime - lastUpdateTime;
if (elapsed >= 1000) {
int seconds = elapsed / 1000;
if (isRedTurn) {
redTimer.totalTime -= seconds;
redTimer.stepTime -= seconds;
}
else {
blackTimer.totalTime -= seconds;
blackTimer.stepTime -= seconds;
}
lastUpdateTime = currentTime;
if (redTimer.totalTime <= 0 || redTimer.stepTime <= 0) {
MessageBox(GetHWnd(), "紅方超時,黑方勝利!", "游戲結束", MB_OK);
exit(0);
}
if (blackTimer.totalTime <= 0 || blackTimer.stepTime <= 0) {
MessageBox(GetHWnd(), "黑方超時,紅方勝利!", "游戲結束", MB_OK);
exit(0);
}
}
BeginBatchDraw();
GameDraw();
EndBatchDraw();
}
EndBatchDraw();
closegraph();
mciSendString("close all", NULL, 0, NULL);
return 0;
}
到這里中國象棋的基本功能我們就實現了。由于這是本作者的第一個項目有點小激動,于是準備先進性項目打包處理。
這里使用傳統打包方式,使用Microsoft Visual Studio Installer Projects打包。 - 安裝擴展插件
- 在VS中安裝Microsoft Visual Studio Installer Projects擴展
- 操作路徑:擴展 > 管理擴展 > 搜索安裝 > 重啟VS
- 創建安裝項目
- 右鍵解決方案>添加>新建項目
- 搜索選擇"setup Project"模板
- 配置項目名稱
- 配置安裝內容
- 主程序添加:右鍵Application Folder>Add>項目輸出>選擇主輸出
- 資源文件處理:將"sound"音效文件夾拖入Application Folder(確保所有依賴的DLL被包含)
- 配置快捷方式
- 右鍵Application>Add>文件>選擇所需的快捷方式圖案.ico文件(圖片轉換成.ico格式可以通過PS轉換,需要提前配置插件)
- 右鍵主輸出>創建快捷方式
- 將快捷方式拖入User's Desktop和User's Programs Menu
- 右鍵快捷方式>屬性窗口>Icon>Browse>Browse>選擇Application Folder文件中的ico文件>OK
- 處理依賴項
- 右鍵Application Folder>Add>文件>找到vcredist_x64.exe(路徑:VS安裝目錄\VC\Redist\MSVC\版本號)
- 生成安裝包
- 右鍵安裝項目>生成
- 在輸出目錄即可獲取Setup.exe和.msi文件
轉自https://www.cnblogs.com/Yygz314/p/18899387
該文章在 2025/6/2 9:25:04 編輯過
|
|