benners Posted November 20, 2020 Share Posted November 20, 2020 (edited) I have previously used _WinAPI_ShellGetKnownFolderPath to get the the full path of a known folder. This time I want to do the reverse and check if a file path matches a known folder. The idea is to supply a path to a function and first try to find an exact match. If no exact match is found, walk up the file path to find the closest partial match. I have found information on T'Internet about the KnownFolderManager and that has a method called FindFolderFromPath but the information there is right over my head. I have also found some VB code Here, and other code here, again when dll calls are mentioned, eyes glaze over and I end up going into a Homer Simpson day dream 🤔 I created a BS pseudo code function layout as an example of what I would like returned. I think the answer may involve a dll call. If someone could look at the links and add any info which may help me on my way, it would be appreciated 😍 Global $s_Path = 'C:\Program Files (x86)' Global $s_KFN = GetKnownFolderFromPath($s_Path) If Not @error Then MsgBox(0,'', $s_KFN) Func GetKnownFolderFromPath($s_Path) Const $FFFP_EXACTMATCH = 0 Const $FFFP_NEARESTPARENTMATCH = 1 Local $o_KFM = ObjCreate('KnownFolderManager') If @error Then Return SetError(1, 0, 'ObjCreate(KnownFolderManager)') Local $o_IKF = ObjCreate('IKnownFolder') If @error Then Return SetError(2, 0, 'ObjCreate(IKnownFolder)') ; try to find an exact match for the path Local $v_Ret = $o_KFM.FindFolderFromPath($s_Path, $FFFP_EXACTMATCH, $o_IKF) If @error Then ; if no exact match then walk backwards up the file path and find the nearest match $v_Ret = $o_KFM.FindFolderFromPath($s_Path, $FFFP_NEARESTPARENTMATCH, $o_IKF) ; if no partial match then return the file path If @error Then Return SetError(3, 0, $s_Path) EndIf Return $o_IKF.Path EndFunc ;==>GetKnownFolderFromPath Edited November 20, 2020 by benners added another link Link to comment Share on other sites More sharing options...
boom221 Posted November 21, 2020 Share Posted November 21, 2020 Maybe this? AutoItSetOption("MustDeclareVars", True) Local $path = "C:\Program Files (x86)\something" ConsoleWrite(FindExactOrClosestPatialMatch($path) & @CRLF) Func FindExactOrClosestPatialMatch($path) While True If FileExists($path) Then Return $path $path = StringRegExpReplace($path, "^(.*[\\/]).*?$", "$1") If @extended == 0 Then Return SetError(1, 0, Null) WEnd EndFunc Link to comment Share on other sites More sharing options...
benners Posted November 22, 2020 Author Share Posted November 22, 2020 Thanks for the reply boom The code isn't any that I can use to sort the issue. I don't need to get the path from the string. I need to know if the string is a KnownFolder. These are special folders like My Documents or My Pictures for instance. These are different per user. I am writing a program that install software, copy files to dirs and delete shortcuts etc for mutliple users. The idea is to test on my system, do the deleting, copying etc and any file paths that relate to my user folder are changed to the correct environment string for any CurrentUser or AllUsers. I have been using Code by Yashied, which is now included in the default includes, to get the file path from a folders guid and needed to do the reverse. I had found some info as mentioned in the first post and various AHK scripts and C Code but can't fathom how to do it in AutoIt. I might have a workaround but don't know how bulletproof it is. Link to comment Share on other sites More sharing options...
boom221 Posted November 23, 2020 Share Posted November 23, 2020 I am not great with ObjCreate. However, I managed to wrap the function in my own dll so that I can call it within AutoIt. If you do not find a better solution, I can share the dll with you. Link to comment Share on other sites More sharing options...
benners Posted November 23, 2020 Author Share Posted November 23, 2020 I managed to cobble some code together that gets me what I want. I haven't used it in anger yet so it may need tweaknig when I get around to using it in the app. I would prefer to be able the built in api ones in the first post but hey 😦 What function did you wrap? expandcollapse popup#AutoIt3Wrapper_AU3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w- 4 -w 5 -w 6 -w-7 #include-once ; #INDEX# ======================================================================================================================= ; Title .........: KnownFolders ; AutoIt Version : 3.3.14.3 ; Description ...: A collection of functions for KnownFolder interrogation ; Author(s) .....: Benners ; =============================================================================================================================== ; #CURRENT# ===================================================================================================================== ;_KFS_CreateArray : Create an array listing all the knownfolders for the current system along with their path values ;_KFS_GetFolderNameFromPath : Searches an array for matches to a specified path and subsitutes path for KnownFolder name ;_KFS_GetFolderPathFromName : Retrieve a knownfolders path based on its assigned name ; =============================================================================================================================== ; #INTERNAL# ==================================================================================================================== ; =============================================================================================================================== #include <Array.au3> #include <WinAPIReg.au3> #include <WinAPIShellEx.au3> ;~ #Tidy_Parameters=/sf ;#### Example #### ; create an array Global $as_KF = _KFS_CreateArray() ;~ _ArrayDisplay($as_KF) If @error Then MsgBox(0, 'Error', $as_KF) Else ; search for the closest path and create a string path from it Local $s_FolderPath = _KFS_GetFolderNameFromPath('C:\ProgramData\Microsoft\User Account Pictures\Default Pictures', $as_KF) MsgBox(0, 'New Folder Path', $s_FolderPath) ; get the path from the name $s_FolderPath = _KFS_GetFolderPathFromName('#Common AppData#', $as_KF) MsgBox(0, 'KnownFolder Path', $s_FolderPath) EndIf ;#### Example #### ; #FUNCTION# ==================================================================================================================== ; Name ..........: _KFS_CreateArray ; Description ...: Create an array of knownfolders found in the registry for the current system along with their path values ; Syntax ........: _KFS_CreateArray() ; Parameters ....: None ; Return values .: Success: a 2D array, sorted on column 2 ; arr[0] - name of the known folder ; arr[1] - path of the folder ; arr[2] - length of the path string ; Failure: sets @error to non zero and based on @error returns string error message (1, 2) or an array (6, 7) ; @error 1: unable to open the registry key (_WinAPI_RegOpenKey) ; @error 2: unable to query the registry key (_WinAPI_RegQueryInfoKey) ; @error 6: unable to close the registry query (_WinAPI_RegCloseKey) ; @error 7: unable to sort thr array (_ArraySort) ; Author ........: Benners ; Modified ......: ; =============================================================================================================================== Func _KFS_CreateArray() Local _ $s_SubKey = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FolderDescriptions', _ $s_FullKey = 'HKEY_LOCAL_MACHINE' & '\' & $s_SubKey, _ $s_GUID ; gopen the root key for reading Local $h_RootKey = _WinAPI_RegOpenKey($HKEY_LOCAL_MACHINE, $s_SubKey, $KEY_READ) If @error Then Return SetError(1, @extended, _WinAPI_GetErrorMessage(@extended)) ; get the number of sub keys Local $i_Max = _WinAPI_RegQueryInfoKey($h_RootKey) If @error Then Return SetError(2, @extended, _WinAPI_GetErrorMessage(@extended)) ; columns in the array Local Enum _ $NAME, _ $PATH, _ $LENGTH ; create an array for the info Local $as_KF[$i_Max[0]][3] ; loop through the sub keys and get the 'Name' value from the key For $i = 0 To $i_Max[0] - 1 ; get the GUID $s_GUID = _WinAPI_RegEnumKey($h_RootKey, $i) If @error Then ContinueLoop ; Return SetError(3, @extended, _WinAPI_GetErrorMessage(@extended)) ; get the 'Name' value from the GUID key $as_KF[$i][$NAME] = RegRead($s_FullKey & '\' & $s_GUID, 'Name') If @error Then ContinueLoop ; Return SetError(4, @error, 'RegRead) ; get the path of the folder $as_KF[$i][$PATH] = _WinAPI_ShellGetKnownFolderPath($s_GUID) If @error Then ContinueLoop ; Return SetError(5, @extended, _WinAPI_GetErrorMessage(@extended)) ; get the string length. used for sorting later $as_KF[$i][$LENGTH] = StringLen($as_KF[$i][$PATH]) Next ; close the handle to the key _WinAPI_RegCloseKey($h_RootKey) If @error Then SetError(6, @extended, _WinAPI_GetErrorMessage(@extended)) ; sort the array, longest path first _ArraySort($as_KF, 1, 0, 0, $LENGTH) If @error Then SetError(7, @error, '_ArraySort failed') Return SetError(@error, @extended, $as_KF) EndFunc ;==>_KFS_CreateArray ; #FUNCTION# ==================================================================================================================== ; Name ..........: _KFS_GetFolderNameFromPath ; Description ...: Searches an array for matches to a specified path andsubsitutes path for KnownFolder name ; Syntax ........: _KFS_GetFolderNameFromPath($s_Path, $as_KF) ; Parameters ....: $s_Path - a string value. ; $as_KF - a 2D array of strings. uses _KFS_CreateArray() is passed variable isn't an array ; Return values .: Success: a string path ; If a match is found - sets @extended to 1, path strings substituted and enclosed in '#' ; If a match isn't found - sets @extended to 0, original path is returned ; Failure: sets @error to non zero is _KFS_CreateArray is not created and returns an error message ; Author ........: Benners ; Modified ......: ; Remarks .......: Uses _KFS_CreateArray to create the array of KnownFolders if no array specified ; =============================================================================================================================== Func _KFS_GetFolderNameFromPath($s_Path, ByRef $as_KF) ; create the array If Not IsArray($as_KF) Then $as_KF = _KFS_CreateArray() If @error Then Return SetError(@error, @extended, $as_KF) EndIf ; columns in the array Local Enum _ $NAME, _ $PATH ; loop through the array and search for the path string For $i = 0 To UBound($as_KF) - 1 ; if the string is found, replace it with the name If StringInStr($s_Path, $as_KF[$i][$PATH]) Then $s_Path = StringReplace($s_Path, $as_KF[$i][$PATH], '#' & $as_KF[$i][$NAME] & '#') SetExtended(1) ExitLoop EndIf Next Return SetError(@error, @extended, $s_Path) EndFunc ;==>_KFS_GetFolderNameFromPath ; #FUNCTION# ==================================================================================================================== ; Name ..........: _KFS_GetFolderPathFromName ; Description ...: Retrieve a knownfolders path based on its assigned name ; Syntax ........: _KFS_GetFolderPathFromName($s_Name[, $as_KF = 0]) ; Parameters ....: $s_Name - a string value. name of the folder ; $as_KF - a 2D array of strings. uses _KFS_CreateArray() is passed variable isn't an array ; Return values .: Success: a string ; If a match is found - sets @extended to 1, path to the specified folder ; If a match isn't found - sets @extended to 0, original name is returned ; Failure: sets @error to non zero is _KFS_CreateArray is not created and returns an error message ; Author ........: Benners ; Modified ......: ; Remarks .......: Uses _KFS_CreateArray to create the array of KnownFolders if no array specified ; =============================================================================================================================== Func _KFS_GetFolderPathFromName($s_Name, ByRef $as_KF) ; create the array If Not IsArray($as_KF) Then $as_KF = _KFS_CreateArray() If @error Then Return SetError(@error, @extended, $as_KF) EndIf ; columns in the array Local Enum _ $NAME, _ $PATH ; strip the '#' symbols $s_Name = StringReplace($s_Name, '#', '') ; set the default return path Local $s_Path = $s_Name ; loop through the array and search for the name string For $i = 0 To UBound($as_KF) - 1 ; if the string is found, replace it with the path If StringCompare($s_Name, $as_KF[$i][$NAME]) = 0 Then $s_Path = $as_KF[$i][$PATH] SetExtended(1) ExitLoop EndIf Next Return SetError(@error, @extended, $s_Path) EndFunc ;==>_KFS_GetFolderPathFromName Link to comment Share on other sites More sharing options...
Nine Posted November 23, 2020 Share Posted November 23, 2020 @benners Not a bad solution you got there. If you also need localized name, you can use this to get it from : #include <WinAPIRes.au3> ConsoleWrite (_GetString(21813) & @CRLF) Func _GetString($iNum) Local $hInstance = _WinAPI_LoadLibraryEx("shell32.dll", $LOAD_LIBRARY_AS_DATAFILE) If Not $hInstance Then Return SetError(1, 0, "") Local $sText = _WinAPI_LoadString($hInstance, $iNum) Local $iErr = @error $iNum = @extended _WinAPI_FreeLibrary($hInstance) If $iErr Then Return SetError(2, 0, "") Return SetError(0, $iNum, $sText) EndFunc where the number is from the registry you are reading... “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Screen Scraping Multi-Threading Made Easy Link to comment Share on other sites More sharing options...
benners Posted November 23, 2020 Author Share Posted November 23, 2020 Cheers Nine. It may need some extra to use the correct registry tree based on bit type but I'll get to that. I'll also keep trying to understand the AHK code I found and other code types and maybe improve it, but for now it will do. I'll add your code to mysnippets collection 😀. It could come in useful for other projects. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now