Where is the "downloads" folder located
Asked Answered
S

4

20

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.

Soult answered 15/9, 2019 at 18:36 Comment(1)
Not that this has any bearing on the main thrust of your question, but what happens if the user has their web browser configured to prompt for a download location instead of auto-saving to Downloads? (I assume you're talking about browser downloads since I can't think of another application that downloads to that directory by default.) What if the user's browser doesn't resolve naming conflicts using that same (#) scheme? What if the user simply removes the anti-piracy code from your script? How does preventing multiple copies of the same file on the same computer prevent piracy, anyways?Arnettearney
T
-12

Ummm... C:/Users/your name here/Downloads

Put the name you registered your computer with as your name here

Tranquilize answered 15/9, 2019 at 18:40 Comment(10)
After Downloads put a slash then the file name and then the extension. Kind of like; C:/Users/MrVillager10/Downloads/funnyimg.pngTranquilize
The downloads folder won't always be in this location - they can be redirected via the registry: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell FoldersHorotelic
@Jacob, if he had done that, why would he be asking this question?Tranquilize
They might not have done it personally, but the script might be deployed to an environment where the downloads folder is in a different location, in which case it will fail due to a false assumption. Although I think this is an XY problem, because checking if a file exists by downloading something with the same name doesn't seem like a sound approach, especially as an anti-piracy method.Horotelic
Unfortunately PowerShell's [environment]::getfolderpath is incomplete and appears to be limited to the old CSIDL special folders. PowerShell Core calls SHGetKnownFolderPath directly, so it should be able to support FOLDERID_Downloads. It just doesn't yet, which is weird since known folders were added way back in 2006 (Vista). Since they don't fully support it, we have to manually P/Invoke SHGetKnownFolderPath and define the required known-folder GUIDs.Nolin
@Jacob: Good point, but the registry method is deprecated on Windows 10 (the registry key has a string value named !Do not use this registry key with content Use the SHGetFolderPath or SHGetKnownFolderPath function instead).Tajuanatak
@ErykSun: Good points, but you don't need to resort to P/Invoke, you can use the Shell.Application COM server from PowerShell.Tajuanatak
@Tajuanatak yes, the problem with those registry keys is that they are only created when the shell folder is accessed, and whilst for downloads it shouldn't be a problem, it's definitely not a recommended approach. See devblogs.microsoft.com/oldnewthing/20031103-00/?p=41973Horotelic
@MrVillager10: It's great that you want to contribute, but when valid criticisms are leveled against your answer and you don't address them, the answer deserves a down-vote. Ideally, though, you'd amend (or, if it isn't salvageable, delete) your answer - or, if you disagree with the criticisms - seek a shared understanding through dialogue. P.S.: Unless you specifically @-mention someone in comments on your own answer (you can only address 1person at a time), no one will be notified of your follow-up comments.Tajuanatak
Seeing as how this ignores the intent of the question and answers it literally without presenting any code, I strongly suspect this answer treats it as a user support question rather than a programming question. This is Stack Overflow, not Super User. If the question were really about finding the literal, static, not-flexible-at-all path of a folder on one specific system (which maybe it is, since somehow this became the accepted answer), then the entire question is off-topic and should be closed as such.Arnettearney
T
52

tl;dr

  • The naive[1] answer, which typically but not always works:
"$HOME\Downloads"
  • The robust answer:
(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.0.0-preview.3, 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, the designated folder for downloads is - surprisingly - NOT among them.

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.

Tajuanatak answered 16/9, 2019 at 4:36 Comment(4)
Regarding localization of known-folder directory names, since Vista (NT 6) the primary, default names are English. Localized installations of Windows in some cases use either junctions (like Unix bind mounts) or directory symbolic links (like Unix symlinks) to provide localized names at a low level in the file system, and at a high level the graphical shell shows localized names, but the preferred paths for applications and scripts to use are the common English names.Nolin
I upvoted even though I disagree on one point: I would call "$HOME\Downloads" the "quick-and-dirty" or "quick-and-not-necessarily-accurate" answer. As for the "naive answer", I'd say that's something else entirely...Arnettearney
Thanks, @BACON, but I don't see a semantic difference between "quick-and-not-necessarily-accurate" and "which typically but not always works". Is it the word "naive" that you disagree with, and, if so, why? Please see the footnote I've added, which explains the intended semantics.Tajuanatak
I, too, usually refer to a not-so-robust implementation to be the "naive approach." My point was (in jest) that, all things considered, if there is a naive answer to this question, it's certainly not this one.Arnettearney
S
1

Using Windows API:

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)"

