Per-Drive Default Directory

Share your ideas as to how the documentation can be improved.
JBensimon
Posts: 33
Joined: 19 Nov 2017, 11:19

Per-Drive Default Directory

Post by JBensimon » 20 Dec 2020, 22:11

I notice that, when launched from CMD.exe, an AHK script inherits the concept of a per-drive default directory (which is unique to CMD among Windows apps) and that file operations (for example Loop, Files, D:*) obey these per-drive defaults if they were set in CMD before launch via CD commands. Is this behavior intentional and can it be relied on in future versions? Should it be documented?

I note also that the current default for a drive (say D: for example),if set by CMD, can be retrieved with EnvGet by querying CMD's weird hidden variable named "=D:". and that a drive's default directory for future file operations can be set using EnvSet on those same variables, whether or not the AHK script was launched from CMD. Again, is this behavior intentional and reliable in future versions?

Thanks.

Jacques.
User avatar
Ragnar
Posts: 367
Joined: 30 Sep 2013, 15:25

Re: Per-Drive Default Directory

Post by Ragnar » 21 Dec 2020, 05:23

In AutoHotkey v1, this is called "initial working directory" and is intended to be so; this is, the script's working directory defaults to the "initial working directory". If you want to use the script's location as working directory instead, use SetWorkingDir % A_ScriptDir in the auto-execute section (by default, this line is included in a new script when it is created via right-click context menu). See SetWorkingDir (v1) for details.

In AutoHotkey v2, the script's working directory defaults to the script's location. See SetWorkingDir (v2) for details.
JBensimon
Posts: 33
Joined: 19 Nov 2017, 11:19

Re: Per-Drive Default Directory

Post by JBensimon » 21 Dec 2020, 12:39

Hi. You may have misunderstood my question: I was referring not to the script's working directory but rather to the fact that scripts obey per-drive default directories when launched from CMD. For example, if the following commands are executed in CMD,

Code: Select all

CD /D C:\TEMP
CD D:\TEMP\SUB1
CD E:\FOLDER\SUB
C:\AutoHotkey\AutoHotkeyU64.exe C:\Code\Test.ahk
then within the Test.ahk script yes, A_ScriptDir is C:\Code and A_WorkingDir is C:\TEMP, but a "naked" reference to C: also corresponds to C:\TEMP, a naked reference to D: refers to D:\TEMP\SUB1, a reference to D:SUB2 corresponds to D:\TEMP\SUB1\SUB2, a reference to E: corresponds to E:\FOLDER\SUB, a reference to E:.. corresponds to E:\FOLDER, etc., etc.. This means that AHK is "inheriting" and obeying CMD's per-drive default directories (which is a unique feature of CMD -- other apps in Windows only have the concept of a single working directory that is used for all relative references, and relative references involving other drives are interpreted starting at the root of those drives -- see Raymond Chen's article https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133).

I also pointed out that, per the above article, the current default directory of a given drive (D: in the following example) can be retrieved using

Code: Select all

EnvGet, D_Default, =D:         ; this sets the variable D_Default to D:\TEMP\SUB1 in our example
and it also turns out that the current default directory for a drive can be set using

Code: Select all

EnvSet, =D:, D:\TEMP\SUB1\SUB2         ; future naked references to D: will now access D:\TEMP\SUB1\SUB2
whether or not the script was launched by CMD.

Overall, my post asks whether these behaviors are intentional (I have to believe that they are since nothing in the Win32 API would cause them "accidentally"), whether they're reliable in future 1.x versions, and whether they maybe ought to be documented (especially the first part). I guess a side question is whether they'll remain in v2.x.

Note: I am not suggesting here that making use of these behaviors represents good coding practice, but a user launching an AHK script with parameters from a CMD console window after setting some per-drive default directories would probably expect such relative references among the parameters to be respected.

JB
User avatar
lmstearn
Posts: 392
Joined: 11 Aug 2016, 02:32
GitHub: lmstearn
Contact:

Re: Per-Drive Default Directory

Post by lmstearn » 22 Dec 2020, 04:19

