IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 获取硬盘和卷或分区相关信息(容量,ID,卷标名字等) -> 正文阅读

[C++知识库]获取硬盘和卷或分区相关信息(容量,ID,卷标名字等)

1、枚举所有硬盘

这里主要借助了setup API,这些API主要是NT4.0之后提供的一些用于操作设备的API。

枚举所有硬盘借助了SetupDiGetClassDevs与SetupDiEnumDeviceInterfaces和 SetupDiGetDeviceInterfaceDetail这三个API。

#pragma once


#ifndef DEVICE_DISK_H
#define DEVICE_DISK_H
#include <Windows.h>
#include <Setupapi.h>
#pragma comment( lib, "setupapi.lib" )
#define DevCount 1024
struct DevInterfaceDetaillArray {
?? ?DWORD szCount;
?? ?PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData_[DevCount];
};

int GetALLDISK(struct DevInterfaceDetaillArray* DevInterfaceDetailArr_);

int FreeALLDISK(struct DevInterfaceDetaillArray* DevInterfaceDetailArr_);

#endif
?

#include "DeviceDisk.h"

#include <iostream>
#include <string>
using namespace std;


//函数调用出错一定要记得回收内存,否则会内存泄漏。
//切记要与FreeALLDISK 成对使用。

int GetALLDISK(struct DevInterfaceDetaillArray* DevInterfaceDetailArr_) {

? ? HDEVINFO diskClassDevices;
? ? GUID diskClassDeviceInterfaceGuid = GUID_DEVINTERFACE_DISK;
? ? SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
? ? PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData;
? ? DWORD requiredSize;
? ? DWORD deviceIndex;


? ? //
? ? // Get the handle to the device information set for installed
? ? // disk class devices. Returns only devices that are currently
? ? // present in the system and have an enabled disk device
? ? // interface.
? ? //
? ? diskClassDevices = SetupDiGetClassDevs(&diskClassDeviceInterfaceGuid,
? ? ? ? NULL,
? ? ? ? NULL,
? ? ? ? DIGCF_PRESENT |
? ? ? ? DIGCF_DEVICEINTERFACE);
? ??
? ? if (INVALID_HANDLE_VALUE == diskClassDevices) {
? ? ? ? return -1;
? ? }


? ? ZeroMemory(&deviceInterfaceData, sizeof(SP_DEVICE_INTERFACE_DATA));
? ? deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
? ? deviceIndex = 0;

? ? while (SetupDiEnumDeviceInterfaces(diskClassDevices,
? ? ? ? NULL,
? ? ? ? &diskClassDeviceInterfaceGuid,
? ? ? ? deviceIndex,
? ? ? ? &deviceInterfaceData)) {


? ? ? ? SetupDiGetDeviceInterfaceDetail(diskClassDevices,
? ? ? ? ? ? &deviceInterfaceData,
? ? ? ? ? ? NULL,
? ? ? ? ? ? 0,
? ? ? ? ? ? &requiredSize,
? ? ? ? ? ? NULL);
? ? ? ? ? ? //回收掉临时内存
? ? ? ? if (!(ERROR_INSUFFICIENT_BUFFER == GetLastError())) {
? ? ? ? ? ? wprintf_s(L"122\n"); //正常情况下,就是获得122错误码,咱也不知道windows在干啥。
? ? ? ? ? ? if (INVALID_HANDLE_VALUE != diskClassDevices) {
? ? ? ? ? ? ? ? SetupDiDestroyDeviceInfoList(diskClassDevices);
? ? ? ? ? ? }
? ? ? ? ? ? return -1;
? ? ? ? }


? ? ? ? deviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(requiredSize);


? ? ? ? ZeroMemory(deviceInterfaceDetailData, requiredSize);
? ? ? ? deviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

? ? ? ? if (!SetupDiGetDeviceInterfaceDetail(diskClassDevices,
? ? ? ? ? ? &deviceInterfaceData,
? ? ? ? ? ? deviceInterfaceDetailData,
? ? ? ? ? ? requiredSize,
? ? ? ? ? ? NULL,
? ? ? ? ? ? NULL)) {
? ? ? ? ? ? //没有将此次的deviceInterfaceDetailData 内存返回到DevInterfaceDetailArr_中,所以在返回之前必须释放掉,防止内存泄漏。
? ? ? ? ? ? free(deviceInterfaceDetailData);
? ? ? ? ? ? //回收掉临时内存
? ? ? ? ? ? if (INVALID_HANDLE_VALUE != diskClassDevices) {
? ? ? ? ? ? ? ? SetupDiDestroyDeviceInfoList(diskClassDevices);
? ? ? ? ? ? }
? ? ? ? ? ? return -1;
? ? ? ? }
? ? ? ? ? ??
? ? ? ? DevInterfaceDetailArr_->deviceInterfaceDetailData_[deviceIndex] = deviceInterfaceDetailData;
? ? ? ? ++deviceIndex;
? ? }

? ? DWORD Err_Code = GetLastError();

? ? DevInterfaceDetailArr_->szCount = deviceIndex;

? ? //返回之前回收临时内存
? ? if (INVALID_HANDLE_VALUE != diskClassDevices) {
? ? ? ? SetupDiDestroyDeviceInfoList(diskClassDevices);
? ? }
? ? if (ERROR_NO_MORE_ITEMS == Err_Code) {
? ? ? ? //正常退出while的状态码:259,no more data availible。
? ? ? ? return 0;
? ? }
? ? return -2; //0正常执行成功,-1循环内部执行出错,-2执行出错退出循环。
}


int FreeALLDISK(struct DevInterfaceDetaillArray* DevInterfaceDetailArr_) {
? ? for (int i = 0; i < DevInterfaceDetailArr_->szCount; i++) {
? ? ? ? free(DevInterfaceDetailArr_->deviceInterfaceDetailData_[i]);
? ? }
? ? return 0;
}

2、获取硬盘信息(容量、制造商,销售商,版本,硬盘号(即硬盘在系统中显示的序号)等)

核心就是通过createfile打开硬盘设备,获得一个硬盘设备相关的处理句柄。

然后通过DeviceIoControl与对应的控制码来获取硬盘相关信息。

一个使用了这种类似方法的项目地址:GitHub - comefromezero/efiboot: a efiboot tool on windows

下面的代码只是示例,并不能直接运行。

??if (0 != GetALLDISK(&DevIn)) {
? ? ? ? wprintf_s(L"调用GetALLDISK出错!");
? ? ? ? FreeALLDISK(&DevIn);
? ? ? ? return -1;
? ? }
? ? HANDLE hdisk = INVALID_HANDLE_VALUE ;
? ? PSTORAGE_DEVICE_DESCRIPTOR pDevDesc;
? ? STORAGE_PROPERTY_QUERY Query;
? ? DWORD dwOutBytes;


? ? Query.PropertyId = StorageDeviceProperty;
? ? Query.QueryType = PropertyStandardQuery;
? ??
? ? pDevDesc = (PSTORAGE_DEVICE_DESCRIPTOR)new BYTE[sizeof(STORAGE_DEVICE_DESCRIPTOR) + 512 - 1];
? ??
? ? ZeroMemory(pDevDesc, sizeof(STORAGE_DEVICE_DESCRIPTOR) + 512 - 1);
? ? pDevDesc->Size = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 512 - 1;
? ? //wprintf_s(L"%d", pDevDesc->Size);
? ??
? ? //struct EFI_Disk_Info efi_disk[1024] = { 0 };
? ? //g_efi_disk = efi_disk;

? ? GET_LENGTH_INFORMATION disk_len = { 0 };
? ? STORAGE_DEVICE_NUMBER diskNumber = { 0 };
? ? DWORD sizePar = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + sizeof(PARTITION_INFORMATION_EX) * 128;
? ? PDRIVE_LAYOUT_INFORMATION_EX ParTable = (PDRIVE_LAYOUT_INFORMATION_EX)new BYTE[sizePar];
? ? ZeroMemory(ParTable, sizePar);
? ? int devIndex = 0;

? ? TCHAR szPathName[MAX_PATH + 1] = { 0 };

