2

So I am trying to write a cd -like program that can be executed using cmd and after it exits the working directory of the calling cmd process should be changed.

Now before this post is flagged as a duplicate: I am aware of this and this question that were asked for pretty much this exact problem but using Linux instead of Windows as well as being pretty broad and unspecific, and I am aware that similar limitations apply to Windows as well (changing the working directory of my process will not change the parent’s working directory).

There is actually is a working solution to this for linux. However it is using gdb for this, and I would like to achieve this task using only built-in Windows utilities (WinAPI, dotNET, etc.).

What I have tried so far

I did manage to use Cheat Engine and the OpenProcess() / WriteProcessMemory() WinAPI funtions to successfully override cmd's working directory. However this solution feels sloppy and doesn't work well (or at least requires more work to be put into.)

My question

Is there a different (maybe simpler?) way on Windows to achieve this? Like a way to invoke/inject code to the cmd process to execute cd whatever\directory\I\want directly without overriding its memory? I have seen the CreateRemoteThread() functions however I didn't manage to find a way to put them to use.

FYI: I am mainly using C# but C/C++ solutions should help too as long as they are based on the native Microsoft libraries.

Frederik Hoeft
  • 1,177
  • 1
  • 13
  • 37
  • [Why does each drive have its own current directory?](https://devblogs.microsoft.com/oldnewthing/20101011-00/?p=12563) What this means for you is that you have to modify internal logic of Windows' command interpreter. There is no public API to reliably do that. You'll have to hack things one way or another. Or do the obvious: Use `cd`. Since you haven't made clear, why you aren't using `cd`, this question doesn't appear to be about a practical programming problem. – IInspectable Feb 21 '20 at 13:44
  • So, from within a running executable you want to change directory location programmatically, then from the new location execute some other task? ( Like for example: `cd && someProgram.exe` ) – ryyker Feb 21 '20 at 13:55
  • @ryyker: Where do you see a request to run some other task after the directory change? – Eric Postpischil Feb 21 '20 at 14:06
  • I am actually trying to write a wrapper for cd to be able to use shortcuts for common directories like in Linux the ~ to go to the home directory quickly and similar shortcuts for other common directories. That's why cd simply won't do it as the level of convenience is missing :) – Frederik Hoeft Feb 21 '20 at 14:09
  • If you want that level of convenience, you're looking to write a full command interpreter. Or just use one, that's already available (like the [WSL](https://learn.microsoft.com/en-us/windows/wsl/faq), [Git BASH](https://gitforwindows.org/), etc.). – IInspectable Feb 21 '20 at 14:14
  • @EricPostpischil - It is a question to determine if that is what OP is after, not a statement :) It seemed to be an implied interest from the context of what OP is talking about. And something similar to what I needed to do awhile back. – ryyker Feb 21 '20 at 14:43
  • @FrederikHoeft: The way that is usually done is to write a shell script (Windows “batch file”), which can change the command interpreter’s directory because it is executed by the command interpreter itself. Aside from various commands provided by the commander interpreter, a batch file can execute an arbitrary program you write that produces a string for the desired directory as output, which the batch file then uses to set the current directory. – Eric Postpischil Feb 21 '20 at 14:50
  • 1
    Maybe this way: Get window hwnd Win32 pointer of the parent, then use `SendInput` of user32.dll API for sending key sequence :) – aepot Feb 21 '20 at 18:00
  • 1
    @aep: Why would you need a window handle to call [SendInput](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput)? – IInspectable Feb 21 '20 at 18:24
  • Ah, sorry. Forgot the exact arguments list. Unfortunately in this case it's not usable. Thanks for corrections. Maybe [this way](https://stackoverflow.com/questions/12805345/send-combination-of-keystrokes-to-background-window) then :) – aepot Feb 21 '20 at 18:30
  • [No](https://devblogs.microsoft.com/oldnewthing/20050530-11/?p=35513). – IInspectable Feb 22 '20 at 02:18

2 Answers2

0

This post describes a Windows implementation of a function that launches a child process, creates pipes to stdin and stdout from which a command is sent, and a response is returned. Finally, once all response is captured the child process is terminated. If this sounds familiar it is similar in concept to Linux's popen() function with the exception that this implementation was specifically created to capture the response into a buffer of any command that returns one. (Also included is a variant for use when no-response is expected or needed.)

The full source can be adapted for use within a standalone executable, or as an API. (.dll) Either way, the resulting functions accept and process any command using standard Windows CMD syntax. The function cmd_rsp(...) returns the Windows response via stdout into a self-sizing buffer.

The exported prototypes are:

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

A simple use case when capturing a response:

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

A simple use case when response is not needed:

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

    return 0;
}

A detailed description of purpose and usage is described in the link provided above. To illustrate, here are a few sample command inputs, each in this case change the working directory, then execute a command from that directory:

A command to change to sqlite directory, then execute a query:

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\

A command to change to teraterm directory, then execute a script:

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

A command to change directory then execute a command to send multiple digital acquisition channel settings.

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

A recursive directory search from a specified location:

cd C:\\dir1\\dir2 && dir /s /b  
ryyker
  • 22,849
  • 3
  • 43
  • 87
0

I got it working. As was suggested SendInput finally did the trick. I used a combination of WinAPI calls to GetForegroundWindow() / SetForegroundWindow() and the Windows Forms System.Windows.Forms.SendKeys.SendWait() Method to achieve what I wanted:

Upon calling my cd-wrapper program (sd.exe) and providing my custom target directory (~/ home) it generates the corresponding command along with the "Enter-Pressed-Event" to be sent to it's parent cmd process.

enter image description here

Here's the complete C# code:

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

Note that I kind of started to write a complete Framework for this and similar problems alongside this project that contains all my different approaches to this question and the low level WinAPI calls as well as the Extension methods to get the parent process :D

As it would be a bit overkill to paste all of it's code in this answer, here's the GitHub. If I can find the time I'll go ahead and optimize the code, but for now this'll do. Hope this helps anyone encountering a similar problem :)

Edit:

An even "cleaner" way is to use dll injection to directly make cmd switch it's working directory. While it is a lot harder to get working it has the advantage of not littering the cmd command history as compared to the approach described above. In addition to that cmd seems to be aware of any changes to it's current working directory, so it automatically updates the prompt text. Once I have a fully working example, that allows to dynamically specify the target directory I will post it here :)

Frederik Hoeft
  • 1,177
  • 1
  • 13
  • 37