dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.28k stars 4.74k forks source link

Environment.GetFolderPath() doesn't support all KNOWNFOLDERID's #554

Open sba923 opened 4 years ago

sba923 commented 4 years ago

(Redirecting from (https://github.com/PowerShell/PowerShell/issues/11240))

It should be possible to retrieve the location of all "special folders" instead of hardcoding them (or assuming they are just subfolders of the user's profile folder).

As of today, Environment.GetFolderPath() (which reportedly relies on SHGetKnownFolderPath()) doesn't support all KNOWNFOLDERID's. Here's a few of the ones that are not supported:

FOLDERID_Downloads
FOLDERID_DocumentsLibrary
FOLDERID_CameraRoll

This piece of code

using System;

namespace GetFolderPath
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach (Environment.SpecialFolder folder in (Environment.SpecialFolder[]) Enum.GetValues(typeof(Environment.SpecialFolder)))
            {
                Console.WriteLine("{0,-30}{1}", folder, Environment.GetFolderPath(folder));
            }
        }
    }
}

outputs only this subset of folder paths:

Desktop                       C:\Users\joe\Desktop
Programs                      C:\Users\joe\AppData\Roaming\Microsoft\Windows\Start Menu\Programs
MyDocuments                   C:\Users\joe\Documents
MyDocuments                   C:\Users\joe\Documents
Favorites                     C:\Users\joe\Favorites
Startup                       C:\Users\joe\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
Recent                        C:\Users\joe\AppData\Roaming\Microsoft\Windows\Recent
SendTo                        C:\Users\joe\AppData\Roaming\Microsoft\Windows\SendTo
StartMenu                     C:\Users\joe\AppData\Roaming\Microsoft\Windows\Start Menu
MyMusic                       C:\Users\joe\Music
MyVideos                      C:\Users\joe\Videos
DesktopDirectory              C:\Users\joe\Desktop
MyComputer                    
NetworkShortcuts              C:\Users\joe\AppData\Roaming\Microsoft\Windows\Network Shortcuts
Fonts                         C:\windows\Fonts
Templates                     C:\Users\joe\AppData\Roaming\Microsoft\Windows\Templates
CommonStartMenu               C:\ProgramData\Microsoft\Windows\Start Menu
CommonPrograms                C:\ProgramData\Microsoft\Windows\Start Menu\Programs
CommonStartup                 C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
CommonDesktopDirectory        C:\Users\Public\Desktop
ApplicationData               C:\Users\joe\AppData\Roaming
PrinterShortcuts              
LocalApplicationData          C:\Users\joe\AppData\Local
InternetCache                 C:\Users\joe\AppData\Local\Microsoft\Windows\INetCache
Cookies                       C:\Users\joe\AppData\Local\Microsoft\Windows\INetCookies
History                       C:\Users\joe\AppData\Local\Microsoft\Windows\History
CommonApplicationData         C:\ProgramData
Windows                       C:\windows
System                        C:\windows\system32
ProgramFiles                  C:\Program Files
MyPictures                    C:\Users\joe\Pictures
UserProfile                   C:\Users\joe
SystemX86                     C:\windows\SysWOW64
ProgramFilesX86               C:\Program Files (x86)
CommonProgramFiles            C:\Program Files\Common Files
CommonProgramFilesX86         C:\Program Files (x86)\Common Files
CommonTemplates               C:\ProgramData\Microsoft\Windows\Templates
CommonDocuments               C:\Users\Public\Documents
CommonAdminTools              C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools
AdminTools                    C:\Users\joe\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Administrative Tools
CommonMusic                   C:\Users\Public\Music
CommonPictures                C:\Users\Public\Pictures
CommonVideos                  C:\Users\Public\Videos
Resources                     C:\windows\resources
LocalizedResources            
CommonOemLinks                
CDBurning                     C:\Users\joe\AppData\Local\Microsoft\Windows\Burn\Burn
danmoseley commented 4 years ago

Looks like this is because on .NET Framework, Environment.GetFolderPath() uses ShGetFolderPath which cannot give you the downloads folder (it has no CSIDL value). On .NET Core, perhaps to accommodate long paths, we already changed to ShGetKnownFolderPath. This takes KNOWNFOLDERID and Downloads has one of these.

