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`:当前文件夹相对于内部存储根目录的路径,用于构建文件的完整相对路径。
- **核心逻辑**:
1. 调用 `$FolderObject.Items()` 获取文件夹内所有子项(文件和子文件夹)。
2. 遍历每个子项:
- 构建当前项的完整相对路径 `$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` 对象
```powershell
$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. 定位到"此电脑"文件夹
```powershell
$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`
```powershell
$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 筛选便携设备
```powershell
$portableDevices = $myComputer.Items() | Where-Object { $_.Type -eq "$tp" }
if (-not $portableDevices) {
Write-Host "未找到任何 MTP 设备,请连接手机并解锁。"
exit
}
```
**解释:**
- 使用用户输入的 `$tp` 字符串筛选出所有 Type 匹配的项目(理论上应该只有便携设备)。
- 若没有匹配项,提示并退出。
- **问题**:
- 如果用户输错一个字符(如"便抉设备"),就会匹配失败。
- 未考虑多个便携设备的情况(会保留所有匹配项,后续仍可能出错)。
---
### 6. 再次列出设备并让用户输入设备的 `Name`
```powershell
$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 筛选特定设备
```powershell
$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. 进入设备内部并列出存储区域
```powershell
$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 选择内部存储区域
```powershell
$internalStorage = $storageAreas | Where-Object { $_.Name -eq "$nme" }
if (-not $internalStorage) {
Write-Host "未找到内部存储区域,可用的区域有:"
$storageAreas | foreach-object -process {$_} | format-list -property Name,Type
exit
}
```
**解释:**
- 使用精确匹配(`-eq`)查找用户输入的存储区名称。
- 若未找到,显示可用区域并退出。
- **缺点**:用户必须完全正确输入名称(包括大小写、空格)。如果名称是"内部共享存储空间",输成"内部共享存储"就会失败。
---
### 10. 获取内部存储根目录并递归列出所有文件
```powershell
$storageFolder = $internalStorage.GetFolder()
Write-Host "内部存储所有文件(递归):"
Get-MTPItemsRecursive -FolderObject $storageFolder
```
**解释:**
- `$internalStorage.GetFolder()` 获得内部存储的根 `Folder` 对象。
- 调用递归函数,输出所有文件的相对路径(每行一个)。
- **最终输出**:手机内部存储中的所有文件路径(相对于存储根目录)。
---
## 三、脚本的总体评价
### 优点
- 无需安装 ADB 或开启 USB 调试,纯 Windows 原生 MTP 支持。
- 提供了交互式选择过程,适应不同手机的不同设备名称和存储区名称。
### 缺点与改进建议
1. **过度依赖人工输入**
- 用户需要手动输入 `Type`、`Name` 两次,极易出错。
- **改进**:脚本应自动识别 `Type -eq "便携设备"`,并让用户从列表中选择编号,而不是手动输入字符串。
2. **无错误处理**
- 未检查 `$device.GetFolder()` 是否返回 `$null`(手机未解锁或断开时可能失败)。
- 递归函数未处理无效的 `$FolderObject`。
- **改进**:增加 `if ($null -eq $deviceFolder) { exit }` 等检查。
3. **性能问题**
- 递归遍历整个手机存储可能非常慢(MTP 协议本身低效)。
- **改进**:增加进度提示或允许用户指定子目录(如仅扫描 `DCIM`)。
4. **输出格式**
- 仅输出文件路径,没有区分不同文件夹,输出量大时难以阅读。
- **改进**:允许输出到文件(`> filelist.txt`)或同时显示文件夹路径。
5. **递归深度风险**
- PowerShell 默认递归深度无限制,但 MTP 文件夹层级一般较浅(< 50),风险较低。
---
## 四、使用示例
假设手机已通过 USB 连接并选择"传输文件"模式,运行脚本:
1. 显示"此电脑"下所有项目(本地磁盘 C:、D:、便携设备等)。
2. 用户输入便携设备的 `Type`(例如"便携设备")。
3. 显示所有便携设备列表(如"Redmi Note 10"、"SD 卡")。
4. 用户输入设备 `Name`(例如"Redmi")。
5. 显示设备内的存储区域("内部共享存储空间"、"SD 卡")。
6. 用户输入内部存储的 `Name`(例如"内部共享存储空间")。
7. 脚本开始递归输出所有文件路径,如:
```
DCIM/Camera/IMG_001.jpg
DCIM/Camera/IMG_002.jpg
Download/app.apk
...
```
---
## 五、总结
该脚本是一个交互式的 MTP 文件列表工具,通过 COM 接口访问手机存储。虽然可以实现基本功能,但交互方式原始、易错,且缺乏健壮的错误处理。更适合作为学习 PowerShell COM 对象和 MTP 协议的参考示例,实际生产环境中建议改用 ADB 或成熟的第三方工具。