Skip to content

Instantly share code, notes, and snippets.

@cveld
Last active June 23, 2024 20:19
PowerShell that crawls your Android usb folder structure

Crawl your Android device attached via usb with PowerShell

The PowerShell script Crawl_Android.ps1 is capable of crawling your Android device when it is attached via usb. Just execute Crawl_Android.ps1 and select the device from the menu.

Limited file details

What I found out is that the Shell Namespace API does not provide a lot of details of the file. It is limited to only a couple of fields that are relevant for the Windows Explorer GUI. Strangely, a byte-oriented filesize field is not part of it. It only contains a summarized version, e.g. 4 kB or 10 MB. After futher investigation, I noticed that Windows Explorer has to make a local temporary copy to get all the details in order to populate the information pane. That's why most operations are so slow!

# http://blogs.technet.com/b/heyscriptingguy/archive/2013/04/26/use-powershell-to-work-with-windows-explorer.aspx
$o = New-Object -com Shell.Application
$folder = $o.NameSpace(0x11)
# https://msdn.microsoft.com/en-us/library/windows/desktop/bb774096(v=vs.85).aspx
# ShellSpecialFolderConstants.ssfDRIVES == 0x11
$items = $folder.Items()
for ($i= 0; $i -lt $items.Count; $i++) {
write-output ([string]$i + ": " + $items.Item($i).Name)
}
$choice = Read-Host "Make your choice"
$android = $items.Item([int]$choice)
$root = $android.GetFolder()
# FolderItem versus FolderItems
$maxdepth = Read-Host "Max depth"
Function Write-Items($item, $depth, $maxdepth) {
if ($depth -ge $maxdepth) {
return;
}
$indent = ""
for ($i = 0; $i -lt $depth; $i++) {
$indent += " ";
}
#write-output ($indent + "Name: " + $item.name)
if ($item.Title) {
$hash = @{
Name = $item.Title
Size = $null #$item.ParentFolder.GetDetailsOf($item, 2)
Modified = $null #$item.ParentFolder.GetDetailsOf($item, 3)
Parent = $item.ParentFolder.Title
Level = $depth
}
$Object = New-Object PSObject -Property $hash
write-output $object
} else {
$hash = @{
Name = $item.Name
Size = $item.Parent.GetDetailsOf($item, 2)
Modified = $item.Parent.GetDetailsOf($item, 3)
Parent = $item.Parent.Title
Level = $depth
}
$Object = New-Object PSObject -Property $hash
Write-Output $object
}
if ($item.Count -gt 0) {
# $item is a folder with its own items
for ($i = 0; $i -lt $item.Count) {
$item2 = $item.item($i)
Write-Items $item2 $depth+1 $maxdepth
}
}
else {
if ($item.Items) {
$items = $item.Items()
if ($items.Count -gt 0) {
foreach ($i in $items) {
if ($i.IsFolder) {
$folder = $i.GetFolder()
Write-Items $folder ($depth+1) $maxdepth
}
else {
Write-Items $i ($depth+1) $maxdepth
}
}
}
}
else {
# .Count == 0 and no Items present
# we don't need to do anything further here
}
}
}
Write-Items $root 0 $maxdepth | Out-GridView
@rmacmorran
Copy link

I think you need to change
$android.GetFolder()
to
$android.GetFolder
It's a property, not a method. (though its name implies otherwise)

@toto1515
Copy link

hello,
really good job.
Please can you help me ?
I am trying to copy files from ssfDRIVES to my computer.
It's a file on 'MY PC\Bebop 2\Media" (a drone) that i want to transfert to my computer
Using $item.path in your script, i found the path of the file is :
::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\?\usb#vid_19cf&pid_5038#pi040436aa7j016983#{6ac27878-a6fa-4155-ba85-f98f491d4f33}\SID-{10001,,7695343616}{00530071-0048-0006-0600-1D007A0794D7}
if i try to use copy-item, a get error because PS can't find "c:\Users\mylogin::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"

Thanks in advance for your help

Here is my code :

clear
Import-Module BitsTransfer


$o = New-Object -com Shell.Application
$folder = $o.NameSpace(0x11)
 write-output ($folder)

$items = $folder.Items()
for ($i= 0; $i -lt $items.Count; $i++) {
    write-output ([string]$i + ": " + $items.Item($i).Name)
	if ($items.Item($i).Name -eq "Bebop 2") {
	$choice = $i
	}
}
$android = $items.Item([int]$choice)
$root = $android.GetFolder
write-output ($root)

$maxdepth = 10
$arraytest = @()

