Получить текущий рабочий каталог для cmd.exe

Как я могу получить текущий рабочий каталог cmd.exe?

Это кажется возможным. Например, используя ProcessExplorer, выберите CMD.exe, щелкните правой кнопкой мыши, свойства, вкладка «Изображение», «Текущий каталог» отображает каталог, установленный с помощью команд CD или CHDIR.

Я просмотрел классы .NET Process и ProcessStartInfo (ProcessStartInfo.WorkingDirectory всегда возвращает "") и не могу найти способ определить это. На PInvoke также ничего не выделяется.

В качестве примера я хочу, чтобы программно можно было сказать что-то вроде: Process.GetCurrentWorkingDirectory(processID), где processID — это идентификатор процесса Windows другого запущенного процесса.

Есть ли решение, WinAPI или .NET?

[Обновлять]

Причина задать этот вопрос:

Я некоторое время использовал «Панель проводника командной строки», и это здорово, за исключением того, что если я «перехожу» в новый каталог, текущее окно проводника также не меняется. (т.е. синхронизация только 1 путь от проводника до командной строки). Я хочу сделать это 2 способами.


person Ash    schedule 20.10.2008    source источник
comment
То, что вы просите, звучит подозрительно. Процесс не должен мешать другому, ЕСЛИ вы не тестируете (QA) или отлаживаете. Использование этого для производственного или коммерческого программного обеспечения плохо, так как требует привилегий. Чего не получит ваш продукт. Итак, чего вы пытаетесь достичь?   -  person jim    schedule 20.10.2008
comment
Справедливый вопрос. Ничего подозрительного. Я некоторое время использовал панель проводника командной строки, и это здорово, за исключением того, что если я перехожу в новый каталог, текущее окно проводника также не меняется. (т.е. синхронизация только 1 путь от проводника до командной строки). Я хочу сделать это 2 способами.   -  person Ash    schedule 21.10.2008
comment
Мне нужно это для целей тестирования - я хочу убить процесс java Tomcat и использовать это как способ отличить его от других способов.   -  person ripper234    schedule 13.01.2009


Ответы (6)


Непроверенный, возможный подход:

Создайте DLL с DllMain, которая использует GetThreadStartInformation() для поиска адреса буфера, а затем использует GetCurrentDirectory для его заполнения. Это должно быть в порядке, потому что обе эти функции находятся в ядре32, которое всегда присутствует. Вам нужно будет иметь некоторую структуру, чтобы возвращать успех/неудачу.

  1. Получите дескриптор процесса cmd.exe.
  2. Выделите там немного памяти (VirtualAllocEx)
  3. Поместите путь к вашей DLL в память. (Память процесса записи)
  4. Загрузите вашу dll в адресное пространство cmd.exe. (CreateRemoteThread с точкой входа LoadLibrary, аргументом является память, которую вы выделили ранее.)
  5. WaitForSingleObject, за которым следует GetExitCodeThread(), дает вам HMODULE вашей DLL в процессе cmd.exe.
  6. ReadProcessMemory, чтобы получить текущий каталог.
  7. Выгрузите вашу dll из адресного пространства cmd.exe. CreateRemote Thread с точкой входа FreeLibrary, аргументом является HMODULE.
  8. WaitForSingleObject для ожидания выгрузки DLL.

Общий набросок: Детали оставлены в качестве упражнения! Риски: Выделяет память в адресном пространстве cmd.exe, изменяет ее состояние. Следует соблюдать осторожность с функциями, вызываемыми в DllMain.

