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

A Zig SHGetKnownFolderPath implementation in the standard library #18098

Open
squeek502 opened this issue Nov 24, 2023 · 3 comments
Open

A Zig SHGetKnownFolderPath implementation in the standard library #18098

squeek502 opened this issue Nov 24, 2023 · 3 comments
Labels
contributor friendly This issue is limited in scope and/or knowledge of Zig internals. enhancement Solving this issue will likely involve adding new logic or components to the codebase. standard library This issue involves writing Zig code for the standard library.
Milestone

Comments

@squeek502
Copy link
Collaborator

squeek502 commented Nov 24, 2023

EDIT: This is currently being worked on here: https://github.com/squeek502/get-known-folder-path


Follow up from #18091

To avoid a dependency on shell32.dll, the SHGetKnownFolderPath call within std.fs.getAppDataDir was replaced with a LOCALAPPDATA environment variable lookup. This has the potential to regress getAppDataDir behavior for certain setups and it's not foolproof to rely on LOCALAPPDATA being set.

Instead, SHGetKnownFolderPath should be reimplemented in Zig without introducing a dependency on shell32. For this, wine's implementation would likely be a good reference.

Standalone version of the previous implementation using SHGetKnownFolderPath (this is ultimately the behavior that the Zig reimplementation is looking to match, but it shouldn't be FOLDERID_LocalAppData-specific):

const std = @import("std");
const os = std.os;

pub extern "shell32" fn SHGetKnownFolderPath(
    rfid: *const os.windows.KNOWNFOLDERID,
    dwFlags: os.windows.DWORD,
    hToken: ?os.windows.HANDLE,
    ppszPath: *[*:0]os.windows.WCHAR,
) callconv(os.windows.WINAPI) os.windows.HRESULT;

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer std.debug.assert(gpa.deinit() == .ok);
    const allocator = gpa.allocator();

    var dir_path_ptr: [*:0]u16 = undefined;
    switch (SHGetKnownFolderPath(
        &os.windows.FOLDERID_LocalAppData,
        os.windows.KF_FLAG_CREATE,
        null,
        &dir_path_ptr,
    )) {
        os.windows.S_OK => {
            const global_dir = std.unicode.utf16leToUtf8Alloc(allocator, std.mem.sliceTo(dir_path_ptr, 0)) catch |err| switch (err) {
                error.UnexpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
                error.ExpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
                error.DanglingSurrogateHalf => return error.AppDataDirUnavailable,
                error.OutOfMemory => return error.OutOfMemory,
            };
            defer allocator.free(global_dir);
            std.debug.print("{s}\n", .{global_dir});
        },
        os.windows.E_OUTOFMEMORY => return error.OutOfMemory,
        else => return error.AppDataDirUnavailable,
    }
}
@squeek502 squeek502 added contributor friendly This issue is limited in scope and/or knowledge of Zig internals. standard library This issue involves writing Zig code for the standard library. enhancement Solving this issue will likely involve adding new logic or components to the codebase. labels Nov 24, 2023
@matu3ba
Copy link
Contributor

matu3ba commented Dec 1, 2023

I'll work on this to get a stab at some more windows tooling.

@squeek502
Copy link
Collaborator Author

@matu3ba I've got an implementation mostly complete for retrieving a known path that I'll likely create a PR for soon.

Right now I'm thinking that the more complicated parts should not be supported (KF_FLAG_CREATE and KF_FLAG_INIT in particular, which would potentially need to do things like create "Library" folders, init desktop.ini files, respect various undocumented flags in FolderDescriptions registry entries, etc).

I'll go into more detail in the PR.

@Vexu Vexu added this to the 0.13.0 milestone Dec 2, 2023
@squeek502
Copy link
Collaborator Author

squeek502 commented Dec 8, 2023

I've decided against making a PR for now to allow for (hopefully) developing a more conformant implementation. SHGetKnownFolderPath has a lot of moving parts, and Zig only really cares about getting the path to the Local App Data directory, so %LOCALAPPDATA% seems like a decent solution since it's set by the OS. That is, this issue doesn't seem urgent enough to warrant merging in a potentially half-baked SHGetKnownFolderPath reimplementation just to stop using %LOCALAPPDATA%.

I've put my mostly-working-but-probably-has-incorrect-corner-cases implementation here with a lot of explanation in the readme:

https://github.com/squeek502/get-known-folder-path

If anyone wants to help out or has ideas about how to go about this sort of thing, it'd be appreciated. It feels like it's nearly there, just a few pieces that need to be figured out.

@Vexu Vexu modified the milestones: 0.13.0, 0.14.0 Dec 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
contributor friendly This issue is limited in scope and/or knowledge of Zig internals. enhancement Solving this issue will likely involve adding new logic or components to the codebase. standard library This issue involves writing Zig code for the standard library.
Projects
None yet
Development

No branches or pull requests

3 participants