Function showarray($array)
{
Write-Output "----------------------"
	for ($i=0;$i -lt $array.Length;$i++)
	{
		Write-Output "boucle : $i"
		Write-Output "Source=" $array[$i][0]
		Write-Output "Dest=" $array[$i][1]
		Write-Output "--------"
	}
}

Function copyfiles($array)
{
for ($i=0;$i -lt $array.Length;$i++)
	{
	Copy-Item -Path $array[$i][0] -dest $array[$i][1]
	#Copy-File $array[$i][0] $array[$i][1]
}
}

function Copy-File {
    param([string]$from, [string]$to)

    try {
        $job = Start-BitsTransfer -Source $from -Destination $to `
                   -Description "Moving: $from => $to" `
                   -DisplayName "Backup" -Asynchronous

        # Start stopwatch
        $sw = [System.Diagnostics.Stopwatch]::StartNew()
        Write-Progress -Activity "Connecting..."

        while ($job.JobState.ToString() -ne "Transferred") {
            switch ($job.JobState.ToString()) {
                "Connecting" {
                    break
                }
                "Transferring" {
                    $pctcomp = ($job.BytesTransferred / $job.BytesTotal) * 100
                    $elapsed = ($sw.elapsedmilliseconds.ToString()) / 1000

                    if ($elapsed -eq 0) {
                        $xferrate = 0.0
                    } else {
                        $xferrate = (($job.BytesTransferred / $elapsed) / 1mb);
                    }

                    if ($job.BytesTransferred % 1mb -eq 0) {
                        if ($pctcomp -gt 0) {
                            $secsleft = ((($elapsed / $pctcomp) * 100) - $elapsed)
                        } else {
                            $secsleft = 0
                        }

                        Write-Progress -Activity ("Copying file '" + ($PathName.Split("\") | Select -last 1) + "' @ " + "{0:n2}" -f $xferrate + "MB/s") `
                                       -PercentComplete $pctcomp `
                                       -SecondsRemaining $secsleft
                    }
                    break
                }
                "Transferred" {
                    break
                }
                Default {
                    throw $job.JobState.ToString() + " unexpected BITS state."
                }
            }
        }

        $sw.Stop()
        $sw.Reset()
    } finally {
        Complete-BitsTransfer -BitsJob $job
        Write-Progress -Activity "Completed" -Completed
    }
}

Function Write-Items($item, $depth, $maxdepth) {

    if ($depth -ge $maxdepth) {
        return;
    }
    $indent = ""
    for ($i = 0; $i -lt $depth; $i++) {
        $indent += "   ";
    }
    if ($item.Title) {
        $hash = @{
            Name = $item.Title
            Size = $null #$item.ParentFolder.GetDetailsOf($item, 2)
            Modified = $null #$item.ParentFolder.GetDetailsOf($item, 3)
            Parent = $item.ParentFolder.Title
            Level = $depth
			Path = $item.Path
			
        }
        $Object = New-Object PSObject -Property $hash            
        write-output $object
    } else {
        $hash = @{
            Name = $item.Name
            Size = $item.Parent.GetDetailsOf($item, 2)
            Modified = $item.Parent.GetDetailsOf($item, 3)
            Parent = $item.Parent.Title
            Level = $depth
			Path = $item.Path
        }

		$sourcepath = $item.path
		$destpath = "G:\tempo\" + $item.Name
		Write-Output $item.path | clip
		Write-Output "sourcepath=" $sourcepath
		Write-Output "destpath=" $destpath
		$script:arraytest+=,($sourcepath,$destpath)
        #$Object = New-Object PSObject -Property $hash            
        #Write-Output $object
    }
    if ($item.Count -gt 0) {
        # $item is a folder with its own items
        for ($i = 0; $i -lt $item.Count) {
            $item2 = $item.item($i)
            Write-Items $item2 $depth+1 $maxdepth
        }

    }
    else { 
        if ($item.Items) {
            $items = $item.Items()
            if ($items.Count -gt 0) {
                foreach ($i in $items) {
                    if ($i.IsFolder) {
                        $folder = $i.GetFolder
                        Write-Items $folder ($depth+1) $maxdepth
                    }
                    else {                    
                        Write-Items $i ($depth+1) $maxdepth
                    }
                }
            }
        }
        else {
            # .Count == 0 and no Items present
            # we don't need to do anything further here
        }
    }
}

#Write-Items $root 0 $maxdepth | Out-GridView
Write-Items $root 0 $maxdepth

showarray $arraytest
#$arraytest
copyfiles $arraytest

@mgbeda
Copy link

mgbeda commented Jun 23, 2024

Thank you! This works. I did have to change $android.GetFolder() to $android.GetFolder in two places. Thanks to you both.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment