16

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.

Ash
  • 60,973
  • 31
  • 151
  • 169
  • What you are asking for sounds fishy. A process should not be interfering with another UNLESS you are testing (QA) or debugging. Using this for "production" or commercial software is bad as it requires privileges. Which your product won't get. So, What are you trying to accomplish ? – jim Oct 20 '08 at 12:23
  • Fair question. Nothing fishy. 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. – Ash Oct 21 '08 at 00:40
  • I need this for testing purposes - I want to kill a java Tomcat process, and using this as a way to distinguish it from other ways. – ripper234 Jan 13 '09 at 17:27

6 Answers6

6

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
  • 17,976
  • 1
  • 43
  • 61
  • 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 Oct 21 '08 at 00:44
  • 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 Oct 21 '08 at 08:02
  • 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 Jan 15 '09 at 11:53
  • 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 Jan 15 '09 at 12:11
5

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
  • 4,079
  • 2
  • 19
  • 24
  • I'm looking to programmatically be able to say something like: Process.GetCurrentWorkingDirectory(processID) where processID is a windows process ID. – Ash Oct 20 '08 at 10:14
5

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
  • 102,760
  • 52
  • 202
  • 249
  • Thanks, I'll look into GetProcessStrings. I thought it could be something to do with that. – Ash Oct 21 '08 at 00:41
  • 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 Oct 21 '08 at 08:05
2

UPDATE: This is not the solution, see the comments to this answer: "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)."

You could use System.Diagnostics Namespace. Here an example as C# console application. From the filename you can easily extract the Path information (System.IO.Path...).

You would make sure to have the permissions (run as admin) to do this.

Hope this helps. Here is the working code (tested):

using System;
using System.Diagnostics;
using System.IO;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Process[] procList =  Process.GetProcessesByName("cmd");

            string sFileName;

            for (int i = 0; i < procList.Length; i++ )
            {
                Console.Write(procList[i].Id);
                Console.Write(" ");

                try
                {
                    sFileName = procList[i].Modules[0].FileName;
                    Console.Write("(");
                    Console.Write(Path.GetFileName(sFileName));
                    Console.Write("): ");
                    Console.WriteLine(Path.GetDirectoryName(sFileName));
                }
                catch (Exception ex)
                {
                    // catch "Access denied" etc.
                    Console.WriteLine(ex.Message);
                }


            }

        }
    }
}

Here the the output on my machine (I've opened four command lines):

alt text http://img236.imageshack.us/img236/3547/processworkingdirvn4.png

splattne
  • 102,760
  • 52
  • 202
  • 249
  • 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 Oct 20 '08 at 19:49
  • 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 Oct 21 '08 at 00:48
  • D'oh! That happens, if you research and then forget the core question. I apologize... – splattne Oct 21 '08 at 06:29
1

Try this simple environment property:

Environment.CurrentDirectory()

0

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.

Community
  • 1
  • 1
ripper234
  • 222,824
  • 274
  • 634
  • 905