person janm    schedule 20.10.2008
comment
Спасибо за подробный ответ. Это кажется немного более сложным, чем, скажем, GetProcessStrings(), но я могу попробовать, если это не сработает. - person Ash; 21.10.2008
comment
Без проблем. Подход GetProcessStrings может работать в особом случае cmd.exe, но я не уверен. Смотрите мои комментарии выше. Мой подход будет работать с процессами, которые не изменяют переменные среды, если они меняют каталоги. - person janm; 21.10.2008
comment
По сути, вы не должны ничего делать в DllMain(), кроме, может быть, TlsAlloc() из-за блокировки загрузчика. Просто поищите в Google блокировку загрузчика dllmain по множеству причин, по которым это плохая идея. - person Christian.K; 15.01.2009
comment
Обратите внимание на мой комментарий: это должно быть в порядке, потому что обе эти функции находятся в kernel32 ... Вы должны быть осторожны, чтобы не вызвать загрузку DLL, иначе у вас возникнут проблемы с блокировкой загрузчика. Вы очень ограничены, но это не значит, что TlsAlloc не ограничивает вас ничем. - person janm; 15.01.2009

Вы имеете в виду переменную %CD% в пакетном файле?

Нравится:

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

Попробуйте вывести %CD% в командной строке. :)

Это то, что вам нужно, вот функция PowerShell для этого:

$(get-location)

Надеюсь это поможет.

Я нашел все это на здесь.

person Veynom    schedule 20.10.2008
comment
Я хочу, чтобы программно можно было сказать что-то вроде: Process.GetCurrentWorkingDirectory(processID), где processID — это идентификатор процесса Windows. - person Ash; 20.10.2008

Возможно, эта запись на форуме Sysinternals содержит намек на решение. Ищите это в функции GetProcessStrings:

// 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.

Эта статья CodeProject "Чтение строк среды удаленного процесса" также может оказаться полезной.

person splattne    schedule 20.10.2008
comment
Спасибо, я посмотрю на GetProcessStrings. Я подумал, что это может быть как-то связано с этим. - person Ash; 21.10.2008
comment
Возможные проблемы с этим подходом: 1. Гонки между чтением env целевого процесса и целевым процессом, изменяющим его. 2. Магические числа меняются между выпусками Windows. 3. %CD% кажется особенным в cmd.exe и может не быть envvar. например. Попробуйте установить CD=blah; он перестает меняться на компакт-диске. - person janm; 21.10.2008

ОБНОВЛЕНИЕ: это не решение, см. комментарии к этому ответу: "Как сказал Джанм, .Modules[0].FileName (или MainModuile.FileName) дает расположение исполняемого файла, запущенного в этом процессе. Мне нужно найти текущий рабочий каталог (который можно изменить с помощью команд CD или CHDIR)."

Вы можете использовать пространство имен System.Diagnostics. Вот пример консольного приложения C#. Из имени файла вы можете легко извлечь информацию о пути (System.IO.Path...).

Вы должны убедиться, что у вас есть права (запуск от имени администратора) для этого.

Надеюсь это поможет. Вот рабочий код (проверено):

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);
                }


            }

        }
    }
}

Вот вывод на моей машине (я открыл четыре командные строки):

замещающий текст http://img236.imageshack.us/img236/3547/processworkingdirvn4.png< /а>

person splattne    schedule 20.10.2008
comment
Это не работает! Это дает каталог модуля, а не текущий каталог процесса. В вашем тесте одна из строк должна была быть C:\Users\stefan. - person janm; 20.10.2008
comment
Как сказал Янм, .Modules[0].FileName (или MainModuile.FileName) указывает местоположение исполняемого файла, работающего в этом процессе. Я ищу текущий рабочий каталог (который можно изменить с помощью команд CD или CHDIR). - person Ash; 21.10.2008
comment
О! Это происходит, если вы исследуете, а затем забываете основной вопрос. Я прошу прощения... - person splattne; 21.10.2008

Попробуйте это простое свойство среды:

Окружающая среда.CurrentDirectory()

person Community    schedule 05.03.2009

См. мой ответ на аналогичный вопрос (от меня). Я написал утилиту командной строки и оболочку C# для чтения переменных среды процесса. На мой вопрос (получение текущего каталога для java) я просто прочитал каталог catalina_base.

Я не уверен, относится ли это непосредственно к cmd.exe. Утилита, представленная в статье Code Project, не работала с cmd.exe.

person ripper234    schedule 14.01.2009