Skip to content

Commit ced1ea5

Browse files
authored
Add files via upload
1 parent 65c8ef6 commit ced1ea5

9 files changed

Lines changed: 1111 additions & 0 deletions

README_ZH.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# VolumeAssignController
2+
3+
这是一个简单的程序,帮助你一键为磁盘卷分配卷标与取消分配。这个程序基于 `diskpart.exe`
4+
5+
**这个项目不是MucheXD的计划内项目,因此可能存在许多潜在的问题,~~与不堪入目的码风~~**
6+
7+
## 使用说明
8+
9+
### 配置
10+
11+
程序配置保存在 `config.mtd` 中(你可以在程序运行目录下找到它)。使用任意文本编辑器(建议使用 JSON 支持的编辑器)即可打开它。程序配置遵循 JSON 格式,具体编写参见下例:
12+
13+
```json
14+
{
15+
"configs": [
16+
{
17+
"name": "default",
18+
"disk": 0,
19+
"volume": 0,
20+
"char": "Z"
21+
}
22+
],
23+
"default": 0
24+
}
25+
```
26+
27+
`configs` 是一个数据组,包含所有的子配置以供稍后在程序运行时快速切换。所有的子配置对象包含 `name` `disk` `volume` `char` 四个键值对,下面列出它们的作用:
28+
29+
| key | value | description |
30+
| ------ | ------ | -------------------------------------------------- |
31+
| name | string | 显示名称 |
32+
| disk | int | 磁盘索引 [关于索引](#%E5%85%B3%E4%BA%8E%E7%B4%A2%E5%BC%95) |
33+
| volume | int | 卷索引 [关于索引](#%E5%85%B3%E4%BA%8E%E7%B4%A2%E5%BC%95) |
34+
| char | string | 目标分配盘符。如果此项缺省,则会使用最小的可用字母。 |
35+
36+
`default` 规定默认使用的子配置。
37+
38+
`disableWarning` 禁用磁盘保护警告。如果你尝试操作 C 盘,则会触发警告。不建议关闭该警告(除非你的系统盘盘符不为 C)。
39+
40+
### 分配 & 取消分配
41+
42+
简单的点击 `Assign``Unassign` 按钮即可!现行磁盘状态将显示在其上。
43+
44+
### 选择子配置
45+
46+
在下拉框中选择配置好的子配置,相关信息将显示在下方。
47+
48+
## 其它
49+
50+
### 声明
51+
52+
操作磁盘卷有时会导致一些问题,尤其是当有程序正在使用这些卷时。请务必自己小心使用!永远不要删除系统盘的盘符!**MucheXD不对任何故障或文件丢失负责。**
53+
54+
### 关于索引
55+
56+
请自行获取目标的索引!下面是基本方法:
57+
58+
1. 运行 `cmd.exe` (管理员模式下)
59+
60+
2. 运行命令 `diskpart`
61+
62+
3. 运行命令 `list disk` 并找到目标磁盘的索引
63+
64+
4. 运行命令 `select disk [目标磁盘索引]` 定位目标磁盘
65+
66+
5. 运行命令 `list volume` 获取目标卷的索引
67+
68+
### 建议的运行环境
69+
70+
- Windows 10 (x64) / Windows 11 (x64)
71+
72+
- Microsoft Visual C++ 2015-2022 Redistributable x64
73+
74+
### Copyright and License
75+
76+
This program uses the following compilers or support libraries
77+
78+
- Visual Studio 2022
79+
80+
- QT 6.5
81+
82+
This program is under the LGPL license.
83+
84+
For detailed license content, please refer to its corresponding official website.

Screenshot.png

22.5 KB
Loading

VolumeSHController.cpp

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
#include "VolumeSHController.h"
2+
3+
VolumeSHController::VolumeSHController(QWidget *parent)
4+
: QMainWindow(parent)
5+
{
6+
ui.setupUi(this);
7+
connect(ui.cb_configChoose, &QComboBox::activated,
8+
this, &VolumeSHController::changeConfig);
9+
connect(ui.btn_assign, &QPushButton::clicked,
10+
this, &VolumeSHController::assignVolume);
11+
connect(ui.btn_editConfig, &QPushButton::clicked,
12+
this, &VolumeSHController::openConfig);
13+
readConfig();
14+
refreshUi();
15+
runDiskpart();
16+
}
17+
18+
VolumeSHController::~VolumeSHController()
19+
{
20+
diskpartProcess->kill();
21+
diskpartProcess->close();
22+
}
23+
24+
void VolumeSHController::readConfig()
25+
{
26+
QFile configFile;
27+
configFile.setFileName("config.mtd");
28+
if (!configFile.open(QIODevice::ReadWrite))
29+
{
30+
QMessageBox::critical(this, "ERROR", "Cannot open config file");
31+
exit(-1);
32+
}
33+
QJsonDocument jsonDoc;
34+
QJsonParseError jsonErr;
35+
jsonDoc = jsonDoc.fromJson(configFile.readAll(), &jsonErr);
36+
configFile.close();
37+
if (jsonErr.error != QJsonParseError::NoError)
38+
{
39+
if (QMessageBox::critical(this, "ERROR", "Illegal config, create new?",
40+
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)
41+
{
42+
buildDefaultConfig();
43+
saveConfig();
44+
}
45+
exit(-1);
46+
}
47+
defaultConfigIndex = jsonDoc.object().value("default").toInt();
48+
disableWarning = jsonDoc.object().value("disableWarning").toBool(false);
49+
QJsonArray configArray = jsonDoc.object().value("configs").toArray();
50+
51+
for (auto nConfig : configArray)
52+
{
53+
const QJsonObject nObj = nConfig.toObject();
54+
ConfigData cfgData;
55+
cfgData.configName = nObj.value("name").toString();
56+
cfgData.useChar = nObj.value("char").toString();
57+
if (cfgData.useChar.isEmpty() || !cfgData.useChar.at(0).isLetter())
58+
cfgData.useChar = "";
59+
cfgData.diskIndex = nObj.value("disk").toInt();
60+
cfgData.volumeIndex = nObj.value("volume").toInt();
61+
configs.push_back(cfgData);
62+
}
63+
}
64+
65+
void VolumeSHController::refreshUi()
66+
{
67+
for (auto n : configs)
68+
{
69+
ui.cb_configChoose->addItem(n.configName);
70+
}
71+
if (defaultConfigIndex >= ui.cb_configChoose->count())
72+
{
73+
QMessageBox::warning(this, "WARNING", "Illegal default config, set to 0.");
74+
defaultConfigIndex = 0;
75+
}
76+
ui.cb_configChoose->setCurrentIndex(defaultConfigIndex);
77+
updateIndexs();
78+
}
79+
80+
void VolumeSHController::updateIndexs()
81+
{
82+
int a = ui.cb_configChoose->currentIndex();
83+
ConfigData currentConfig = configs.at(ui.cb_configChoose->currentIndex());
84+
ui.l_diskIndex->setText(QString("%1").arg(currentConfig.diskIndex));
85+
ui.l_VolumeIndex->setText(QString("%1").arg(currentConfig.volumeIndex));
86+
if(!currentConfig.useChar.isEmpty())
87+
ui.l_diskLetter->setText(QString("%1").arg(currentConfig.useChar.at(0)));
88+
else
89+
ui.l_diskLetter->setText("×");
90+
}
91+
92+
void VolumeSHController::buildDefaultConfig()
93+
{
94+
configs.clear();
95+
ConfigData cfgData;
96+
cfgData.configName = "default";
97+
cfgData.diskIndex = 0;
98+
cfgData.volumeIndex = 0;
99+
defaultConfigIndex = 0;
100+
configs.push_back(cfgData);
101+
}
102+
103+
void VolumeSHController::saveConfig()
104+
{
105+
QFile configFile;
106+
configFile.setFileName("config.mtd");
107+
configFile.open(QIODevice::ReadWrite);
108+
QJsonArray configArray;
109+
for (auto n : configs)
110+
{
111+
QJsonObject nObj;
112+
nObj.insert("name",n.configName);
113+
nObj.insert("disk", n.diskIndex);
114+
nObj.insert("volume", n.volumeIndex);
115+
configArray.append(nObj);
116+
}
117+
QJsonObject rootObj;
118+
rootObj.insert("configs", configArray);
119+
rootObj.insert("default", defaultConfigIndex);
120+
QJsonDocument jsonDoc;
121+
jsonDoc.setObject(rootObj);
122+
configFile.write(jsonDoc.toJson());
123+
configFile.close();
124+
}
125+
126+
void VolumeSHController::changeConfig()
127+
{
128+
if (ui.cb_configChoose->count() == 0)
129+
return;
130+
ConfigData currentConfig = configs.at(ui.cb_configChoose->currentIndex());
131+
diskpartProcess->write(QString("select disk %1\n").arg(currentConfig.diskIndex).toStdString().c_str());
132+
diskpartProcess->write(QString("select volume %1\n").arg(currentConfig.volumeIndex).toStdString().c_str());
133+
ui.lb_isAssigned->setProperty("isValid", false);
134+
ui.lb_isAssigned->setText("?");
135+
ui.lb_isAssigned->setStyleSheet(ui.lb_isAssigned->styleSheet());
136+
updateIndexs();
137+
checkAssignment();
138+
}
139+
140+
void VolumeSHController::openConfig()
141+
{
142+
if (ui.btn_editConfig->text() == "Edit Config")
143+
{
144+
QProcess explorerProcess;
145+
explorerProcess.setWorkingDirectory(QApplication::applicationDirPath());
146+
explorerProcess.startDetached("explorer.exe", QStringList("config.mtd"));
147+
ui.btn_editConfig->setProperty("isHighlighted",true);
148+
ui.btn_editConfig->setStyleSheet(ui.lb_isAssigned->styleSheet());
149+
ui.btn_editConfig->setText("Reload Config");
150+
}
151+
else
152+
{
153+
configs.clear();
154+
ui.cb_configChoose->clear();
155+
readConfig();
156+
refreshUi();
157+
ui.btn_editConfig->setProperty("isHighlighted", false);
158+
ui.btn_editConfig->setStyleSheet(ui.lb_isAssigned->styleSheet());
159+
ui.btn_editConfig->setText("Edit Config");
160+
}
161+
}
162+
163+
void VolumeSHController::runDiskpart()
164+
{
165+
diskpartProcess = new QProcess(this);
166+
connect(diskpartProcess, &QProcess::readyReadStandardOutput,
167+
this, &VolumeSHController::readOutput);
168+
connect(diskpartProcess, &QProcess::finished,
169+
this, &VolumeSHController::diskpartFinished);
170+
connect(diskpartProcess, &QProcess::started,
171+
this, &VolumeSHController::diskpartStarted);
172+
diskpartProcess->start("diskpart.exe");
173+
}
174+
175+
void VolumeSHController::readOutput()
176+
{
177+
QString input = QString::fromLocal8Bit(diskpartProcess->readAllStandardOutput());
178+
qDebug() << input;
179+
QRegularExpression regExp("(?<=\\s{5})[0-9]\\s{5}");
180+
QRegularExpressionMatch regExpMatch = regExp.match(input);
181+
if (!regExpMatch.hasMatch())
182+
{
183+
return;
184+
}
185+
regExp.setPattern("(?<=\\s{5}[0-9]\\s{5})[A-Z]");
186+
regExpMatch = regExp.match(input);
187+
if (regExpMatch.hasMatch())
188+
{
189+
setIsAssign(true);
190+
ui.lb_isAssigned->setText(regExpMatch.captured());
191+
}
192+
else
193+
{
194+
setIsAssign(false);
195+
ui.lb_isAssigned->setText("×");
196+
}
197+
}
198+
199+
void VolumeSHController::diskpartFinished()
200+
{
201+
ui.statusBar->setProperty("isConnected", false);
202+
ui.statusBar->showMessage("Diskpart disconnected");
203+
}
204+
205+
void VolumeSHController::diskpartStarted()
206+
{
207+
ui.statusBar->setProperty("isConnected", true);
208+
ui.statusBar->showMessage("Diskpart connected");
209+
changeConfig();
210+
}
211+
212+
void VolumeSHController::assignVolume()
213+
{
214+
if (ui.btn_assign->text() == "Assign")
215+
{
216+
ConfigData currentConfig = configs.at(ui.cb_configChoose->currentIndex());
217+
ui.statusBar->showMessage(QString("Assign to volume %1").arg(currentConfig.volumeIndex));
218+
if (!currentConfig.useChar.isEmpty())
219+
diskpartProcess->write(QString("assign LETTER=%1\n").arg(currentConfig.useChar.at(0)).toLocal8Bit());
220+
else
221+
diskpartProcess->write("assign\n");
222+
checkAssignment();
223+
}
224+
else
225+
{
226+
if (ui.lb_isAssigned->text() == "C" && !disableWarning)
227+
{
228+
QMessageBox::warning(this, "Dangerous operation",
229+
"Most people use \"C\" as system partition.\nUnassigning system partition may be dangerous.\n\nUse [\"disableWarning\": true] in config root to prevent this warning.");
230+
return;
231+
}
232+
ConfigData currentConfig = configs.at(ui.cb_configChoose->currentIndex());
233+
ui.statusBar->showMessage(QString("Remove volume %1").arg(currentConfig.volumeIndex));
234+
diskpartProcess->write("remove\n");
235+
checkAssignment();
236+
}
237+
}
238+
239+
void VolumeSHController::checkAssignment()
240+
{
241+
diskpartProcess->write(QString("detail PARTITION\n").toStdString().c_str());
242+
}
243+
244+
void VolumeSHController::setIsAssign(bool isAssigned)
245+
{
246+
ui.lb_isAssigned->setProperty("isValid", true);
247+
ui.lb_isAssigned->setProperty("isTrue", isAssigned);
248+
ui.lb_isAssigned->setStyleSheet(ui.lb_isAssigned->styleSheet());
249+
if (isAssigned)
250+
{
251+
ui.btn_assign->setText("Unassign");
252+
}
253+
else
254+
{
255+
ui.btn_assign->setText("Assign");
256+
}
257+
}

VolumeSHController.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#pragma once
2+
3+
#include <QtWidgets/QMainWindow>
4+
#include <QFile>
5+
#include <QMessageBox>
6+
#include <QJsonDocument>
7+
#include <QJsonObject>
8+
#include <QJsonArray>
9+
#include <QProcess>
10+
#include <QRegularExpression>
11+
#include "ui_VolumeSHController.h"
12+
13+
struct ConfigData
14+
{
15+
QString configName;
16+
QString useChar;
17+
int32_t diskIndex;
18+
int32_t volumeIndex;
19+
};
20+
21+
class VolumeSHController : public QMainWindow
22+
{
23+
Q_OBJECT
24+
25+
public:
26+
VolumeSHController(QWidget *parent = nullptr);
27+
~VolumeSHController();
28+
29+
private:
30+
std::vector<ConfigData> configs;
31+
int32_t defaultConfigIndex;
32+
bool disableWarning = false;
33+
QProcess* diskpartProcess;
34+
35+
void readConfig();
36+
void refreshUi();
37+
void updateIndexs();
38+
void buildDefaultConfig();
39+
void saveConfig();
40+
void changeConfig();
41+
void openConfig();
42+
43+
void runDiskpart();
44+
void readOutput();
45+
void diskpartFinished();
46+
void diskpartStarted();
47+
void assignVolume();
48+
void checkAssignment();
49+
void setIsAssign(bool isAssigned);
50+
51+
private:
52+
Ui::VolumeSHControllerClass ui;
53+
};

0 commit comments

Comments
 (0)