【问题标题】:Changing the current working directory of cmd (from a child process)更改 cmd 的当前工作目录(来自子进程)
【发布时间】:2020-06-05 22:40:32
【问题描述】:

所以我正在尝试编写一个类似cd 的程序,该程序可以使用 cmd 执行,并且在它退出调用 cmd 进程的工作目录后应该更改。

现在,在这篇文章被标记为重复之前:我知道 thisthis question 被要求解决几乎这个确切的问题,但使用 Linux 而不是 Windows,并且非常广泛和不具体,我我知道类似的限制也适用于 Windows(更改我的进程的工作目录不会更改父进程的工作目录)。

实际上有一个有效的solution to this for linux。但是它为此使用gdb,我想只使用内置的Windows实用程序(WinAPI、dotNET、)来完成这个任务。

到目前为止我所尝试的

我确实设法使用 Cheat Engine 和 OpenProcess() / WriteProcessMemory() WinAPI 函数成功覆盖了 cmd 的工作目录。然而,这个解决方案让人感觉草率且效果不佳(或者至少需要投入更多的工作。)

我的问题

在 Windows 上是否有不同的(可能更简单?)方法来实现这一点?就像一种调用/注入代码到 cmd 进程以直接执行cd whatever\directory\I\want 而不覆盖其内存的方法?我已经看到了 CreateRemoteThread() 函数,但是我没有设法找到使用它们的方法。

仅供参考:我主要使用 C#,但 C/C++ 解决方案也应该有所帮助,只要它们基于本地 Microsoft 库。

【问题讨论】:

  • Why does each drive have its own current directory? 这对你来说意味着你必须修改 Windows 命令解释器的内部逻辑。没有公共 API 可以可靠地做到这一点。您将不得不以一种或另一种方式破解事物。或者做显而易见的事情:使用cd。既然你还没有说清楚,为什么不使用cd,这个问题似乎不是一个实际的编程问题。
  • 那么,您想从正在运行的可执行文件中以编程方式更改目录位置,然后从新位置执行其他任务? (例如:cd <someNewLocation> && someProgram.exe
  • @ryyker:您在哪里看到目录更改后运行其他任务的请求?
  • 可能这样:获取父窗口hwnd Win32指针,然后使用user32.dll API的SendInput发送按键序列:)
  • @aep:为什么你需要一个窗口句柄来调用SendInput

标签: c# c .net winapi


【解决方案1】:

这篇文章描述了一个Windows 函数的实现,该函数启动一个子进程,创建到stdinstdout 的管道,从中发送命令并返回响应。最后,一旦捕获了所有响应,子进程就会终止。如果这听起来很熟悉,那么它在概念上类似于 Linux 的 popen() 函数,只是这个实现是专门创建的,用于捕获响应到任何返回命令的缓冲区中一。 (还包括一个变体,可在预期或需要无响应时使用。)

full source 可以调整为在独立的可执行文件中使用,或作为 API 使用。 (.dll) 无论哪种方式,生成的函数都接受并使用标准 Windows CMD 语法处理任何命令。函数cmd_rsp(...) 通过stdout 将Windows 响应返回到一个自调整大小的缓冲区中。

导出的原型是:

int __declspec(dllexport) cmd_rsp(const char *command, char **chunk, unsigned int size);  
int __declspec(dllexport) cmd_no_rsp(const char *command);  

捕获响应时的一个简单用例:

#include "cmd_rsp.h"
int main(void)
{
    char *buf = {0};
    buf = calloc(100, 1);//initialize to some initial size
    if(!buf)return 0;
    cmd_rsp("dir /s", &buf, 100);//buffer will grow to accommodate response as needed.
    printf("%s", buf);
    free(buf);

    return 0;
}

不需要响应时的简单用例:

#include "cmd_rsp.h"
int main(void)
{
    cmd_no_rsp("cd C:\\dir1\\dir2");

    return 0;
}

上面提供的链接中描述了目的和用法的详细说明。为了说明,这里有一些示例命令输入,在这种情况下,每个命令输入都会更改工作目录,然后从该目录执行命令:

一个命令切换到sqlite目录,然后执行一个查询:

cd c:\\tempExtract\\sqlite\\Tools\\sqlite-tools-win32-x86-3250300 && sqlite3.exe .\\extract.db \"select * from event, eventdata where eventType=38 and eventdata .eventid=event.eventid\

切换到teraterm目录的命令,然后执行脚本:

"c:\\Program Files (x86)\\teraterm\" && ttpmacro c:\\DevPhys\\LPCR_2\\play\\Play.ttl

一个命令改变目录然后执行一个命令发送多个数字采集通道设置。

cd C:\\Dir1\\Dir2\\Dir3\\support\\Exes\\WriteDigChannel && .\\WriteDigChannel.exe P1_CH0 1 &&  .\\WriteDigChannel.exe P1_C H0 0 &&  .\\WriteDigChannel.exe P1_CH0 1

从指定位置递归目录搜索:

cd C:\\dir1\\dir2 && dir /s /b  

【讨论】:

    【解决方案2】:

    我让它工作了。正如SendInput 所建议的那样,终于成功了。 我结合使用 WinAPI 调用 GetForegroundWindow() / SetForegroundWindow() 和 Windows Forms System.Windows.Forms.SendKeys.SendWait() 方法来实现我想要的:

    在调用我的cd-wrapper 程序 (sd.exe) 并提供我的自定义目标目录 (~/home) 后,它会生成相应的命令以及要发送给它的“Enter-Pressed-Event”父 cmd 进程。

    这是完整的 C# 代码:

    if (args.Length != 1)
    {
        Console.WriteLine(Directory.GetCurrentDirectory());
        return;
    }
    string targetDirectory = args[0];
    string command = string.Empty;
    if (targetDirectory.Equals("~"))
    {
        command = @"pushd C:\Users\fred\Desktop";
    }
    else if (!Directory.Exists(targetDirectory))
    {
        Console.WriteLine("I/O Error: No such file or directory.");
        return;
    }
    else
    {
        command = @"cd " + targetDirectory;
    }
    Target target = Target.Create(Process.GetCurrentProcess().GetParentProcess());
    target.SendKeys(command + "{ENTER}", true);
    

    请注意,我开始为这个项目和类似问题编写一个完整的框架,其中包含我对这个问题的所有不同方法和低级 WinAPI 调用以及获取父进程的扩展方法:D

    由于在此答案中粘贴所有代码有点过分,这里是GitHub。如果我能找到时间,我会继续优化代码,但现在就可以了。希望这可以帮助遇到类似问题的人:)

    编辑:

    一个更“干净”的方法是使用 dll 注入直接让 cmd 切换它的工作目录。虽然开始工作要困难得多,但与上述方法相比,它的优点是不会乱扔 cmd 命令历史记录。除此之外 cmd 似乎知道对其当前工作目录的任何更改,因此它会自动更新提示文本。一旦我有了一个完整的工作示例,它允许动态指定目标目录,我将在此处发布它:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-03
      • 1970-01-01
      • 2014-11-14
      • 2014-12-02
      • 2015-02-22
      • 2022-10-14
      相关资源
      最近更新 更多