Yeah, that's the default flavour of the command processor. The internal commands PUSHD and POPD also have provision for handling one marvellous thing, the "current drive". And as noted here, the current directory holds no favour in UNC Paths.
SS64 is actually a great resource for anything related to cmd- also check out the undocumented environment vars. :)
:arrow: itros "ylbbub eht tuO kaerB" a ni kcuts m'I pleH
lexikos
Posts: 7185
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Per-Drive Default Directory

Post by lexikos » 27 Feb 2021, 07:43

It is neither a unique feature of CMD (in whole) nor in any way unique to AutoHotkey. The only part unique to CMD is that the CD command updates those environment variables. The actual implementation of resolving paths like "a:" and "a:b" is almost certainly in GetFullPathName or some underlying system function.

It doesn't seem to be documented explicitly but there are probably hints here and there. For instance,
If you specify "U:" the path returned is the current directory on the "U:\" drive
Source: GetFullPathNameA function (fileapi.h) - Win32 apps | Microsoft Docs
When you call a command line program and pass it a path like "D:somefile.txt" while the actual working directory is on C:, how do you think that program will find the file? It wouldn't be very useful, and probably wouldn't meet the goals of the feature, if it only worked within CMD's own internal commands.
relative references involving other drives are interpreted starting at the root of those drives
What gave you that idea? Of course if none of the special variables are set, each drive other than the "current" one will default to its root.

Another point of note is that shell-related functions (such as file dialogs) perform their own "strict" validation of paths which treat paths like "D:somefile.txt" as invalid, along with a bunch of other valid Win32 paths. (For instance, I think it is only recent that Explorer has allowed names beginning with a dot.)
Raymond Chen wrote:Win32 does not have the concept of a separate current directory for each drive
Either Raymond doesn't mean what you think, or he is mistaken (gasp!) or mispoke. He basically confirms there is such a thing as per-drive current directory in another post: How do I get the current directory for a non-current drive? Why would he be posting code used to retrieve the per-drive current directory using the standard Set & GetCurrentDirectory functions if such a concept did not even exist within Win32?


As for whether AutoHotkey's support for the concept was intentional, I think Chris wasn't even aware that "C:" is not the same as "C:\" -
// v1.0.45.01: Since A_ScriptDir omits the trailing backslash for roots of drives (such as C:),
// and since that variable probably shouldn't be changed for backward compatibility, provide
// the missing backslash to allow SetWorkingDir %A_ScriptDir% (and others) to work as expected
// in the root of a drive.
The documentation for A_ScriptDir says "The final backslash is omitted (even for root directories)."

I noticed it in 2018;
// Update in 2018: The reason it wouldn't by default is that "C:" is actually a reference to the
// the current directory if it's on C: drive, otherwise a reference to the path contained by the
// env var "=C:". Similarly, "C:x" is a reference to "x" inside that directory.
// For details, see https://blogs.msdn.microsoft.com/oldnewthing/20100506-00/?p=14133
// Although the override here creates inconsistency between SetWorkingDir and everything else
// that can accept "C:", it is most likely what the user wants, and now there's also backward-
// compatibility to consider since this workaround has been in place since 2006.
// v1.1.31.00: Add the slash up-front instead of attempting SetCurrentDirectory(_T("C:"))
// and comparing the result, since the comparison would always yield "not equal" due to either
// a trailing slash or the directory being incorrect.
When long path support was added in v1.1.31.00, it included some other fixes for bugs where Loop File misbehaved when given a path with a drive letter and no slash.

Other than what I've mentioned above, I don't think AutoHotkey has any specific allowances for per-drive working directories. Perhaps even what I've mentioned can't be considered such, as I think they are just as applicable when everything is on a single drive.


In case you missed the implication, SetWorkingDir C: is equivalent to SetWorkingDir C:\, whereas (probably) all other commands allow C: to be treated the standard Windows way, which would seem to depend on the =C: environment variable (if one exists and the "real" current directory is on a different drive).


Fun fact: the environment variables aren't limited to letters of the alphabet.

Code: Select all

SetWorkingDir %A_WinDir% ; To ensure A_ScriptDir is not the working directory. Presumably A_WinDir != A_ScriptDir.
EnvSet `=?:, %A_ScriptDir%
Run notepad ?:%A_ScriptName%
(This could be done without AutoHotkey, but I'm not sure how to set the variables using CMD.)

Also, it seems that C:\Windows\notepad.exe is not the same as C:\Windows\System32\notepad.exe on my system. It's missing its icon. :eh:
JBensimon
Posts: 33
Joined: 19 Nov 2017, 11:19

Re: Per-Drive Default Directory

Post by JBensimon » 27 Feb 2021, 09:09

Thank you for taking the time to provide such a detailed reply, lexikos.

You are quite right that the other Raymond Chen article you cite completely undermines the statement in the article I cited that "Win32 does not have the concept of a separate current directory for each drive", which seemed rather categorical and not open to any other interpretation than the one I gave it. I suppose it's possible that the API changed in this respect sometime between 2010 and 2017 -- I prefer that theory to the possibility that the great Raymond Chen may have been wrong about something Windows! ;) [it remains a feather in your cap that you were able to correct Him!]

So, bottom line, to answer my original question, AutoHotkey's respect of per-drive default directories is not explicitly implemented in code but is rather a simple consequence of the concept being enforced in the Win32 API, so isn't likely to go away.

I'll leave you with my own environment variable "fun fact": I was once called upon to troubleshoot a client's system that was failing to boot, and it turned out (after mounting and examining its image) that, as a result of a syntax mistake in a startup script (which coincidentally was written in AutoHotkey), an entry with empty data had been written to "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment", i.e. an empty environment variable had been created, which is an impossibility that clearly freaks out Windows (SET VAR= erases a variable rather than empties it).

Thanks again.
JBensimon
Posts: 33
Joined: 19 Nov 2017, 11:19

Re: Per-Drive Default Directory

Post by JBensimon » 27 Feb 2021, 10:11

Oh, one more thing: about your observation regarding the copy of notepad.exe in \Windows not displaying an icon (even though it's identical to the one in \Windows\System32), this is the result of the fact that notepad.exe itself doesn't contain any icon resource (in recent Windows versions) -- the icon displayed for it is drawn automatically from C:\Windows\SystemResources\notepad.exe.mun, and it seems that the contents of the SystemResources folder only apply to files in or under System32 or SysWOW64. EXEs directly in Windows (like regedit.exe for example) must contain their own icon(s). [It's a good guess that Microsoft did this to eliminate some redundancies in executables that appear in both System32 and SysWOW64 by consolidating most of their resources in a single .mun file under SystemResources, which would explain why executables elsewhere don't participate in this mechanism.]

As further evidence, a renamed copy of notepad.exe in System32 will display an icon if a corresponding copy of C:\Windows\SystemResources\notepad.exe.mun is created in the same folder (which requires temporarily taking ownership of that folder away from TrustedInstaller).

JB
lexikos
Posts: 7185
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Per-Drive Default Directory

Post by lexikos » 27 Feb 2021, 21:50

JBensimon wrote:
27 Feb 2021, 09:09
i.e. an empty environment [...] is an impossibility that clearly freaks out Windows (SET VAR= erases a variable rather than empties it).
That doesn't appear to generally be the case. It's quite easy to create an empty environment variable, and EnvSet has probably always done that when the value is omitted/blank.

Code: Select all

MsgBox % EnvVarSize("foo") ; 0, and A_LastError = ERROR_ENVVAR_NOT_FOUND (203)
RunWait cmd /c set foo & pause ; Show that foo does not exist.
EnvSet foo
MsgBox % EnvVarSize("foo") ; 1 (includes the null terminator)
Run cmd /c set foo & pause ; Show that foo exists but is empty.

EnvVarSize(name) {
    return DllCall("GetEnvironmentVariable", "str", "foo", "int*", 0, "uint", 0)
}
EnvSet foo is equivalent to DllCall("SetEnvironmentVariable", "str", "foo", "str", ""), whereas DllCall("SetEnvironmentVariable", "str", "foo", "ptr", 0) will delete the variable. SET presumably does the latter to conserve memory, since a non-existent environment variable is generally treated the same as an empty one.

AutoHotkey v2 deletes the variable if you use EnvSet("foo") rather than EnvSet("foo", "").
Post Reply

Return to “Suggestions on documentation improvements”