It appears that Wine is storing and using a "current directory" for each DOS drive. According to some testing we have just done on Windows 2000 and Windows 98, Windows only stores a current directory for the current drive.
"But CMD.EXE seems to remember drives, and their children too", comes the reply.
Well, sort of. The documentation for CreateProcess talks about environment variables used to hold the current directory for each drive, with an environment variable with a name of the form "=C:" having the value, say "C:\WINDOWS". That documentation suggests you must make sure all these environment variables are set if you construct a new environment block, and that you need to get the values of them using "GetFullPathName". Shock, horror, the Windows documentation is wrong.
What actually happens is that CMD.EXE and COMMAND.COM independently set those environment variables when you change directories in those shells. SetCurrentDirectory has no effect on those environment variables - either in the current process or in child processes. It is the responsibility of the application to set them if it wants the children to have access to its current directory on drives other than its current drive.
However, SetCurrentDirectory("D:") (assuming a starting point of drive "C") will read those environment variables to determine which directory of drive "D" to go to. You can even 'SetEnvironmentVariable("=D:", "\foo")' to make the next 'SetCurrentDirectory("D:")' end up in "D:\foo".
GetFullPathName also picks up the environment variable if the path requested is not on the current drive (although it also checks that the path is valid, so 'GetFullPathName("A:\", ...)' will rattle drive A if drive A is empty). If there's no environment variable, both GetFullPathName and SetCurrentDirectory assume "x:".
If the drive in question is the current drive, SetCurrentDirectory and GetFullPathName both ignore the environment variables and use the value from GetCurrentDirectory. So, for example:
// Start with no "=D:" variable in environment SetCurrentDirectory("d:\bar"); SetCurrentDirectory("c:"); SetCurrentDirectory("d:"); // Current directory now d:\ SetCurrentDirectory("d:\bar"); SetCurrentDirectory("c:"); SetEnvironmentVariable("=D:", "\foo"); SetCurrentDirectory("d:"); // Current directory now "d:\foo"; SetEnvironmentVariable("=D:", "\bar"); SetCurrentDirectory("d:"); // Current directory still d:\foo
The Borland C++ run-time libraries have code in their chdir functions that sets these variables, but it's inside a "#if 0" section, presumably reflecting an attempt to be compatible with MSC.
I'm not aware of anything that relies on this perverse behaviour, so whether Wine needs to be adjusted to mimic it may be an open question. It is probably of more interest to people who want to pass an environment block to CreateProcess. If you're doing that, ignore what the documentation says about including "=C:" & etc environment variables in the environment block - simply start with the environment block returned by GetEnvironmentBlock and make the desired changes, ensuring the block is sorted according to instructions I gave previously on one of the Cygwin mailing lists.
Troy Rollo wrote:
It appears that Wine is storing and using a "current directory" for each DOS drive. According to some testing we have just done on Windows 2000 and Windows 98, Windows only stores a current directory for the current drive.
We've just implemented the behavior you describe in ntdll. (except for the part on cd D: with curr drive already being D: which I'm not sure we have set correctly). However, kernel hasn't been updated (yet) to use ntdll for files & path related functions. This should be done in the coming week^H^H^H^Hmonths
A+