use "lib:shell32" if windows
use "lib:ole32" if windows
use "debug"
use @SHGetKnownFolderPath[U32](rfid: Pointer[U8] tag, flags: U32,
token: Pointer[U32], path: Pointer[Pointer[U16]]) if windows
use @WideCharToMultiByte[I32](code_page: U32, flags: U32, char_str: Pointer[U16],
char_str_size: I32, multi_byte_str: Pointer[U8] tag, multi_byte_str_size: I32,
default_char: Pointer[U8], used_default_char: Pointer[U8]) if windows
use @CoTaskMemFree[None](pv: Pointer[U16] tag) if windows
primitive KnownFolderIds
"""
Known folder ids as described in:
https://docs.microsoft.com/en-ca/windows/desktop/shell/knownfolderid
functions return the little endian byte values of the folderid GUIDs
"""
fun profile(): Array[U8] val =>
"""
The user's profile folder. A typical path is C:\Users\username.
Applications should not create files or folders at this level;
they should put their data under the locations referred to by CSIDL_APPDATA or CSIDL_LOCAL_APPDATA.
However, if you are creating a new Known Folder the profile root referred to by CSIDL_PROFILE is appropriate.
FOLDERID_Profile
5E6C858F-0E22-4760-9AFE-EA3317B67173
"""
[as U8: 0x8f; 0x85; 0x6c; 0x5e; 0x22; 0x0e; 0x60; 0x47; 0x9A; 0xFE; 0xEA; 0x33; 0x17; 0xB6; 0x71; 0x73]
fun app_data_roaming(): Array[U8] val =>
"""
The file system directory that serves as a common repository for application-specific data.
A typical path is C:\Documents and Settings\username\Application Data.
KNOWNFOLDERID: FOLDERID_RoamingAppData
GUID: 3EB685DB-65F9-4CF6-A03A-E3EF65729F3D
"""
[as U8: 0xdb; 0x85; 0xb6; 0x3e; 0xf9; 0x65; 0xf6; 0x4c; 0xa0; 0x3a; 0xe3; 0xef; 0x65; 0x72; 0x9f; 0x3d]
fun app_data_local(): Array[U8] val =>
"""
The file system directory that serves as a data repository for local (nonroaming) applications.
A typical path is C:\Documents and Settings\username\Local Settings\Application Data.
FOLDERID_LocalAppData
F1B32785-6FBA-4FCF-9D55-7B8E7F157091
"""
[as U8: 0x85; 0x27; 0xb3; 0xf1; 0xba; 0x6f; 0xcf; 0x4f; 0x9d; 0x55; 0x7b; 0x8e; 0x7f; 0x15; 0x70; 0x91]
fun program_data(): Array[U8] val =>
"""
The file system directory that contains application data for all users.
A typical path is C:\Documents and Settings\All Users\Application Data.
This folder is used for application data that is not user specific.
For example, an application can store a spell-check dictionary,
a database of clip art, or a log file in the CSIDL_COMMON_APPDATA folder.
This information will not roam and is available to anyone using the computer.
FOLDERID_ProgramData
62AB5D82-FDC1-4DC3-A9DD-070D1D495D97
"""
[as U8: 0x82; 0x5d; 0xab; 0x62; 0xc1; 0xfd; 0xc3; 0x4d; 0xa9; 0xdd; 0x07; 0x0d; 0x1d; 0x49; 0x5d; 0x97]
primitive KnownFolders
"""
Utility for getting some known folders on windows.
https://docs.microsoft.com/en-ca/windows/desktop/shell/known-folders
"""
fun apply(folderid: Array[U8] val): String iso^ ? =>
ifdef not windows then
compile_error "known folders only supported on windows"
else
// get UTF-16 wide-char path from windows API
var path_pointer: Pointer[U16] = Pointer[U16]
let result: U32 =
@SHGetKnownFolderPath(
folderid.cpointer(), // REFKNOWNFOLDERID
U32(0), // retrieval flags -- no flags
Pointer[U32], // some strange handle, pass NULL
addressof path_pointer
)
if result != 0 then
Debug("Error getting known folder path: " + result.string())
error
end
// extract path from path_pointer
let bytes_necessary: I32 = @WideCharToMultiByte(
WindowsCodePages.utf8(),
U32(0),
path_pointer,
I32(-1), // length of path pointer, -1 for null terminated
Pointer[U8].create(),
I32(0), // indicating we want the required bytes back
Pointer[U8].create(), // NULL
Pointer[U8].create() // NULL
)
let utf8_path: Array[U8] iso = recover Array[U8](bytes_necessary.usize()) end
utf8_path.undefined(bytes_necessary.usize())
let convert_result: I32 = @WideCharToMultiByte(
WindowsCodePages.utf8(),
U32(0),
path_pointer,
I32(-1), // length of path pointer, -1 for null terminated
utf8_path.cpointer(),
utf8_path.size().i32(),
Pointer[U8].create(), // NULL
Pointer[U8].create() // NULL
)
if convert_result == 0 then
Debug("error converting from wchar to utf8.")
error
end
@CoTaskMemFree(path_pointer)
try utf8_path.pop()? end // remove 0 terminator
String.from_iso_array(consume utf8_path)
end