I'm making a script in Powershell ISE and to prevent piracy a part of the script needs to locates the file name and if it exists on the computer the script won't work. This will work cause downloading a file twice will give it a little (1).
I've google all kind of questions but I just really want to figure out the file path to a file located in downloads.
tl;dr
"$HOME\Downloads"
(New-Object -ComObject Shell.Application).NameSpace('shell:Downloads').Self.Path
"$HOME\Downloads"
assumes two things, which aren't necessarily true:
That $HOME
, which is equivalent to environment variable USERPROFILE
($env:USERPROFILE
), is the root directory for the user's documents isn't always true, namely not with roaming profiles - only "${env:HOMEDRIVE}${env:HOMEPATH}"
reliably reflects the documents folder.
More importantly, the downloads folder may have been explicitly configured to be in an arbitrary location, unrelated to the documents location
The only robust way to determine the downloads folder's location is to ask the system for it:
PowerShell, as of PowerShell (Core) 7 v7.4.x, has no PowerShell-native way of asking the system for known folder locations.
While PowerShell has virtually unlimited access to the .NET framework and can therefore use the System.Environment
type's .GetFolderPath()
method to ask for special known folders (e.g. [Environment]::GetFolderPath('Desktop')
), asking for the downloads folder, specifically, is not supported, surprisingly, as of .NET 9.
Only the WinAPI's Known Folders API allows retrieval of the designated downloads folders in a robust fashion, without relying on fixed relationships with other known folders:
In PowerShell, you can access it via the Shell.Application
COM server:
(New-Object -ComObject Shell.Application).NameSpace('shell:Downloads').Self.Path
For a list of all supported (shell:
-prefixed) folder identifiers, see this article.
[1] By naive I mean: a solution that one is understandably tempted to use, but which doesn't work in all situations.
function Get-DownloadFolderPath() {
$SHGetKnownFolderPathSignature = @'
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
public extern static int SHGetKnownFolderPath(
ref Guid folderId,
uint flags,
IntPtr token,
out IntPtr lpszProfilePath);
'@
$GetKnownFoldersType = Add-Type -MemberDefinition $SHGetKnownFolderPathSignature -Name 'GetKnownFolders' -Namespace 'SHGetKnownFolderPath' -Using "System.Text" -PassThru
$folderNameptr = [intptr]::Zero
[void]$GetKnownFoldersType::SHGetKnownFolderPath([Ref]"374DE290-123F-4565-9164-39C4925E467B", 0, 0, [ref]$folderNameptr)
$folderName = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($folderNameptr)
[System.Runtime.InteropServices.Marshal]::FreeCoTaskMem($folderNameptr)
$folderName
}
$downloadFolderPath = Get-DownloadFolderPath
write-host "Download folder path: $($downloadFolderPath)"
$downloadFolderPath = (Get-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders").PSObject.Properties["{374DE290-123F-4565-9164-39C4925E467B}"].Value
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With