? ? for (devIndex = 0; devIndex < DevIn.szCount; devIndex++) {
? ? ? ? wprintf_s(L"DiskInfo:%s\n", DevIn.deviceInterfaceDetailData_[devIndex]->DevicePath);
? ? ? ? hdisk = CreateFile(DevIn.deviceInterfaceDetailData_[devIndex]->DevicePath,?
? ? ? ? ? ? GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,?
? ? ? ? ? ? NULL,
? ? ? ? ? ? OPEN_EXISTING,?
? ? ? ? ? ? FILE_ATTRIBUTE_NORMAL, NULL);
? ? ? ? if (INVALID_HANDLE_VALUE == hdisk) {
? ? ? ? ? ? wprintf_s(L"createfile error! error_code:%d\n", GetLastError());
? ? ? ? ? ? return -1;
? ? ? ? }
? ? ? ? if (!DeviceIoControl(hdisk,
? ? ? ? ? ? IOCTL_STORAGE_QUERY_PROPERTY,
? ? ? ? ? ? &Query, sizeof(STORAGE_PROPERTY_QUERY),
? ? ? ? ? ? pDevDesc, pDevDesc->Size,
? ? ? ? ? ? &dwOutBytes,
? ? ? ? ? ? NULL)
? ? ? ? ? ? ){?
? ? ? ? ? ? wprintf_s(L"DeviceIoControl Error! line:238 Error_Code:%d %d\n", GetLastError(),dwOutBytes);
? ? ? ? ? ??
? ? ? ? ? ??
? ? ? ? ? ??
? ? ? ? }
? ? ? ? if (!DeviceIoControl(hdisk, IOCTL_DISK_GET_LENGTH_INFO,
? ? ? ? ? ? NULL,
? ? ? ? ? ? 0,
? ? ? ? ? ? &disk_len, sizeof(disk_len), &dwOutBytes, NULL)) {

? ? ? ? ? ? wprintf_s(L"DeviceIoControl Error! line:248 Error_Code:%d %d\n", GetLastError(), dwOutBytes); //122是正常的。

? ? ? ? }

? ? ? ? if (!DeviceIoControl(hdisk,
? ? ? ? ? ? IOCTL_STORAGE_GET_DEVICE_NUMBER,
? ? ? ? ? ? NULL,
? ? ? ? ? ? 0,
? ? ? ? ? ? &diskNumber,
? ? ? ? ? ? sizeof(STORAGE_DEVICE_NUMBER),
? ? ? ? ? ? &dwOutBytes,
? ? ? ? ? ? NULL)) {

? ? ? ? ? ? wprintf_s(L"DeviceIoControl Error! line:272 Error_Code:%d %d\n", GetLastError(), dwOutBytes);

? ? ? ? }
? ? ? ? dwOutBytes = 0;
? ? ? ? if (!DeviceIoControl(hdisk,
? ? ? ? ? ? IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
? ? ? ? ? ? NULL,
? ? ? ? ? ? 0,
? ? ? ? ? ? ParTable,
? ? ? ? ? ? sizePar,
? ? ? ? ? ? &dwOutBytes,
? ? ? ? ? ? NULL)) {

? ? ? ? ? ? wprintf_s(L"DeviceIoControl Error! line:285 Error_Code:%d %d\n", GetLastError(), dwOutBytes);

? ? ? ? }

? ? ? ? g_efi_disk[devIndex] = (struct EFI_Disk_Info*)new (struct EFI_Disk_Info);
? ? ? ? CloseHandle(hdisk);
? ? ? ? hdisk = INVALID_HANDLE_VALUE;
? ? ? ? //wprintf_s(L"%d %d\n", dwOutBytes, pDevDesc->Size);
? ? ? ? char* p_char_string = (char*)pDevDesc;
? ? ? ? stringstream sz_buffer;
? ? ? ? g_efi_disk[devIndex]->productor = std::string(&p_char_string[pDevDesc->ProductIdOffset]);
? ? ? ? g_efi_disk[devIndex]->version = std::string(&p_char_string[pDevDesc->ProductRevisionOffset]);
? ? ? ? g_efi_disk[devIndex]->vendor = std::string(&p_char_string[pDevDesc->VendorIdOffset]);
? ? ? ? int buffer_disk_len = disk_len.Length.QuadPart / 1024 / 1024 / 1024;
? ? ? ? sz_buffer << buffer_disk_len << "GB";
? ? ? ? g_efi_disk[devIndex]->length = sz_buffer.str();
? ? ? ? g_efi_disk[devIndex]->number = std::to_string(diskNumber.DeviceNumber);
? ? ? ? printf_s("Productor:%s\n", g_efi_disk[devIndex]->productor.c_str());
? ? ? ? printf_s("Version:%s\n", g_efi_disk[devIndex]->version.c_str());
? ? ? ? printf_s("Vendor:%s\n", g_efi_disk[devIndex]->vendor.c_str());
? ? ? ? printf_s("Length:%s\n", g_efi_disk[devIndex]->length.c_str());
? ? ? ? printf_s("Number:%s\n", g_efi_disk[devIndex]->number.c_str());

? ? ? ? wprintf_s(L"===================================================================================\n");
? ? ? ? wprintf_s(L"ParTable:\n");
? ? ? ? wprintf_s(L"ParTableType:%d\n",ParTable->PartitionStyle);
? ? ? ? wprintf_s(L"ParCount:%d\n", ParTable->PartitionCount);
? ? ? ??
? ? ? ? wprintf_s(L"Par0:\nParNum:%d\nParName:%s\n", ParTable->PartitionEntry[0].PartitionNumber,ParTable->PartitionEntry[0].Gpt.Name);
? ? ? ? wprintf_s(L"PatLength:%dMB\n", ParTable->PartitionEntry[0].PartitionLength.QuadPart / 1024 / 1024);
? ? ? ? wprintf_s(L"ParType:{%04x-%02x-%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionType.Data1,
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionType.Data2,
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionType.Data3,
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionType.Data4[0],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionType.Data4[1],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionType.Data4[2],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionType.Data4[3],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionType.Data4[4],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionType.Data4[5],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionType.Data4[6],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionType.Data4[7]);
? ? ? ? wprintf_s(L"ParGUID:{%04x-%02x-%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data1,
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data2,
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data3,
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[0],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[1],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[2],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[3],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[4],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[5],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[6],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[7]);
? ? ? ??
? ? ? ? wprintf_s(L"返回的字节数:%d\n", dwOutBytes);
? ? ? ? //ParTable = { 0 };
? ? ? ? wprintf_s(L"===================================================================================\n");
? ? ? ? wsprintf(szPathName, L"\\\\\?\\Volume{%04x-%02x-%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\\",
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data1,
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data2,
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data3,
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[0],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[1],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[2],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[3],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[4],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[5],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[6],
? ? ? ? ? ? ParTable->PartitionEntry[0].Gpt.PartitionId.Data4[7]);
? ? ? ? wprintf_s(L"%s\n", szPathName);
? ? }
? ? delete pDevDesc;
? ? FreeALLDISK(&DevIn);

