Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use SHGetKnownFolderPath to get path to localappdata for current user #4998

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

stevenbrix
Copy link

On Windows, it's not guaranteed that USERPROFILE is equivalent to %SYSTEMDRIVE%\Users\%USERNAME%. So it's possible that UserDefaults tries to write to a non-existent location, in which case it will never work. When this happens, all writes silently no-op.

This PR uses the Shell API SHGetKnownFolderPath to get the location of %LOCALAPPDATA% instead of manually building the path.

See documentation for this API:
image

Fixes #4997

@ShikiSuen
Copy link

@stevenbrix You might want to remove the format changes introduced on your computer.
This minifies the modifications that only necessary changes are kept.

P.S.: This PR is really important and I don't know why it gets abandoned here for months.

@ShikiSuen
Copy link

ShikiSuen commented Feb 4, 2025

@stevenbrix This PR is fallen behind the main branch too far.
The file to modified has been relocated to this folder:
/Sources/CoreFoundation/CFKnownLocations.c
And the part I guess you might want to modify is limited to this scope:

case _kCFKnownLocationUserCurrent:
username = CFGetUserName();
// fallthrough
case _kCFKnownLocationUserByName: {
DWORD size = 0;
GetProfilesDirectoryW(NULL, &size);
wchar_t* path = (wchar_t*)malloc(size * sizeof(wchar_t));
GetProfilesDirectoryW(path, &size);
CFStringRef pathRef = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, path, size - 1);
free(path);
CFURLRef profilesDir = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, pathRef, kCFURLWindowsPathStyle, true);
CFRelease(pathRef);
CFURLRef usernameDir = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, profilesDir, username, true);
CFURLRef appdataDir = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, usernameDir, CFSTR("AppData"), true);
location = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, appdataDir, CFSTR("Local"), true);
CFRelease(usernameDir);
CFRelease(appdataDir);
CFRelease(profilesDir);
if (user == _kCFKnownLocationUserCurrent) {
CFRelease(username);
}
break;

@parkera parkera requested a review from compnerd February 4, 2025 19:28
}
wchar_t* path = NULL;
SHGetKnownFolderPath(&FOLDERID_LocalAppData, 0, NULL, &path);
location = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, path, kCFURLWindowsPathStyle, true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems incorrect. The path parameter is a wide C String not a CFStrimg. This should do the creation of the CFString and pass that.

@ShikiSuen
Copy link

ShikiSuen commented Mar 2, 2025

@compnerd I emailed stevenbrix on Feb 18 trying to bring his attention towards your previous response here, but he has no response.

I am less familiar with the moral culture context in FOSS projects like this one. Is it appropriate for me (or anyone else) to replicate this PR and patch this? (with credits of stevenbrix in the commit message wherever necessary.)

@compnerd
Copy link
Member

compnerd commented Mar 2, 2025

@compnerd I emailed stevenbrix on Feb 18 trying to bring his attention towards your previous response here, but he has no response.

I am less familiar with the moral culture context in FOSS projects like this one. Is it appropriate for me (or anyone else) to replicate this PR and patch this? (with credits of stevenbrix in the commit message wherever necessary.)

Yes, maintaining the authorship/credit is key. It would be very much acceptable for someone to pick up the patch and help push it along if the original author is unable to do so.

@ShikiSuen
Copy link

Yes, maintaining the authorship/credit is key. It would be very much acceptable for someone to pick up the patch and help push it along if the original author is unable to do so.

Solid copy. Thanks for your response.

While I may see whether I can have some time to handle this issue within the next week, anyone can go ahead doing this without having to wait for me. This issue directly affects the usability of Swift (especially UserDefaults) on Windows.

@stevenbrix
Copy link
Author

Hi @ShikiSuen sorry for not responding. I'm not sure these changes are needed anymore now that the code has moved. I'll need to do more investigation here.

@ShikiSuen
Copy link

Hi @ShikiSuen sorry for not responding. I'm not sure these changes are needed anymore now that the code has moved. I'll need to do more investigation here.

Thanks for your response. Good to see you get back online.
I'll wait for your investigation results.

@stevenbrix stevenbrix force-pushed the use-SHGetKnownFolderPath branch 3 times, most recently from 8566cb1 to 0a1a7c8 Compare March 5, 2025 18:57
@stevenbrix stevenbrix force-pushed the use-SHGetKnownFolderPath branch from 0a1a7c8 to 733f2b0 Compare March 5, 2025 18:59
@parkera
Copy link
Contributor

parkera commented Mar 5, 2025

@swift-ci test

@ShikiSuen
Copy link

@compnerd Sorry for bothering you.
This PR is on halt for 2 weeks since the previous comment left above, hence my question.
Could you please take a look at stevenbrix's most-recent commit to see whether there's still any issue?

@@ -96,30 +97,16 @@ CFURLRef _Nullable _CFKnownLocationCreatePreferencesURLForUser(CFKnownLocationUs
}
case _kCFKnownLocationUserCurrent:
username = CFGetUserName();
// fallthrough
break;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems wrong. It is backwards no? LocalAppData would be for the current user, not a specified user. This seems like it would not return a location for the current user, and return the location for the current user when you specify a user name.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

UserDefaults doesn't work on Windows when USERNAME and USERPROFILE don't match
5 participants