Sign in to follow this  
Followers 0
Kilmatead

FileGetLongName and the Loss of an Undocumented Feature...

12 posts in this topic

Yes, I know, one can hardly complain when an undocumented feature is "removed" from a function, but just out of my own curiosity (as it's script-changing), I'm wondering if it was intentional or just a random side-effect.

Essentially (in pre-3.3.12.0) FileGetLongName was a convenient means of regularising the "case" of a given qualified path:

FileGetLongName("c:program files") would return "C:Program Files", etc.

Since NTFS is case-agnostic this had no impact on actual usage, but it was infinitely useful when users would type in a location, and you could clean up the (verified) text before displaying the path, especially when it came to relative-path transformations.  Since 3.3.12.0. however, it does no case-correction at all, and seems to behave exactly the same as _WinAPI_GetFullPathName.

Now, obviously this isn't the end of the world - a raggety-line of 5 nested functions can accomplish the behaviour I expect:

FileGetLongName(FileGetShortName(StringUpper(StringLeft($sFile, 1)) & StringMid($sFile, 2)))

 

...but hey, 5:1 isn't really a bad ratio. :D (Yes, the extra string functions are needed as the drive letter itself remains lower-case, while the rest is auto-corrected - all in all it's still easier than dancing along the components rebuilding the path manually by reading each from the drive.)

Anyway, is there now any benefit to using the inbuilt function over the API call, or is this just a quirk effecting only me?

Share this post


Link to post
Share on other sites



You could use _StringTitleCase to clean up the text if you wanted to use a single function.


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

#3 ·  Posted (edited)

_StringTitleCase doesn't have any relation to the actual pathname on the disc itself - it's just a blind-formatter.  I'm talking about retrieving the actual case contained within the filenames themselves.  True, as I said above, it's a purely aesthetic thing (unless you play with FILE_FLAG_POSIX_SEMANTICS cross-platform nomenclature), but there's no end of workaround threads on stackoverflow related to it - so somebody besides me must pay attention to these things.  I just always found it convenient that it was built-in to the native FGLN function - and now it's not.

Edited by Kilmatead

Share this post


Link to post
Share on other sites

_StringTitleCase doesn't have any relation to the actual pathname on the disc itself - it's just a blind-formatter.

Neither does this have any relation to the path name returned, I was merely making a counter suggestion to your 5 functions and just using one.

 

FileGetLongName(FileGetShortName(StringUpper(StringLeft($sFile, 1)) & StringMid($sFile, 2)))

If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

...except in this case that "one" function doesn't actually do anything useful (it only capitalises an arbitrary string) - the 5-function example returns the existing case of whatever filesystem object you point it at, that's what I'm referring to.  I could use _WinAPI_GetFinalPathNameByHandle, except that doesn't exist in XP and it also doesn't always return the given path (in the case of links).

Like I said, the workaround isn't important (as long as it works), I'm just curious about the change in the original function - since it doesn't seem to be much different, otherwise.

Share this post


Link to post
Share on other sites

Here's a quick proof of concept script I threw together that will allow you to fix the issue until something official comes along. One function call, and no UDFs needed.

$sFile = "c:\progra~1\"
$sFile1 = "c:\pRoGraM fiLes"
ConsoleWrite(FileGetLongName(FileGetShortName(StringUpper(StringLeft($sFile, 1)) & StringMid($sFile, 2))) & @CRLF)
ConsoleWrite(FileGetLongName(FileGetShortName(StringUpper(StringLeft($sFile1, 1)) & StringMid($sFile1, 2))) & @CRLF)
ConsoleWrite(FileGetLongName($sFile) & @CRLF) ; correct case returned
ConsoleWrite(FileGetLongName($sFile1) & @CRLF) ; <<<<<<<<<< wrong case returned
ConsoleWrite($sFile & ' => ' & _WinAPI_GetCaseCorrectLongFileName($sFile) & @CRLF) ; works with short path/file names
ConsoleWrite($sFile1 & ' => ' & _WinAPI_GetCaseCorrectLongFileName($sFile1) & @CRLF) ; and long path/file names, even with the case wrong
Func _WinAPI_GetCaseCorrectLongFileName($sFile)
    Local $Ret = DllCall('kernel32.dll', 'dword', 'GetLongPathNameW', 'wstr', $sFile, 'wstr', '', 'dword', 4096)
    $test = ObjCreate("Scripting.FileSystemObject")
    Return $test.GetAbsolutePathName($Ret[2])
EndFunc   ;==>_WinAPI_GetLongPathName

It SHOULD work with either folder names or file names


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