?3、获取卷或分区相关信息(卷标名字和文件系统格式等)

核心API就是GetVolumeInfomation来获得对应的信息。

一个使用该办法的项目地址:

GitHub - comefromezero/efiboot: a efiboot tool on windows

以下示例不能直接运行。

if (0 == GetVolumeInformation(ParName, VolumeName, 1024, NULL, NULL, NULL, FileSysName, 1024)) {
wprintf_s(L"Line:%d Err_Code:%d\n",__LINE__, GetLastError());
return -1;
}

4、获取 volume所在硬盘的硬盘号

有了硬盘号就能通过createfile创建一个硬盘设备的句柄,然后利用DeviceIoControl获取硬盘相关信息。

一个使用该办法的项目地址:

GitHub - comefromezero/efiboot: a efiboot tool on windows

以下示例不能直接运行。


? ? //获取分区所在硬盘


? ? TCHAR Buf_Par_Name[1024] = { 0 };
? ? memcpy(Buf_Par_Name, ParName, wstrlen(ParName)-4); //构建分区设备,用于createfile打开分区。
? ? //wprintf_s(L" ? ?Line:%d ParName:%s\n",__LINE__, Buf_Par_Name);
? ? HANDLE h_vol = INVALID_HANDLE_VALUE;
? ? h_vol = CreateFile(Buf_Par_Name,
? ? ? ? GENERIC_READ,
? ? ? ? FILE_SHARE_READ | FILE_SHARE_WRITE,
? ? ? ? NULL,
? ? ? ? OPEN_EXISTING,
? ? ? ? FILE_ATTRIBUTE_NORMAL, NULL);

? ? if (INVALID_HANDLE_VALUE == h_vol) {
? ? ? ? wprintf_s(L"Line:%d Err_Code:%d\n",__LINE__, GetLastError());
? ? ? ? return -1;
? ? }

? ? DWORD dwOutBytes = 0;
? ? PVOLUME_DISK_EXTENTS vol_disk_info = (PVOLUME_DISK_EXTENTS)new BYTE[sizeof(VOLUME_DISK_EXTENTS) + sizeof(DISK_EXTENT) * 10];
? ? ZeroMemory(vol_disk_info, sizeof(VOLUME_DISK_EXTENTS) + sizeof(DISK_EXTENT) * 10);
? ? if (!DeviceIoControl(h_vol,
? ? ? ? IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
? ? ? ? NULL, 0,
? ? ? ? vol_disk_info, sizeof(VOLUME_DISK_EXTENTS) + sizeof(DISK_EXTENT) * 10,
? ? ? ? &dwOutBytes,
? ? ? ? NULL)
? ? ? ? ) {

? ? ? ? wprintf_s(L"DeviceIoControl Error! line:%d Error_Code:%d %d\n",__LINE__, GetLastError(), dwOutBytes);

? ? }

其中VOLUME_DISK_EXTENTS结构体中就保存了硬盘号。

5、获取一个硬盘中所有的分区

?使用DeviceIoControl和控制码IOCTL_DISK_GET_DRIVE_LAYOUT_EX枚举出该硬盘的所有的分区入口项,然后就可以知道该硬盘上所有的分区数量以及其相关信息了。

6、枚举出所有的volume信息

FindFirstVolume与FindNextVolume这两个API可以枚举出所有的分区路径,然后通过GetVolumeInfomation这个API就能够获得对应的信息了。

这里有一个问题,在容量这个问题上,GetDiskFreeSpaceA这个获取的是簇的数量,这就有一个对齐的问题,这个API获得的volume容量可能与分区的实际容量不符,因为分区的时候没有文件系统,但是格式化为某个文件系统之后,文件系统对分区容量进行管理,所以出现GetDiskFreeSpaceA容量可能与分区的实际容量不符的问题,这需要注意一下。

系统中显示的volume容量与GetDiskFreeSpaceA容量一致。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-02-16 12:52:35  更:2022-02-16 12:53:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:44:53-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码