Original version using Registry:

$downloadFolderPath = (Get-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders").PSObject.Properties["{374DE290-123F-4565-9164-39C4925E467B}"].Value
Sind answered 15/7, 2022 at 17:21 Comment(3)
This is not language-dependent.Rann
At least on Win11 this is not recommended. When I look into the registry key with get-item "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" the first entry I get is !Do not use this registry key Use the SHGetFolderPath or SHGetKnownFolderPath function instead. Win Version 23H2 (Build 22631.3447)Caphaitien
Thanks @Caphaitien . I updated the answer to use the Windows APISind
S
0

This should work os agnostic. On non Windows systems, there is no ComObject support.

$path =  Join-Path -Path "$([System.Environment]::GetFolderPath(40))" -ChildPath "Downloads"

Note that the folder "Downloads" is language dependent. .NET does not have a cross-platform call to detect this.

Swagger answered 20/2, 2023 at 16:20 Comment(0)
T
-12

Ummm... C:/Users/your name here/Downloads

Put the name you registered your computer with as your name here

Tranquilize answered 15/9, 2019 at 18:40 Comment(10)
After Downloads put a slash then the file name and then the extension. Kind of like; C:/Users/MrVillager10/Downloads/funnyimg.pngTranquilize
The downloads folder won't always be in this location - they can be redirected via the registry: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell FoldersHorotelic
@Jacob, if he had done that, why would he be asking this question?Tranquilize
They might not have done it personally, but the script might be deployed to an environment where the downloads folder is in a different location, in which case it will fail due to a false assumption. Although I think this is an XY problem, because checking if a file exists by downloading something with the same name doesn't seem like a sound approach, especially as an anti-piracy method.Horotelic
Unfortunately PowerShell's [environment]::getfolderpath is incomplete and appears to be limited to the old CSIDL special folders. PowerShell Core calls SHGetKnownFolderPath directly, so it should be able to support FOLDERID_Downloads. It just doesn't yet, which is weird since known folders were added way back in 2006 (Vista). Since they don't fully support it, we have to manually P/Invoke SHGetKnownFolderPath and define the required known-folder GUIDs.Nolin
@Jacob: Good point, but the registry method is deprecated on Windows 10 (the registry key has a string value named !Do not use this registry key with content Use the SHGetFolderPath or SHGetKnownFolderPath function instead).Tajuanatak
@ErykSun: Good points, but you don't need to resort to P/Invoke, you can use the Shell.Application COM server from PowerShell.Tajuanatak
@Tajuanatak yes, the problem with those registry keys is that they are only created when the shell folder is accessed, and whilst for downloads it shouldn't be a problem, it's definitely not a recommended approach. See devblogs.microsoft.com/oldnewthing/20031103-00/?p=41973Horotelic
@MrVillager10: It's great that you want to contribute, but when valid criticisms are leveled against your answer and you don't address them, the answer deserves a down-vote. Ideally, though, you'd amend (or, if it isn't salvageable, delete) your answer - or, if you disagree with the criticisms - seek a shared understanding through dialogue. P.S.: Unless you specifically @-mention someone in comments on your own answer (you can only address 1person at a time), no one will be notified of your follow-up comments.Tajuanatak
Seeing as how this ignores the intent of the question and answers it literally without presenting any code, I strongly suspect this answer treats it as a user support question rather than a programming question. This is Stack Overflow, not Super User. If the question were really about finding the literal, static, not-flexible-at-all path of a folder on one specific system (which maybe it is, since somehow this became the accepted answer), then the entire question is off-topic and should be closed as such.Arnettearney

© 2022 - 2024 — McMap. All rights reserved.