This is essentially an API request for new members of Environment.SpecialFolders. Someone interested in this should make a list of proposed additions. Note that they ideally all have some meaning on Mac and Linux. We should not add "CameraRoll" unless it's actually important. Downloads seems pretty basic though.

ChadSimmons commented 4 years ago

If it's documented, I recommend including it since determining importance is a fool's errand. For example, CameraRoll might not be important to many, but since it is included in One Drive Known Folder Move it is of interest to people trying to use that. Definitely include

danmoseley commented 4 years ago

If it's documented, I recommend including it since determining importance is a fool's errand.

Maybe if Windows was the only relevant OS, but it is not: as the list gets longer there are more entries without clear mappings on various Linux distros, macOS, Android, iOS, etc. We can take guesses or return empty string but that potentially makes life harder for folks trying to target those OS, who must now figure out which members of the enumeration are going to be useful. Eg., what should CameraRoll return on Ubuntu? Maybe there is no mapping, or several possible mappings. If I'm writing a photo editor app, should I offer it in my "open file" picker?

We are already returning empty string in quite a few cases for Unix https://github.com/dotnet/runtime/blob/2d4acbde200dba78794d9baf8cc69c170267fde0/src/libraries/System.Private.CoreLib/src/System/Environment.GetFolderPathCore.Unix.cs#L143

And for example on about 30 cases on iOS https://github.com/mono/mono/blob/c5b88ec4f323f2bdb7c7d0a595ece28dae66579c/mcs/class/corlib/System/Environment.iOS.cs#L91

sba923 commented 4 years ago

Sure, Windows is not the only targeted OS. I'm a great advocate of non-Windows .NET Core / PowerShell.

But OTOH, not all code / scripts is / are written to be xplat!

I hate that I have to write weird code (mainly on Windows) to determine where the Downloads folder resides...

danmoseley commented 4 years ago

It would be good to improve the doc so it has a big table showing what works on what platform. It looks like it's not been updated for cross plat.

https://docs.microsoft.com/en-us/dotnet/api/system.environment.specialfolder?view=netcore-3.1

sba923 commented 4 years ago

Yes! And that big table would have Windows-only entries ;-) Or "not MacOS" entries. Whatever.

jeffhandley commented 3 years ago

Per #57133, we should consider the Saved Games folder as a candidate too.

AtomicBlom commented 3 years ago

In the interest of pushing this forward I've put together a spreadsheet of all the KnownFolderIds that are missing, as well as whether they're usable on *nix or mac.

What I don't have is a good way to collaborate on these, as I'm not a mac or *nix user so I'm not sure what the sane defaults would be.

(read-only view of the spreadsheet) https://1drv.ms/x/s!Aqd0GRdPyzYske0aoWS8kUiTamIu7A?e=bBfntf

One thing that struck me as a bit odd while I was working on this is that MyComputer is a valid SpecialFolder. This is the only KnownFolderId which is marked as a Virtual folder, and I don't think it will EVER return a valid path on Windows.

RayKoopa commented 2 years ago

This seems to be the newest discussion on this topic. I got my feet wet and tried implementing the "Downloads" and "Public" folder with these paths, given they seem requested the most:

Folder Linux1 macOS2 iOS2 Windows3
Downloads XDG_DOWNLOAD_DIR or <home>/Downloads <home>/Downloads <personal>/Downloads FOLDERID_Downloads
Public XDG_PUBLICSHARE_DIR or <home>/Public <home>/Public - FOLDERID_Public4

1 Judging from existing code and https://wiki.archlinux.org/title/XDG_user_directories 2 Judging from existing code and screenshots (I do not own Apple hardware) 3 https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid 4 This path is the same for all users on Windows. Unlike Linux and macOS, there is no user-specific Public folder on Windows.

I noticed some things while doing so:

I did not tackle the "Saved Games" folder as only Windows has it, and the discussion went into the direction of focusing folders that have viable cross-platform paths first.

If you are interested, I can write an API Proposal to add these two folders. Wanted to hear some feedback on this existing issue first :-)

danmoseley commented 2 years ago

I named the new enum value for the "Downloads" folder MyDownloads in the style of MyDocuments, MyPictures, and MyVideos - I recall Windows 7 reintroducing the "My" prefix after it was dropped in Vista, but I don't remember if this was the case for the Downloads folder too.

On my Windows 7 VM, only Documents, Music, Pictures, and Videos have the My prefix. (Matches https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid)

danmoseley commented 2 years ago

. Are XDG variables even defined in macOS, the preprocessor block below seems to state the opposite

I don't have a Mac but I don't believe so. If Macs have a "movies" folder then we should implement it to return that for SpecialFolders.MyVideos. That's just a bug fix -- https://github.com/dotnet/runtime/issues/68323

BTW, I found https://github.com/dotnet/runtime/issues/25577 and https://github.com/dotnet/runtime/issues/63214 -- lots of discussion here, as no maintainers like me have sat down and organized.

I wouldn't bother to add to the VB enum as you point out.

It would be great to open an API proposal for those two, and any others you believe (based on all these discussions) seem to make general sense. Perhaps we can end up closing all these issues..

danmoseley commented 2 years ago

BTW @RayKoopa thanks for reviving this. And @AtomicBlom for the spreadsheet, which I pasted here in case it gets lost. https://gist.github.com/danmoseley/edb3708c1bacc2998890e44e7ea4e4d4

RayKoopa commented 2 years ago

Thank you for checking on the folder names and other discussions.

I'm still sorting my notes a bit from the many comments written throughout the years, but have updated mine above (which I slightly use as my blueprint for an API proposal):

For simplicity, at the moment I'd prefer to only focus on the "missing user folders" common to Linux / macOS / Windows, e.g. "Downloads" and "Public" (despite the latter not being a user-specific folder on Windows, but I think that is neglectible since everyone has access to it by default).

sba923 commented 2 years ago

BTW @RayKoopa thanks for reviving this. And @AtomicBlom for the spreadsheet, which I pasted here in case it gets lost. https://gist.github.com/danmoseley/edb3708c1bacc2998890e44e7ea4e4d4

Thanks for saving the spreadsheet.

Shouldn't it include a column indicating what is currently supported by .NET (might even depen on version?)?

RayKoopa commented 2 years ago

Phew, I've now dug through related issues, PRs and API proposals.

They currently block me moving forward with my blueprint above as they make valid points in that extending the enum may not be a good idea. However, I cannot rate how important the points made are today:

So either

  1. the points made are discarded and the enum is extended for a quick / "short-term" solution that will probably be deprecated in the future anyway
  2. the points made are respected and the new API proposal is resurrected instead for a "long-term" solution

What do? 🤔

AtomicBlom commented 2 years ago

I would honestly like to see a short-term fix in place for at least the user folders.

I stumbled across and became invested in this task after I became aware of how few developers were making use of the "Saved Games" folder in windows, and many games scatter their saved games everywhere across local app data, roaming app data, my documents, etc. All this means that for games that don't cloud save, it's very difficult to back them all up

I wanted to make sure that my games used what I perceive to be the best location for them, and I want to promote that there is a location for saved games, However the Enum value doesn't work for non-windows platforms, so it's back to square one.

RayKoopa commented 2 years ago

I would honestly like to see a short-term fix in place for at least the user folders.

Me too. The only "real" reason against short-term extending the enum because "code assuming it maps to Windows' CSIDLs would break" is valid but exotic. What would be a use case for that? Any code doing this...

I became aware of how few developers were making use of the "Saved Games" folder in windows

However the Enum value doesn't work for non-windows platform

These two reasons actually made me exclude the "Saved Games" in an API Proposal I'd write so far, and only focus on the more commonly requested "Downloads" and "Public" first - unless maintainers are okay with "Saved Games" returning "yet another" String.Empty on Linux and macOS. Would definitely make .NET more inviting for game developers given the almost nonexistant adoption of this folder after Vista used it for Purble Place etc. :-)

Miepee commented 2 years ago

This seems to be the newest discussion on this topic. I got my feet wet and tried implementing the "Downloads" and "Public" folder with these paths, given they seem requested the most:

Just wanted to leave a quick note, that the public directory functions a little differently between Windows and Linux/MacOS. On Windows, there only exists one public folder which every user has access to. On Linux/MacOS, every user has it's own public folder that the other users can access. This should get documented, just so that developers are aware of it.

Also, this may just be a thing that's unclear due to it being short, but for the Linux assignments, it shouldn't be an "or", rather a "fallback" should the former variable not exist. If you're using ReadXdgDirectory then it's already handled for you there.

RayKoopa commented 2 years ago

Yes, I've mentioned this in the "foot"notes under the table, footnote 4 specifically. Indeed 'or' is just to save space in the table, I linked to the existing code in footnote 1.

soc commented 2 years ago

If improvements were to ship in the next version of .NET, which Windows version would be the oldest to receive that .NET version by default?

I maintain a library that starts a new process, runs PowerShell, throws

      "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8\n" +
      "Add-Type @\"\n" +
      "using System;\n" +
      "using System.Runtime.InteropServices;\n" +
      "public class Dir {\n" +
      "  [DllImport(\"shell32.dll\")]\n" +
      "  private static extern int SHGetKnownFolderPath([MarshalAs(UnmanagedType.LPStruct)] Guid rfid, uint dwFlags, IntPtr hToken, out IntPtr pszPath);\n" +
      "  public static string GetKnownFolderPath(string rfid) {\n" +
      "    IntPtr pszPath;\n" +
      "    if (SHGetKnownFolderPath(new Guid(rfid), 0, IntPtr.Zero, out pszPath) != 0) return \"\";\n" +
      "    string path = Marshal.PtrToStringUni(pszPath);\n" +
      "    Marshal.FreeCoTaskMem(pszPath);\n" +
      "    return path;\n" +
      "  }\n" +
      "}\n" +
      "\"@\n"

over the fence, hopes for the best and parses the response.

Reliability has been a consistent issue with this and a better replacement would be appreciated, but I wonder how long improvements will take until I can actually use it.

RayKoopa commented 2 years ago

If improvements were to ship in the next version of .NET, which Windows version would be the oldest to receive that .NET version by default?

According to MSDN the current .NET 6 supports Windows 7 / Server 2012 and newer. But I can't tell you if Windows 7 is planned to be dropped in .NET 7 / 8 / whenever this feature will make the cut.

That means Windows Vista and Server 2008 (R2) would be the only systems not prospering from the special folders added here, and would also be the only systems even having these folders and the API to query them.

Reliability has been a consistent issue with this and a better replacement would be appreciated, but I wonder how long improvements will take until I can actually use it.

Apparently offtopic here, but can you explain why it isn't working reliably for you? In effect, it is imported and called by .NET like you do (minus the automatic string marshaling).

soc commented 2 years ago

@RayKoopa

Windows Vista and Server 2008 (R2) would be the only systems not prospering from the special folders added here

Interesting, thanks!

can you explain why it isn't working reliably for you

I went through all issues and tagged Windows-specific issues for better visibility, hope this helps.

It ranges from the mundane "Add-Type failed because some other application messed with LIB" to hilarious things like having to downgrade PowerShell to v2.0 to evade Constrained Language Mode restrictions.

Basically 60% of the project's bugs are "Windows not working for someone", despite Windows probably only being used by a minority of users.

danmoseley commented 2 years ago

But I can't tell you if Windows 7 is planned to be dropped in .NET 7 / 8 / whenever this feature will make the cut.

I can't offer an official word, but I know of no plans to drop Windows 7 support in .NET 7 given it remains in extended support until 2023

soc commented 2 years ago

Hey @danmoseley, thanks for the info!

Just to check that we are on the same page – does this mean that on Windows 7 (and later) one can expect .NET 7 to be installed after its release?

RayKoopa commented 2 years ago

Another thing just crossed my mind... exposing the "Public" folder on Windows may not be sufficient or even meaningful.

So if SpecialFolder.Public(Directory) should be introduced, so should be the specific child folders as PublicPictures, PublicDownloads, etc.. And they would return String.Empty on Unix.

jkotas commented 2 years ago

I can't offer an official word, but I know of no plans to drop Windows 7 support in .NET 7

The list of OS versions that we expected to support for .NET 7 is documented in https://github.com/dotnet/core/blob/main/release-notes/7.0/supported-os.md

does this mean that on Windows 7 (and later) one can expect .NET 7 to be installed after its release?

.NET 5+ is not part of Windows and it is not automatically installed by Windows. You have to either install it on the machine yourself or you can bundle the runtime with the app by publishing it as self-contained.

soc commented 2 years ago

@jkotas

.NET 5+ is not part of Windows and it is not automatically installed by Windows.

Oh, welp. So improvements made in this issue won't be of any help for my use-case then. :-/

You have to either install it on the machine yourself or you can bundle the runtime with the app by publishing it as self-contained.

The library I'm maintaining is around 7kB and included with other people's applications. Bundling a whole .NET install with it is completely out of question.

It's 2022 and it's not possible to retrieve Windows' special folders in an architecture-independent way? Am I missing something?

RayKoopa commented 2 years ago

The library I'm maintaining is around 7kB and included with other people's applications. Bundling a whole .NET install with it is completely out of question.

It's 2022 and it's not possible to retrieve Windows' special folders in an architecture-independent way? Am I missing something?

.NET does not have an API to retrieve "newer" (Vista+ though) special folders like the ones mentioned here - e.g. "Downloads" and "Public" being ones you are asking for, judging from your project (my recent comment on the "Public" folder may be of interest aswell).

It will internally just call the WinAPI method SHGetKnownFolderPath that is available since Vista, just like you already do via PowerShell. The issues you described above have to do with PowerShell, not the call itself. In your case I'd recommend calling it with JNI / C, to prevent having to skip through additional execution layers (effectively layers that may cause problems).

We may want to talk about this in detail on your project's issue as it's slightly offtopic here (feel free to ping me and I add what I know :-) )

RayKoopa commented 2 years ago

Before this stales away again, should I push ahead and make an API proposal as I outlined above? To recap:

🟢 Extend SpecialFolder starting at value 0x10000 with folders commonly available cross-platform:

SpecialFolder macOS iOS Linux (with XDG) Linux Windows
Downloads ~/Downloads NSDocumentDirectory/Downloads XDG_DOWNLOAD_DIR ~/Downloads FOLDERID_Downloads
PublicDirectory ~/Public - XDG_PUBLICSHARE_DIR ~/Public FOLDERID_Public
PublicDesktop - - - - FOLDERID_PublicDesktop
PublicDocuments - - - - FOLDERID_PublicDocuments
PublicDownloads - - - - FOLDERID_PublicDownloads
PublicMusic - - - - FOLDERID_PublicMusic
PublicVideos - - - - FOLDERID_PublicVideos
SavedGames - - - - FOLDERID_SavedGames

Reasoning:

For consistency, the following two breaking changes already mentioned in https://github.com/dotnet/runtime/issues/63214 should be proposed aswell:

🟠 Change SpecialFolder.MyDocuments on Unix to point to the actual "Documents" sub folder, not the home directory:

Behavior macOS iOS Linux (with XDG) Linux
Current ~ NSDocumentDirectory ~ ~
Proposed ~/Documents NSDocumentDirectory XDG_DOCUMENTS_DIR ~/Documents

🟠 Change SpecialFolder.MyVideos to point to macOS' "Movies" folder as it does not actually have a "Videos" folder:

Behavior macOS iOS
Current ~/Videos NSDocumentDirectory/Videos
Proposed ~/Movies NSDocumentDirectory/Movies

This has already been started just recently here: https://github.com/dotnet/runtime/pull/68610. I will wait on the acceptance / merge of that PR first before continuing with the new folder proposal to prevent clashes / confusion.

danmoseley commented 2 years ago

Linking https://github.com/dotnet/runtime/pull/68610 here as well...

RayKoopa commented 2 years ago

Thanks for letting me know. I crossed out the breaking changes in my recap that are already started there and wait for the outcome of the PR / discussions first.

soc commented 2 years ago

This looks reasonable, but I want to ask for clarification regarding the public subfolders on Linux:

Are the subfolder names fixed to the values mentioned above or are the translated values of the personal download/music/document/... folders used?

Sadly those folders are locale-dependent (seemingly every OS needs to make that mistake at least once) and in my opinion using a fixed language for public subdirs would be wrong:

If XDG_DOWNLOAD_DIR was /home/me/Heruntergeladenes, then PublicDownloads should be /home/soc/Öffentlich/Heruntergeladenes, not /home/soc/Öffentlich/Downloads.

So XDG_PUBLICSHARE_DIR/Downloads should be something like XDG_PUBLICSHARE_DIR/last-path-component(XDG_DOWNLOAD_DIR) to avoid English folder names on non-English systems.

RayKoopa commented 2 years ago

Thanks for opening the discussion on that.

I included the paths returned on Unix based on the currently used fallback names, and only added them for "convenience". But I also share the decision to not return a path at all for them since they simply don't exist in Unix, and cannot be retrieved in any meaningful way:

So if you ask me, either use hardcoded English names, or not return any paths for these folders on Unix. For simplicity and comparable behavior to other folders not supported on a system, I'd even prefer not returning a path.

soc commented 2 years ago

@RayKoopa

I don't know how to query the translations like xdg-user-dirs-update does

That's exactly what ReadXdgDirectory does – there is no need for anything beyond what's already implemented.

redirecting all his sub folders to just his home when he doesn't like them

I think this is already spelled out in the spec. There is no reason why subdirs should behave differently.

expose names in a shared folder which nobody should see

That's a good point!

or not return any paths for these folders on Unix

That also sounds fine to me.

RayKoopa commented 2 years ago

That's exactly what ReadXdgDirectory does – there is no need for anything beyond what's already implemented.

ReadXdgDirectory queries a full user folder path, not the system's translation for a folder name to be appended to the public folder. As mentioned, the user folder path cannot be processed in any way for that.

Miepee commented 2 years ago

Agree here that the public subfolders should rather not return anything at all on Linux.

For consistency's sake, should MacOS not return anything for those subfolder as well then? Since they also technically do not exist there, and from what I understand from the docs they'd rather have only Users use those. Not to mention that those folders are translated in MacOS. Not via naming, but by having a .localized in the folder which the file explorer will then use to show a localized name of that folder. I.e. from Documents to Dokumente. Having suddenly English-named folders in a non-English environment would be a little weird, which could get mitigated by .NET always creating that .localized file, but that sounds like it'd create even more issues.

RayKoopa commented 2 years ago

Yeah, whenever I mentioned "Unix", I meant both macOS and Linux. And I agree, adding exceptionally much magic for determining these folder's names or getting them localized is not meaningful here.

After this discussion and my own personal reconsideration, I updated my recap to return empty paths on those systems. After all, the reason the public subfolders were added was only because they are important on Windows. :-)

EDIT: Now my last bullet "Other "special" folders (like Windows' "Saved Games" or "Public Desktop") are not included due to them having no meaningful cross-platform counterpart." looks a little dumb. I could just aswell add those folders for Windows-only now.

Miepee commented 2 years ago

Links | Stores Explorer pinned location shortcut files on < Windows 10.

Considering that Win7 is now EOL, with there only coming security updates, and everything before already being not supported, is this really worth adding?

RayKoopa commented 2 years ago

Probably not - yet Windows 7 is still in extended support, and apparently going to be supported in .NET 7. I don't know about the support periods of Windows 8 right now but would assume they're longer. According to just this it would be better to add it.

However, the only use-case I could think of is someone designing their own OpenFileDialog or wanting to display user favorites - but they'd need special logic for Windows 10 anyway as it uses a different storage for managing "Quick Access". The Links folder still exists in Windows 10 but has no real use anymore IIRC (I think I actually deleted mine completely...).

EDIT: I rated this use case as quite exotic and moved that folder to ❌.

Miepee commented 2 years ago

You also might wanna include the PublicDesktop to the big table as well, since it's just another public subfolder. NVM just remembered that PublicDesktop serves a different function

RayKoopa commented 2 years ago

You're actually right, it should be in there given "Desktop" by itself is a common user folder on all systems, which I forgot. Adjusted my comment.

Miepee commented 2 years ago
Behavior macOS iOS
Current ~/Videos ~/Videos
Proposed ~/Movies ~/Movies

68610 does not move the iOS myVideos to Movies, only does for MacOS.

I honestly have no idea about anything iOS related, but from I've seen, file system access is very limited. The current iOS bindings for example return <DocumentsFolder>/Movies <DocumentsFolder>/Videos instead.

RayKoopa commented 2 years ago

I don't have an iPhone or iOS emulator here too, so I wonder if a participant of this discussion could check for the existence of either "Movies" or "Videos" on a relatively clean device / installation.

The current iOS bindings for example return <DocumentsFolder>/Movies instead.

To me that line of code looks like ~/Videos though, I understood your comment as ~/Documents/Movies.

Miepee commented 2 years ago

To me that line of code looks like ~/Videos though, I understood your comment as ~/Documents/Movies.

Last part was a typo, meant <DocumentsFolder>/Videos instead.

Look at: Path.Combine(GetFolderPathCore(SpecialFolder.Personal, SpecialFolderOption.None), "Videos");. Quick reminder that Personal is the same as MyDocuments. Aka, this returns <DocumentsFolder> + Videos.

It should probably be still changed to <DocumentsFolder>/Movies tho, just to keep it consistent.

I also used <DocumentsFolder> here for a reason since it's very likely not in ~/Documents. Personal is called via NSDocumentDirecctory so this'll likely return the documents folder inside the sandboxed iOS app.

Miepee commented 2 years ago

Just realized, that this also means that the iOS part in your Documents table is wrong as well. The Documents entry on iOS, from what I can see, doesn't need any updating at all.

RayKoopa commented 2 years ago

Look at: Path.Combine(GetFolderPathCore(SpecialFolder.Personal, SpecialFolderOption.None), "Videos");. Quick reminder that Personal is the same as MyDocuments.

Right, I thought SpecialFolder.Personal is SpecialFolder.UserProfile (since that's what it always sounded like). Sorry about the confusion.

But I'm getting a bit more confused now, please let me know if I understood correctly:

Just realized, that this also means that the iOS part in your Documents table is wrong as well. The Documents entry on iOS, from what I can see, doesn't need any updating at all.

Understood and adjusted. Didn't expect the iOS code to be like "that" :-)

Miepee commented 2 years ago

Given NSMoviesDirectory, NSMusicDirectory etc, the user folder paths are at ~/Movies, ~/Music etc. as shown in my table above.

You need to differentiate here between iOS and MacOS. In iOS, the NSDirectories are from what I can see never actually invoked except for NSDocumentsDirectory. In the file I linked, all the user folder paths redirect to <DocumentsFolder>/<UserFolder> instead of the filesystem one, due to the way iOS apps are sandboxed. NSDocumentsDirectory is also a different path from HOME. On MacOS, while the NSDirectories are also never invoked, it (almost) returns the same directories as them. So yes.

If you fix SpecialFolder.MyDocuments&SpecialFolder.Personal to return what would be NSDocumentDirectory / ~/Documents, then the code you linked to needs fixing and should use SpecialFolder.UserProfile instead of SpecialFolder.Personal.

Nope, the way it is right now is intended. The UNIX changes do not affect iOS; iOS already invokes NSDocumentsDirectory for Personal/MyDocuments and checks HOME for UserProfile. And again, it uses NSDocumentsDirectory because of iOS apps are all sandboxed.

Otherwise you would change the path to user folders like ~/Music to ~/Documents/Music that don't exist at all, have no meaning on the platform, and break existing applications.

Sorry, I don't get quite what you're meaning with this.

If you're confused about the sandboxing and haven't looked at the doc I linked above:

For security purposes, an iOS app’s interactions with the file system are limited to the directories inside the app’s sandbox directory. [...] An app is generally prohibited from accessing or creating files outside its container directories.

In other words, no access to HOME, hence why all iOS folders instead point to their specifically designed directories.

RayKoopa commented 2 years ago

If I understood correctly, this means iOS apps have no way of accessing the ~/Music folders etc., and there's only a ~/Documents folder which is seen as the "user root folder"? Which is why the only way to retrieve a custom "Music" folder would be in ~/Documents/Music?

Miepee commented 2 years ago

NSDocumentsDirectory != ~/Documents/ for iOS. All programs on iOS are their own little folder, similar to an AppImage on Linux if you've ever dealt with those. So it points more to <wherever iOS stores apps>/MyApp/Documents/ as seen here: image

this means iOS apps have no way of accessing the ~/Music folders etc.

It seems possible to do via NSSearchPathDirectory.NSMusicDirectory (or for the Apple docs here). I assume the reason why .NET links to local sandboxed folders instead is because unless there's a specific need to, apps shouldn't just break out of their sandbox. The docs for example never even mention anything iOS filesystem related outside of the sandbox:

users of iOS devices do not have direct access to the file system and apps are expected to follow this convention.

RayKoopa commented 2 years ago

Okay, understood. Logically I'll just stay inside the sandboxed folders like .NET already does then, adding the Downloads folder like so:

return Path.Combine(GetFolderPathCore(SpecialFolder.Personal, SpecialFolderOption.None), "Downloads");

I hope my iOS columns in my recap above are now correct at least.