Created
March 31, 2020 22:34
-
-
Save timsneath/08047e15b4ec1a72d291f89d6e22e478 to your computer and use it in GitHub Desktop.
Buggy attempt to call SHGetKnownFolder from Dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // knownfolder.dart | |
| // Shows usage of Shell APIs to retrieve the user's home directory | |
| import 'dart:ffi'; | |
| import 'package:ffi/ffi.dart'; | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // DEFINES ///////////////////////////////////////////////////////////////////// | |
| //////////////////////////////////////////////////////////////////////////////// | |
| const S_OK = 0; | |
| const E_FAIL = 0x80004005; | |
| const E_INVALIDARG = 0x80070057; | |
| const MAX_PATH = 260; | |
| const KF_FLAG_DEFAULT = 0; | |
| const NULL = 0; | |
| const FOLDERID_Documents = '{FDD39AD0-238F-46AF-ADB4-6C85480369C7}'; | |
| bool SUCCEEDED(int result) => (result == S_OK); | |
| // typedef struct _GUID { | |
| // unsigned long Data1; | |
| // unsigned short Data2; | |
| // unsigned short Data3; | |
| // unsigned char Data4[ 8 ]; | |
| // } GUID; | |
| class GUID extends Struct { | |
| @Uint32() | |
| int Data1; | |
| @Uint16() | |
| int Data2; | |
| @Uint16() | |
| int Data3; | |
| @Uint64() | |
| int Data4; | |
| factory GUID.allocate() => allocate<GUID>().ref | |
| ..Data1 = 0 | |
| ..Data2 = 0 | |
| ..Data3 = 0 | |
| ..Data4 = 0; | |
| /// Create GUID from common {FDD39AD0-238F-46AF-ADB4-6C85480369C7} format | |
| factory GUID.fromString(String guidString) { | |
| assert(guidString.length == 38); | |
| final guid = allocate<GUID>().ref; | |
| guid.Data1 = int.parse('${guidString.substring(1, 9)}', radix: 16); | |
| guid.Data2 = int.parse('${guidString.substring(10, 14)}', radix: 16); | |
| guid.Data3 = int.parse('${guidString.substring(15, 19)}', radix: 16); | |
| guid.Data4 = | |
| (int.parse('${guidString.substring(20, 24)}', radix: 16) << 48) + | |
| int.parse('${guidString.substring(25, 37)}', radix: 16); | |
| return guid; | |
| } | |
| /// Print GUID in common {FDD39AD0-238F-46AF-ADB4-6C85480369C7} format | |
| @override | |
| String toString() => | |
| '{${Data1.toRadixString(16).padLeft(8, '0').toUpperCase()}-' | |
| '${Data2.toRadixString(16).padLeft(4, '0').toUpperCase()}-' | |
| '${Data3.toRadixString(16).padLeft(4, '0').toUpperCase()}-' | |
| '${((Data4 >> 48) & 0xFFFF).toRadixString(16).padLeft(4, '0').toUpperCase()}-' | |
| '${(Data4 & 0xFFFFFFFFFFFF).toRadixString(16).padLeft(12, '0').toUpperCase()}}'; | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // FFI ///////////////////////////////////////////////////////////////////////// | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // void CoTaskMemFree( | |
| // _Frees_ptr_opt_ LPVOID pv | |
| // ); | |
| typedef coTaskMemFreeNative = Void Function(Pointer<Void> pv); | |
| typedef coTaskMemFreeDart = void Function(Pointer<Void> pv); | |
| // SHFOLDERAPI SHGetFolderPathW( | |
| // HWND hwnd, | |
| // int csidl, | |
| // HANDLE hToken, | |
| // DWORD dwFlags, | |
| // LPWSTR pszPath | |
| // ); | |
| typedef shGetFolderPathNative = Int32 Function(Int64 hwnd, Int32 csidl, | |
| Int64 hToken, Int32 dwFlags, Pointer<Uint16> pszPath); | |
| typedef shGetFolderPathDart = int Function( | |
| int hwnd, int csidl, int hToken, int dwFlags, Pointer<Uint16> pszPath); | |
| // HRESULT SHGetKnownFolderPath( | |
| // REFKNOWNFOLDERID rfid, | |
| // DWORD dwFlags, | |
| // HANDLE hToken, | |
| // PWSTR *ppszPath | |
| // ); | |
| typedef shGetKnownFolderPathNative = Int32 Function( | |
| Pointer<GUID> rfid, Int32 dwFlags, Int64 hToken, Pointer<Void> ppszPath); | |
| typedef shGetKnownFolderPathDart = int Function( | |
| Pointer<GUID> rfid, int dwFlags, int hToken, Pointer<Void> ppszPath); | |
| final ole32 = DynamicLibrary.open('ole32.dll'); | |
| final CoTaskMemFree = ole32 | |
| .lookupFunction<coTaskMemFreeNative, coTaskMemFreeDart>('CoTaskMemFree'); | |
| final shell32 = DynamicLibrary.open('shell32.dll'); | |
| final SHGetFolderPath = | |
| shell32.lookupFunction<shGetFolderPathNative, shGetFolderPathDart>( | |
| 'SHGetFolderPathW'); | |
| final SHGetKnownFolderPath = shell32.lookupFunction<shGetKnownFolderPathNative, | |
| shGetKnownFolderPathDart>('SHGetKnownFolderPath'); | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // FUNC //////////////////////////////////////////////////////////////////////// | |
| //////////////////////////////////////////////////////////////////////////////// | |
| String fromUtf16(Pointer pointer, int length) { | |
| final buf = StringBuffer(); | |
| final ptr = Pointer<Uint16>.fromAddress(pointer.address); | |
| for (var v = 0; v < length; v++) { | |
| final charCode = ptr.elementAt(v).value; | |
| if (charCode != 0) { | |
| buf.write(String.fromCharCode(charCode)); | |
| } else { | |
| return buf.toString(); | |
| } | |
| } | |
| return buf.toString(); | |
| } | |
| /// Get the path for a known Windows folder, using the classic (deprecated) API | |
| void getFolderPath() { | |
| final CSIDL_MYDOCUMENTS = 0x0005; | |
| var path = allocate<Uint16>(count: MAX_PATH); | |
| final result = SHGetFolderPath(NULL, CSIDL_MYDOCUMENTS, NULL, 0, path); | |
| if (SUCCEEDED(result)) { | |
| print('SHGetFolderPath returned ${fromUtf16(path, MAX_PATH)}'); | |
| } else { | |
| print( | |
| 'SHGetFolderPath returned error code 0x${result.toUnsigned(32).toRadixString(16)}'); | |
| } | |
| } | |
| /// Get the path for a known Windows folder, using the modern API | |
| void getKnownFolderPath() { | |
| final guidFolder = GUID.fromString(FOLDERID_Documents); | |
| assert(sizeOf<GUID>() == 16); | |
| print('Looking for folder with ID: $guidFolder'); | |
| final path = allocate<Uint16>(count: MAX_PATH); | |
| final ptr = Pointer<Void>.fromAddress(path.address); | |
| final result = | |
| SHGetKnownFolderPath(guidFolder.addressOf, KF_FLAG_DEFAULT, 0, ptr); | |
| if (SUCCEEDED(result)) { | |
| print('SHGetKnownFolderPath returned ${fromUtf16(ptr, MAX_PATH)}'); | |
| CoTaskMemFree(ptr); | |
| } else { | |
| if (result == E_FAIL) { | |
| print('SHGetKnownFolderPath returned error code E_FAIL'); | |
| } else if (result == E_INVALIDARG) { | |
| print('SHGetKnownFolderPath returned error code E_INVALIDARG'); | |
| } else { | |
| print('SHGetKnownFolderPath returned error code ' | |
| '0x${result.toUnsigned(32).toRadixString(16)}'); | |
| } | |
| } | |
| } | |
| void main() { | |
| getFolderPath(); | |
| print(''); | |
| getKnownFolderPath(); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment