获取cmd.exe的当前工作目录

16

如何检索cmd.exe的当前工作目录?

这似乎是可能的。例如,使用ProcessExplorer,选择CMD.exe,右键单击,属性,图像选项卡,“当前目录”反映使用CD或CHDIR命令设置的目录。

我查看了.NET Process和ProcessStartInfo类(ProcessStartInfo.WorkingDirectory始终返回“”),似乎找不到确定此内容的方法。在PInvoke中也没有明显的解决方案。

例如,我想要以编程方式能够说出:Process.GetCurrentWorkingDirectory(processID),其中processID是另一个正在运行的Windows进程的进程ID。

是否有任何解决方案,WinAPI或.NET?

[更新]

提出此问题的原因:

我已经使用“命令提示符资源管理器栏”一段时间了,它非常好用,但是如果我“CD”到新目录,则当前的资源管理器窗口不会更改。(即同步只从资源管理器到命令提示符是单向的)。我正在寻求使其双向的方法。


你所要求的听起来有些可疑。一个进程不应该干扰另一个,除非你正在进行测试(QA)或调试。在“生产”或商业软件中使用这种方法是不好的,因为它需要特权。而你的产品得不到这个特权。那么,你想要实现什么? - jim
公平的问题。没有什么可疑的。我已经使用“命令提示符资源管理器栏”有一段时间了,它非常棒,但是如果我“CD”到一个新目录,当前的资源管理器窗口并不会随之改变。(即同步只从资源管理器到命令提示符,而不是双向同步)。我想要实现双向同步。 - Ash
我需要这个进行测试目的 - 我想要杀死一个Java Tomcat进程,并将其作为区分其他方式的一种方法。 - ripper234
6个回答

6

未经测试,一个可能的方法:

创建一个带有DllMain的DLL,使用GetThreadStartInformation()查找缓冲区的地址,然后使用GetCurrentDirectory填充它。这应该是可以的,因为这两个函数都在kernel32中,而kernel32始终存在。你需要在那里设置一些结构来返回成功/失败。

  1. 获取cmd.exe进程的句柄。
  2. 在其中分配一些内存(VirtualAllocEx)
  3. 将你的DLL路径放入内存中。(WriteProcessMemory)
  4. 将你的dll加载到cmd.exe地址空间中。(CreateRemoteThread,入口点为LoadLibrary,参数为之前分配的内存。)
  5. WaitForSingleObject,然后使用GetExitCodeThread(),可以得到cmd.exe进程中你的DLL的HMODULE。
  6. 使用ReadProcessMemory获取当前目录。
  7. 从cmd.exe地址空间卸载你的dll。使用CreateRemote Thread,入口点为FreeLibrary,参数为HMODULE。
  8. 使用WaitForSingleObject等待DLL卸载。

大致概述:细节留作练习!风险:在cmd.exe地址空间中分配内存,改变其状态。必须小心使用DllMain中调用的函数。


感谢详细的回复。它似乎比查看GetProcessStrings()要复杂一些,但如果那个方法行不通的话,我可能会尝试一下。 - Ash
没问题。GetProcessStrings方法在cmd.exe特殊情况下可能有效,但我不确定。请参见上面的评论。我的方法适用于不修改环境变量并更改目录的进程。 - janm
基本上,在DllMain()函数中除了TlsAlloc()之外,不要做任何事情,因为存在“加载器锁定”的问题。只需要在Google中搜索“loader lock dllmain”,就会有很多原因说明这是一个坏主意。 - Christian.K
注意我的注释:“这应该没问题,因为这两个函数都在 kernel32 中…” 你必须小心,不要触发 DLL 加载,否则你将遇到加载器锁定的问题。虽然受到很大的限制,但这并不意味着你只能使用 TlsAlloc。 - janm

5
也许这个在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文章 "读取远程进程的环境变量" 也许会很有用。

谢谢,我会研究一下GetProcessStrings。我觉得可能与它有关。 - Ash
这种方法可能存在以下问题:
  1. 读取目标进程的环境变量和目标进程修改它之间的竞争。
  2. Windows 版本更新后,魔数会发生变化。
  3. 在 cmd.exe 中,%CD% 似乎是特殊的,可能不是一个环境变量。例如,尝试 "set CD=blah";它在 cd 上停止更改。
- janm

5
你是指批处理文件中的%CD%变量吗?像这样:

如何使用 %CD% 变量:

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

在命令提示符中尝试输入echo %CD%

既然这是您需要的,那么这里有一个PowerShell函数可以实现:

$(get-location)

我找到了这个网站:这里,希望对你有所帮助。

我正在寻找一种编程方式来实现像这样的操作:Process.GetCurrentWorkingDirectory(processID),其中processID是Windows进程ID。 - Ash

2

更新: 这不是解决方案,请参见此答案的评论: "如Janm所说.Modules[0].FileName (或MainModuile.FileName)给出了在该进程中运行的可执行文件的位置。我正在寻找当前工作目录(可以使用CD或CHDIR命令更改)。"

您可以使用System.Diagnostics Namespace。这里是一个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


那不起作用!那会给出模块的目录,而不是进程的当前目录。在你的测试中,其中一行应该是C:\Users\stefan。 - janm
正如Janm所说,Modules [0] .FileName(或MainModule.FileName)给出了在该进程中运行的可执行文件的位置。我正在寻找当前工作目录(可以使用CD或CHDIR命令更改)。 - Ash
糟糕!如果你调研后又忘记了核心问题,就会发生这种情况。我道歉... - splattne

1

尝试这个简单的环境属性:

Environment.CurrentDirectory()


0

请参考我的答案,它与此类似。我编写了一个命令行实用程序和C#包装器来读取进程环境变量。对于我的问题(获取Java的当前目录),我只需读取catalina_base目录。

我不确定这是否直接适用于cmd.exe。Code Project文章中提供的实用程序无法与cmd.exe一起使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接