AI工具:读取手机系统文件工具
AI模型:Deepseek
开发:AI+人工
测试环境:鸿蒙4.2+windows11
准备:手机数据线连接电脑+USB连接方式为传输文件+手机被系统识别
保存:记事本编码ansi+所有类型+名称.ps1
运行:右键使用Powershell运行
代码:
点击查看代码
# 定义递归函数,列出 MTP 文件夹中的所有项(文件夹和文件)
function Get-MTPItemsRecursive {
param(
$FolderObject, # Shell.Folder 对象
$RelativePath = "" # 当前相对路径(用于显示)
)
$items = $FolderObject.Items()
foreach ($item in $items) {
$currentPath = if ($RelativePath) { "$RelativePath\$($item.Name)" } else { $item.Name }
if ($item.IsFolder) {
# 如果是文件夹,先显示文件夹本身(可选),然后递归进入
#Write-Host " [DIR] $currentPath"
$subFolder = $item.GetFolder()
if ($subFolder) {
Get-MTPItemsRecursive -FolderObject $subFolder -RelativePath $currentPath
}
} else {
# 如果是文件,直接显示相对路径和类型
#Write-Host " $currentPath [$($item.Type)]"
Write-Host "$currentPath"
}
}
}
# 复制MTP文件到本地的完整示例
$shell = New-Object -ComObject Shell.Application -ErrorAction Stop
if ($null -eq $shell) {
Write-Error "无法创建 Shell.Application 对象,请检查系统组件。"
exit 1
}
# 定位到"此电脑"
$myComputer = $shell.NameSpace(0x11)
if ($null -eq $myComputer -or -not $myComputer.Items()) {
Write-Error "无法获取“此电脑”文件夹。"
exit 1
}
$myComputer.Items() | foreach -process {$_} | format-list -property Name,Type
$tp = Read-Host -Prompt "请查找你的设备并输入你设备的Type字段"
# 找到便携设备
$portableDevices = $myComputer.Items() | Where-Object { $_.Type -eq "$tp" }
if (-not $portableDevices) {
Write-Host "未找到任何 MTP 设备,请连接手机并解锁。"
exit
}
$portableDevices | ForEach-Object -process { $_ } | format-list -property Name,Type
$nm = Read-Host -Prompt "请查找你的设备并输入你设备的Name字段"
# 假设手机名包含 "xxx",可根据实际情况修改
$device = $portableDevices | Where-Object { $_.Name -like "*$nm*" }
if (-not $device) {
Write-Host "未找到匹配的设备,当前设备有:"
$portableDevices | ForEach-Object -process { $_ } | format-list -property Name,Type
exit
}
Write-Host "找到设备: $($device.Name)"
# 进入设备内部
$deviceFolder = $device.GetFolder()
$storageAreas = $deviceFolder.Items()
# 找到内部存储(通常名为"内部共享存储空间")
$storageAreas | foreach-object -process {$_} | format-list -property Name,Type
$nme = Read-Host -Prompt "请查找你的设备并输入你设备的Name字段"
$internalStorage = $storageAreas | Where-Object { $_.Name -eq "$nme" }
if (-not $internalStorage) {
Write-Host "未找到内部存储区域,可用的区域有:"
$storageAreas | foreach-object -process {$_} | format-list -property Name,Type
exit
}
# 获取内部存储根目录的 Folder 对象
$storageFolder = $internalStorage.GetFolder()
Write-Host "内部存储所有文件(递归):"
Get-MTPItemsRecursive -FolderObject $storageFolder
介绍:
点击查看代码
下面详细解释 `checkMobile.ps1` 脚本的每个部分及其作用、潜在问题与改进建议。
---
## 一、脚本总体目标
该脚本利用 Windows 的 `Shell.Application` COM 对象,通过 MTP 协议访问连接的手机(或其他便携设备),并递归列出手机内部存储中的所有文件路径(不包含文件夹名,仅输出文件的相对路径)。
主要流程:
1. 定义递归遍历 MTP 文件夹的函数。
2. 获取“此电脑”下的所有项目,让用户手动识别便携设备的 `Type` 和 `Name`。
3. 定位到手机设备,再进入内部存储区域。
4. 调用递归函数输出所有文件路径。
---
## 二、代码逐段详解
### 1. 递归函数 `Get-MTPItemsRecursive`
```powershell
function Get-MTPItemsRecursive {
param(
$FolderObject, # Shell.Folder 对象
$RelativePath = "" # 当前相对路径(用于显示)
)
$items = $FolderObject.Items()
foreach ($item in $items) {
$currentPath = if ($RelativePath) { "$RelativePath\$($item.Name)" } else { $item.Name }
if ($item.IsFolder) {
# 如果是文件夹,先显示文件夹本身(可选),然后递归进入
#Write-Host " [DIR] $currentPath"
$subFolder = $item.GetFolder()
if ($subFolder) {
Get-MTPItemsRecursive -FolderObject $subFolder -RelativePath $currentPath
}
} else {
# 如果是文件,直接显示相对路径和类型
#Write-Host " $currentPath [$($item.Type)]"
Write-Host "$currentPath"
}
}
}
解释:
- 参数:
$FolderObject:一个Shell.Folder对象,代表 MTP 设备中的一个文件夹(如根目录或子目录)。$RelativePath:当前文件夹相对于内部存储根目录的路径,用于构建文件的完整相对路径。
- 核心逻辑:
- 调用
$FolderObject.Items()获取文件夹内所有子项(文件和子文件夹)。 - 遍历每个子项:
- 构建当前项的完整相对路径
$currentPath(例如DCIM/Camera/photo.jpg)。 - 如果
$item.IsFolder为$true,说明是子文件夹:- 注释掉的
Write-Host原本用于输出文件夹名,现被忽略。 - 调用
$item.GetFolder()获取子文件夹的Folder对象,然后递归调用自身。
- 注释掉的
- 如果是文件(
else分支),直接输出$currentPath。
- 构建当前项的完整相对路径
- 调用
- 输出:每行一个文件相对路径(不包含根目录名称,例如
Pictures/1.jpg,而不是内部共享存储空间/Pictures/1.jpg)。 - 缺点:
- 没有对
$FolderObject做空值检查,如果传入$null会报错。 - 递归可能非常深(手机存储可能有数十万文件),导致 PowerShell 执行缓慢或栈溢出(不过一般 MTP 文件夹深度有限)。
- 未处理访问被拒绝的文件夹(
GetFolder()可能返回$null,但代码只是跳过)。
- 没有对
2. 创建 Shell.Application 对象
$shell = New-Object -ComObject Shell.Application -ErrorAction Stop
if ($null -eq $shell) {
Write-Error "无法创建 Shell.Application 对象,请检查系统组件。"
exit 1
}
解释:
- 使用
New-Object -ComObject Shell.Application创建 Windows Shell 的 COM 对象,这是访问 MTP 设备的核心入口。 -ErrorAction Stop确保创建失败时脚本终止。- 随后检查
$shell是否为$null,若为null则报错退出。
3. 定位到“此电脑”文件夹
$myComputer = $shell.NameSpace(0x11)
if ($null -eq $myComputer -or -not $myComputer.Items()) {
Write-Error "无法获取“此电脑”文件夹。"
exit 1
}
解释:
$shell.NameSpace(0x11):参数0x11是 Windows 常量ssfDRIVES,代表“我的电脑/此电脑”虚拟文件夹。- 检查
$myComputer和其Items()方法是否可用,防止后续遍历失败。
4. 列出所有项目并让用户输入设备的 Type
$myComputer.Items() | foreach -process {$_} | format-list -property Name,Type
$tp = Read-Host -Prompt "请查找你的设备并输入你设备的Type字段"
解释:
$myComputer.Items()返回“此电脑”下所有项目(包括本地磁盘、网络位置、便携设备等)。foreach -process {$_}将每个项目向下传递(等价于ForEach-Object { $_ }),然后format-list -property Name,Type以列表形式显示每个项目的名称和类型。- 用户需从输出中找到自己的手机设备(通常 Type 为“便携设备”或“Portable Device”),并手动输入该 Type 字段的值(例如中文的“便携设备”)。
- 问题:
- 用户需要知道确切 Type 字符串,且不同语言系统可能不同。
- 直接让用户复制粘贴易出错(可能包含不可见字符)。
- 更好的做法是自动筛选
Type -eq "便携设备",但脚本选择交互式输入,增加了手动操作负担。
5. 根据用户输入的 Type 筛选便携设备
$portableDevices = $myComputer.Items() | Where-Object { $_.Type -eq "$tp" }
if (-not $portableDevices) {
Write-Host "未找到任何 MTP 设备,请连接手机并解锁。"
exit
}
解释:
- 使用用户输入的
$tp字符串筛选出所有 Type 匹配的项目(理论上应该只有便携设备)。 - 若没有匹配项,提示并退出。
- 问题:
- 如果用户输错一个字符(如“便抉设备”),就会匹配失败。
- 未考虑多个便携设备的情况(会保留所有匹配项,后续仍可能出错)。
6. 再次列出设备并让用户输入设备的 Name
$portableDevices | ForEach-Object -process { $_ } | format-list -property Name,Type
$nm = Read-Host -Prompt "请查找你的设备并输入你设备的Name字段"
解释:
- 显示所有匹配到的便携设备的
Name和Type(通常Name是设备型号,如“Redmi Note 10”)。 - 用户需根据显示的
Name再次输入设备名称(支持部分匹配,因为后续使用-like "*$nm*")。
7. 根据用户输入的 Name 筛选特定设备
$device = $portableDevices | Where-Object { $_.Name -like "*$nm*" }
if (-not $device) {
Write-Host "未找到匹配的设备,当前设备有:"
$portableDevices | ForEach-Object -process { $_ } | format-list -property Name,Type
exit
}
Write-Host "找到设备: $($device.Name)"
解释:
- 使用
-like "*$nm*"实现模糊匹配,只要设备名包含用户输入的字符串即可。 - 若匹配失败,重新显示可用设备列表并退出。
- 注意:如果
$portableDevices包含多个设备且用户输入的字符串匹配到多个,$device将包含第一个匹配项(取决于Where-Object返回的集合)。若希望精确匹配,应使用-eq或要求用户输入完整名称。
8. 进入设备内部并列出存储区域
$deviceFolder = $device.GetFolder()
$storageAreas = $deviceFolder.Items()
$storageAreas | foreach-object -process {$_} | format-list -property Name,Type
$nme = Read-Host -Prompt "请查找你的设备并输入你设备的Name字段"
解释:
$device.GetFolder()获取设备根目录的Folder对象。$storageAreas是设备下的存储区域(例如“内部共享存储空间”、“SD 卡”等)。- 再次列出这些存储区域的
Name和Type,让用户选择内部存储对应的Name。 - 潜在问题:
- 若设备只有内部存储,
$storageAreas可能只有一个项目。 - 不同手机可能名称不同(“内部存储”、“Phone”、“Internal Storage”),用户需要知道准确名称。
- 若设备只有内部存储,
9. 根据用户输入的 Name 选择内部存储区域
$internalStorage = $storageAreas | Where-Object { $_.Name -eq "$nme" }
if (-not $internalStorage) {
Write-Host "未找到内部存储区域,可用的区域有:"
$storageAreas | foreach-object -process {$_} | format-list -property Name,Type
exit
}
解释:
- 使用精确匹配(
-eq)查找用户输入的存储区名称。 - 若未找到,显示可用区域并退出。
- 缺点:用户必须完全正确输入名称(包括大小写、空格)。如果名称是“内部共享存储空间”,输成“内部共享存储”就会失败。
10. 获取内部存储根目录并递归列出所有文件
$storageFolder = $internalStorage.GetFolder()
Write-Host "内部存储所有文件(递归):"
Get-MTPItemsRecursive -FolderObject $storageFolder
解释:
$internalStorage.GetFolder()获得内部存储的根Folder对象。- 调用递归函数,输出所有文件的相对路径(每行一个)。
- 最终输出:手机内部存储中的所有文件路径(相对于存储根目录)。
三、脚本的总体评价
优点
- 无需安装 ADB 或开启 USB 调试,纯 Windows 原生 MTP 支持。
- 提供了交互式选择过程,适应不同手机的不同设备名称和存储区名称。
缺点与改进建议
-
过度依赖人工输入
- 用户需要手动输入
Type、Name两次,极易出错。 - 改进:脚本应自动识别
Type -eq "便携设备",并让用户从列表中选择编号,而不是手动输入字符串。
- 用户需要手动输入
-
无错误处理
- 未检查
$device.GetFolder()是否返回$null(手机未解锁或断开时可能失败)。 - 递归函数未处理无效的
$FolderObject。 - 改进:增加
if ($null -eq $deviceFolder) { exit }等检查。
- 未检查
-
性能问题
- 递归遍历整个手机存储可能非常慢(MTP 协议本身低效)。
- 改进:增加进度提示或允许用户指定子目录(如仅扫描
DCIM)。
-
输出格式
- 仅输出文件路径,没有区分不同文件夹,输出量大时难以阅读。
- 改进:允许输出到文件(
> filelist.txt)或同时显示文件夹路径。
-
递归深度风险
- PowerShell 默认递归深度无限制,但 MTP 文件夹层级一般较浅(< 50),风险较低。
四、使用示例
假设手机已通过 USB 连接并选择“传输文件”模式,运行脚本:
- 显示“此电脑”下所有项目(本地磁盘 C:、D:、便携设备等)。
- 用户输入便携设备的
Type(例如“便携设备”)。 - 显示所有便携设备列表(如“Redmi Note 10”、“SD 卡”)。
- 用户输入设备
Name(例如“Redmi”)。 - 显示设备内的存储区域(“内部共享存储空间”、“SD 卡”)。
- 用户输入内部存储的
Name(例如“内部共享存储空间”)。 - 脚本开始递归输出所有文件路径,如:
DCIM/Camera/IMG_001.jpg DCIM/Camera/IMG_002.jpg Download/app.apk ...
五、总结
该脚本是一个交互式的 MTP 文件列表工具,通过 COM 接口访问手机存储。虽然可以实现基本功能,但交互方式原始、易错,且缺乏健壮的错误处理。更适合作为学习 PowerShell COM 对象和 MTP 协议的参考示例,实际生产环境中建议改用 ADB 或成熟的第三方工具。
</details>

浙公网安备 33010602011771号