views:

12688

answers:

6

How can I retrieve the current working directory of cmd.exe?

This seems possible. For example using ProcessExplorer, select CMD.exe, right click, properties, Image tab, "Current Directory" relects the directory set using the CD or CHDIR commands.

I've looked at the .NET Process and ProcessStartInfo classes (ProcessStartInfo.WorkingDirectory always returns "") and can't seem to find a way of determining this. Nothing at PInvoke stands out either.

As an example I'm looking to programmatically be able to say something like: Process.GetCurrentWorkingDirectory(processID) where processID is a Windows process ID of another running process.

Is there any solution, WinAPI or .NET?

[Update]

Reason for asking this question:

I've used the "Command Prompt Explorer Bar" for a while and it's great except if I "CD" to a new directory, the current Explorer window does not also change. (ie the Sync is only 1 way from Explorer to the commmand prompt). I'm looking to make this 2 way.

A: 

Do you mean the %CD% variable in a batch file ?

Like this:

set OLDDIR=%CD%
.. do stuff ..
chdir /d %OLDDIR% &rem restore current directory

Try echo %CD% in a command prompt. :)

Well as this is what you need, there here is a PowerShell function to do it:

$(get-location)

Hope this helps.

I found it all from here.

Veynom
I'm looking to programmatically be able to say something like: Process.GetCurrentWorkingDirectory(processID) where processID is a windows process ID.
Ash
+4  A: 

Untested, a possible approach:

Create a DLL with a DllMain that uses GetThreadStartInformation() to find the address of the buffer, and then uses GetCurrentDirectory to populate it. This should be OK, because both of those functions are in kernel32, which is always present. You will need to have some structure there to return success/failure.

  1. Get a handle to the cmd.exe process.
  2. Allocate some memory there (VirtualAllocEx)
  3. Put the path to your DLL in the memory. (WriteProcessMemory)
  4. Load your dll into the cmd.exe address space. (CreateRemoteThread with an entry point of LoadLibrary, the argument is the memory you allocated earlier.)
  5. WaitForSingleObject followed by GetExitCodeThread(), gives you the HMODULE of your DLL in the cmd.exe process.
  6. ReadProcessMemory to get the current directory.
  7. Unload your dll from the cmd.exe address space. CreateRemote Thread with an entry point of FreeLibrary, the argument is the HMODULE.
  8. WaitForSingleObject to wait for the DLL to unload.

Broad sketch: Details left as an excercise! Risks: Allocates memory in the cmd.exe address space, changes its state. Care must be taken with the functions called in DllMain.

janm
Thanks for the detailed response. It does appear a bit more complex than say looking at GetProcessStrings(), but I might give it a go if that does not work out.
Ash
No problem. The GetProcessStrings approach might work in the cmd.exe special case, but I'm not sure. See my comments above. My approach will work with processes that don't modify environment variables are they change directories.
janm
Basically, you shouldn't do anything in DllMain() except for TlsAlloc() maybe, because of the "loader lock". Just google for "loader lock dllmain" for plenty of reasons why it is a bad idea.
Christian.K
Note my comment: "This should be OK, because both of those functions are in kernel32 ..." You must be careful not to trigger a DLL load otherwise you will have problems with the loader lock. You are very constrained, but that doesn't mean you are restricted to nothing by TlsAlloc.
janm
+1  A: 
splattne
That doesn't work! That gives the directory of the module, not the current directory of the process. In your test, one of the lines should have been C:\Users\stefan.
janm
As Janm said .Modules[0].FileName (or MainModuile.FileName) gives the location of the executable running in that process. I'm looking to find the current working directory (that can be changed using the CD or CHDIR commands).
Ash
D'oh! That happens, if you research and then forget the core question. I apologize...
splattne
+4  A: 

Maybe this forum entry on the Sysinternals Forum contains a hint to the solution. Look for this in the GetProcessStrings function:

// Get Command Line Block

// At offset 0x00020498 is the process current directory followed by

// the system PATH. After that is the process full command line, followed

// by the exe name and the windows station it's running on.

This CodeProject article "Read Environment Strings of Remote process" could be useful too.

splattne
Thanks, I'll look into GetProcessStrings. I thought it could be something to do with that.
Ash
Possible issues with this approach: 1. Races between reading the target process's env and the target process modifying it. 2. Magic numbers change between releases of Windows. 3. %CD% seems to be special in cmd.exe and might not be an envvar. eg. Try "set CD=blah"; it stops changing on cd.
janm
A: 

See my answer to a similar question (by myself). I wrote a command line utility and C# wrapper to read the process environment variables. For my question (getting current dir for java), I simply read the catalina_base directory.

I'm not sure if this applies directly to cmd.exe. The utility provided in the Code Project article failed to work with cmd.exe.

ripper234
A: 

Try this simple environment property:

Environment.CurrentDirectory()