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

Posted

技术标签:

【中文标题】获取 cmd.exe 的当前工作目录【英文标题】:Get the current working directory for cmd.exe 【发布时间】:2010-09-18 02:12:38 【问题描述】:

如何?

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

我查看了 .NET Process 和 ProcessStartInfo 类(ProcessStartInfo.WorkingDirectory 总是返回“”),但似乎找不到确定这一点的方法。 PInvoke 也没有什么突出的。

作为一个例子,我希望能够以编程方式说出类似的内容: Process.GetCurrentWorkingDirectory(processID) 其中 processID 是另一个正在运行的进程的 Windows 进程 ID。

有什么解决方案,WinAPI 还是 .NET?

[更新]

问这个问题的原因:

我已经使用“命令提示符资源管理器栏”有一段时间了,它很棒,除非我“CD”到一个新目录,当前的资源管理器窗口也不会改变。 (即同步只是从资源管理器到命令提示符的一种方式)。我正在寻找这两种方式。

【问题讨论】:

您要求的内容听起来很可疑。一个进程不应干扰另一个进程,除非您正在测试 (QA) 或调试。将其用于“生产”或商业软件是不好的,因为它需要特权。你的产品不会得到。那么,你想完成什么? 公平的问题。没有什么鱼腥味。我已经使用“命令提示符资源管理器栏”有一段时间了,它很棒,除非我“CD”到一个新目录,当前的资源管理器窗口也不会改变。 (即同步只是从资源管理器到命令提示符的一种方式)。我正在寻找这两种方式。 我需要这个用于测试目的 - 我想杀死一个 java Tomcat 进程,并用它来区分它与其他方式。 【参考方案1】:

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

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

    获取 cmd.exe 进程的句柄。 在那里分配一些内存 (VirtualAllocEx) 将 DLL 的路径放入内存中。 (WriteProcessMemory) 将您的 dll 加载到 cmd.exe 地址空间。 (CreateRemoteThread,入口点为LoadLibrary,参数是你之前分配的内存。) WaitForSingleObject 后跟 GetExitCodeThread(),在 cmd.exe 进程中为您提供 DLL 的 HMODULE。 ReadProcessMemory 获取当前目录。 从 cmd.exe 地址空间卸载您的 dll。 CreateRemote Thread,入口点为 FreeLibrary,参数为 HMODULE。 WaitForSingleObject 等待 DLL 卸载。

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

【讨论】:

感谢您的详细回复。它看起来确实比查看 GetProcessStrings() 更复杂,但如果这不起作用,我可能会试一试。 没问题。 GetProcessStrings 方法可能适用于 cmd.exe 特殊情况,但我不确定。见上面我的cmets。我的方法适用于不修改环境变量的进程(如果它们更改目录)。 基本上,您不应该在 DllMain() 中执行任何操作,除了 TlsAlloc() 可能,因为“加载程序锁”。只需在谷歌上搜索“loader lock dllmain”,原因有很多,为什么这是一个坏主意。 请注意我的评论:“这应该没问题,因为这两个函数都在 kernel32 中......” 您必须小心不要触发 DLL 加载,否则您将遇到加载程序锁定问题。你受到了很大的限制,但这并不意味着你被 TlsAlloc 限制在任何事情上。【参考方案2】:

您是指批处理文件中的 %CD% 变量吗?

像这样:

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

在命令提示符下尝试 echo %CD%。 :)

这正是你所需要的,这里有一个 PowerShell 函数可以做到这一点:

$(get-location)

希望这会有所帮助。

我都是从here找到的。

【讨论】:

我希望以编程方式能够说出类似这样的话: Process.GetCurrentWorkingDirectory(processID) 其中 processID 是 Windows 进程 ID。【参考方案3】:

也许这个forum entry on the Sysinternals Forum 包含解决方案的提示。在 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 文章“Read Environment Strings of Remote process”也很有用。

【讨论】:

谢谢,我会研究 GetProcessStrings。我认为这可能与此有关。 这种方法可能出现的问题: 1. 读取目标进程的 env 和修改它的目标进程之间的竞争。 2. Windows 版本之间的幻数变化。 3. %CD% 在 cmd.exe 中似乎很特殊,可能不是 envvar。例如。尝试“设置 CD=blah”;它在 cd 上停止变化。【参考方案4】:

更新:不是解决方案,请参阅此答案的 cmets:“正如 Janm 所说 .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);
                


            

        
    

这里是我机器上的输出(我打开了四个命令行):

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

【讨论】:

那不行!这给出了模块的目录,而不是进程的当前目录。在您的测试中,其中一行应该是 C:\Users\stefan。 正如 Janm 所说,.Modules[0].FileName(或 MainModuile.FileName)给出了在该进程中运行的可执行文件的位置。我正在寻找当前的工作目录(可以使用 CD 或 CHDIR 命令更改)。 天啊!如果您研究然后忘记核心问题,就会发生这种情况。我道歉...【参考方案5】:

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

Environment.CurrentDirectory()

【讨论】:

【参考方案6】:

请参阅my answer 到类似的问题(我自己)。我编写了一个命令行实用程序和 C# 包装器来读取进程环境变量。对于我的问题(获取 java 的当前目录),我只需阅读 catalina_base 目录。

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

【讨论】:

以上是关于获取 cmd.exe 的当前工作目录的主要内容,如果未能解决你的问题,请参考以下文章

Java获取程序运行的当前工作目录

如何获取 Xcode Playground 的当前工作目录?

如何在 Java 中获取当前工作目录?

如何获取 Qt 应用程序的当前工作目录路径?

获取当前目录getcwd,设置工作目录chdir,获取目录信息

在 Perl 中获取当前工作目录路径