Как надежно скопировать элементы с помощью PowerShell на устройство mtp? - PullRequest
2 голосов
/ 11 апреля 2019

Я хочу иметь возможность копировать элементы (файлы, папки) с ПК с Windows на устройство MTP. Я хочу сделать это с помощью PowerShell для создания сценариев.

Я нашел эту тему: shell32 copyhere не работает ни в .Net, ни в сценарии powershell , но ответ там не помогает понять решение (и было дано тем же человеком, который задал этот вопрос). Ниже приведен минимальный пример кода:

param ($itemName)

$shell = New-Object -com Shell.Application

$sourceFolder = $shell.Namespace($(Get-Location).toString()).self
$item = $sourceFolder.GetFolder.Items() | where { $_.Name -eq $itemName }

$mtpDevice = $shell.NameSpace(0x11).items() | where { $_.name -eq 'DUMMY_MODEL' }
$destination = $mtpDevice.GetFolder.items() | where { $_.Name -eq 'Card' }
$destinationFolder=$shell.Namespace($destination).self

$destinationFolder.GetFolder.CopyHere($item)

$shell.open($destinationFolder)
Start-Sleep -s 1

Я предполагаю, что копируемый элемент ($ itemName) существует на компьютере с Windows. Я предполагаю, что устройство mtp в проводнике Windows отображается как «DUMMY_MODEL» и содержит пустую папку верхнего уровня «Карта».

Я ожидаю, что линия

$destinationFolder.GetFolder.CopyHere($item)

должен сделать эту работу. Но это не так. Чтобы он работал, мне нужно программно открыть окно папки назначения и использовать режим сна. Зачем? Вышеупомянутый поток говорит, что для того, чтобы завершить процесс копирования. Почему это не может закончиться, не открывая окно? Можно ли это сделать без программного открытия окна? И даже если я открою окно и засну, копирование не будет работать на 100% надежно. Почему?

1 Ответ

0 голосов
/ 11 апреля 2019

Это основано на моих ограниченных знаниях об объекте Shell.Application, который недостаточно хорошо документирован. Если кто-то знает лучше, пожалуйста, не стесняйтесь исправить.

COM-объект Shell.Application является копией оболочки Windows Explorer, которая выполняет файловые операции асинхронно. Новый поток создается для каждого действия «копировать», а экземпляр $shell управляет этими потоками и получает события - завершено / не выполнено / требуется ввод данных пользователем / и т.д. Когда скрипт завершается, $shell очищается и не может получать события. Созданные им потоки копирования будут прерываться ненормально, как если бы вы выключили компьютер во время копирования файлов с одного диска на другой.

Обратите внимание, что CopyHere не вызывает завершенное событие . Это затрудняет определение ошибки или ожидание завершения в скрипте. В идеале вы должны использовать Powershell, встроенный в Copy-Item, а не Shell, но это может быть невозможно на устройствах MTP.

Быстрое решение, вероятно, состоит в том, чтобы добавить System.Console.ReadKey(), например, связанный ответ, или увеличить продолжительность ожидания, если вы хотите запускать без присмотра.

Редактировать: Вместо ожидания вы можете подтвердить существование каждого из файлов в пути назначения: $destinationFolder.GetFolder.Items() содержит список файлов в месте назначения. С этот поток WMI также вариант, но примеры редки.

Редактировать 2: Вот простой скрипт, который копирует с жесткого диска на телефон и подтверждает, что он выполнен:

param([string]$phoneName = 'Nexus 5X', #beyond compare path: 'mtp://Nexus 5X'
    [string]$sourcePath = 'C:\Temp\',
    [string]$targetPath = '\Card\DCIM\',
    [string]$filter='(.jpg)|(.mp4)$'
)

$Shell = New-Object -ComObject Shell.Application
$PhoneObject = $shell.NameSpace(17).self.GetFolder.items() | where { $_.name -eq $phoneName } #gets the phone special folder

$SourceFolder = $Shell.NameSpace($sourcePath).self.GetFolder()
$DestFolder = $Shell.NameSpace((Join-path $PhoneObject.Path $targetPath)).self.GetFolder()

foreach($Item in $SourceFolder.Items() | ?{$_.Name -match $filter}){
    $DestFolder.CopyHere($Item)
    Do {
        $CopiedFile = $null 
        $CopiedFile = $DestFolder.Items() | ?{$_.Name -eq $Item.Name}
    }While( ($CopiedFile -eq $null) -and ($null -eq (Sleep -Milliseconds 100)) )#skip sleeping if it's already copied
    Write-Host "Copied $($item.Name)"
}
...