Now that's more interesting - though it does require the object to exist in the first place, else it returns the currently working directory (which is obviously impractical); adding a simple FileExists check before calling the GetLongPathNameW would do the trick in a blunt way, but doing a canonical call first will resolve as much of the path as possible (and return it) to provide the most clarity in case of failure:

Func _WinAPI_GetCaseCorrectLongFileName($sFile)
    $sFile = _WinAPI_PathCanonicalize($sFile)
    If Not FileExists($sFile) Then Return $sFile   
 
    Local $Ret = DllCall('kernel32.dll', 'dword', 'GetLongPathNameW', 'wstr', $sFile, 'wstr', '', 'dword', 4096)
    Local $oTarget = ObjCreate("Scripting.FileSystemObject")
   
    Return $oTarget.GetAbsolutePathName($Ret[2])
EndFunc
 

...thus "C:ExistingExisting....usersNonExisting" returns "c:usersNonExisting" which may not be case-wise sound (obviously, as it doesn't exist) but it certainly returns a close approximation to the requested object, and is safe to use on user-input.

Which brings us down to 4 function-calls (without the canonical it'd be 3), allowing for relative paths and non-existing objects. :D

Thanks for this - better than my first work-around, and even better than the "old" FileGetLongName which could not deduce relative paths, though at least returned the given path on failure.

Share this post


Link to post
Share on other sites

If the path doesn't exist, it should set the error condition and return the $sFile parameter. I see no reason to include _WinAPI_PathCanonicalize in the function unless you're not replacing the standard FileGetLongName function, because that function doesn't use it or anything like it. You could use the $oTarget.GetAbsolutePathName to replace the _WinAPI_PathCanonicalize function with your "C:ExistingExisting....usersNonExisting" path string to return the same result.

BTW, none of the functions in the snippet return anything but the parameter and an error if the file or folder doesn't exist.

Also forgot to mention, the above snippet I posted works back to WinXP.


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

#9 ·  Posted (edited)

I tacked on the _WinAPI_PathCanonicalize because GetLongPathNameW automatically does the same thing (as does the FileExists call) - I didn't realise that the com's GetAbsolutePathName would do it too - all's the better.  (In fact, a quick check shows that on error GetAbsolutePathName will properly case the part of the canonicalised pathname which does exist, and leave the rest [without truncation], which is perfect.)

Technically none of this was necessary, as my original 5-function mess provided the precise same results (existing/nonexisting) as the old FileGetLongName, but yours reduces that by 1 at least (an extra SetError and FileExists on top of the other two).

The standard FileGetLongName function was more or less irrelevant to me as I only ever used it to "clean" filepaths - at which it worked admirably, until someone decided to neuter it.  So, if I have to replace it anyway, I'll take advantage of building in any extra things that serve a purpose, and canonicalised paths on error fits the bill.

Func _WinAPI_GetCaseCorrectLongFileName($sFile)
    Local $oTarget = ObjCreate("Scripting.FileSystemObject")

    If FileExists($sFile) Then
        Local $Ret = DllCall('kernel32.dll', 'dword', 'GetLongPathNameW', 'wstr', $sFile, 'wstr', '', 'dword', 4096)
        Return $oTarget.GetAbsolutePathName($Ret[2])
    EndIf
    
    Return SetError(1, 0, $oTarget.GetAbsolutePathName($sFile))
EndFunc
Edited by Kilmatead

Share this post


Link to post
Share on other sites

Brewmanh/Kilmataed, maybe you could submit this as a UDF for inclusion with AutoIt, or at least put it in the Example Scripts forum, as this will probably prove useful to others.  I believe the old FileGetLongName() used a SHGetPathFromIDList() call somewhere..

Share this post


Link to post
Share on other sites

Examples section please. Also for compatibility use the _WinAPI_* function calls to reduce bloat in a user's script. Thanks.


_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 04/09/2015

Share this post


Link to post
Share on other sites

#12 ·  Posted (edited)

...for compatibility use the _WinAPI_* function calls to reduce bloat in a user's script.

 

Interestingly enough, there is no _WinAPI_ for GetLongPathNameW defined in the AI library (probably because the native function exists).  Technically that call is superfluous here anyway, as the (otherwise banjaxed) FileGetLongName will serve the same purpose:

Func _GetCaseCorrectLongFileName($sFile)
    If FileExists($sFile) Then
        Local $oTarget = ObjCreate("Scripting.FileSystemObject")
        Return $oTarget.GetAbsolutePathName(FileGetLongName($sFile))
    Else
        Return SetError(1, 0, $sFile)
    EndIf
EndFunc

 

(This thread was not meant to be about a new function, it was just asking why something else went mysteriously "poof"...) shrug.gif

Edited by Kilmatead

Share this post


Link to post
Share on other sites

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 account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0