Introduction
I am a tool collector - going around searching for tools others have written, which I can salvage for my own petty needs... If the tool comes with source code, then that's the icing on the cake, but often that's not the case.
Lately, I found a few binary only tools which operated only in the directory it was invoked from. I really wanted them to operate by delving into subdirectories.
For example, I have a nifty tool which calculates MD5 Digests of file(s) in the directory you passed to it, but it had no option which would let it go on recursively.
And I have so many such others which do their own job well, but miss out on this feature.
While rewriting such tools are not that hard, I prefer being lazy, and thought, well, why not just write a quick tool which will execute these program(s) recursively from a starting directory that I specify? Surely, that will be much faster than rewriting each and every tool that I wanted to add this functionality to!
A quick session of 1 hour, and there it was - RecursiveExecuter.
Features list of RecursiveExecuter
- Option to redirect the output of a console program.
- Option to let the user select which (sub)directories the program will operate in.
- Allows you to set a timeout on the program to be executed recursively. (By default, it's INFINITE).
- Allows you to specify the directory wildcard to narrow down your choice of matches.
- You can specify program arguments with ability to put tokens which will be replaced by the operating directory (directory matched currently) by the program !
How to use RecursiveExecuter
Though the options on the dialog box should be self-explanatory, I will mention a few details which might help you to make better use of this little tool.
Note:
- In the notes below, the term child process will mean the program you have chosen to run.
- You will encounter a new token '<>', which is a special token selected by me. You may place this token where you want RecursiveExecuter to place the operating directory it's going to recurse into.
For example, if you want the arguments to your selected program to be "-l -d:X:\ThisDir"
when RecursiveExecuter delves into X:\ThisDir, and "-l -d:X:\ThisDir\SubDir"
when RecursiveExecuter delves into X:\ThisDir\SubDir, you will put the following into the respective field of RecursiveExecuter: "-l -d:<>"
.
What RecursiveExecuter will do during runtime is to simply replace "<>
" with the operating directory that it just found!
Why I chose this token? Well, because it's otherwise an illegal directory name in Windows - no directory can have any of those two characters!
- The term operating directory means the directory which RecursiveExecuter has currently found while recursing. This is opposed to the sense of the term current directory which means the directory variable being passed to the child process (as specified by the field "Current Dir Value").
However, if you keep the value of the field "Current Dir Value" to the default "<>", then both will have the same values.
For example, if RecursiveExecuter has just encountered the directory X:\RootDir\SubDir\AnotherDir, then the operating directory is X:\RootDir\SubDir\AnotherDir.
RecursiveExecuter interprets the token "<>" as what is to be meant by the operating directory.
Here goes the dialog fields in details:
- Program options:
- Redirect STDIO: This option will let you redirect/capture the output given by console programs into a text file specified by you. It's checked off by default, and when you check it, a file dialog will open up where you can select the output filename where the output will be redirected.
Note: When this option is checked, the child process window will also be hidden, that is, you will not get to see console window as seen otherwise.
- Confirm new entry: As this program recurses into directories, you may want to filter out a few ones which you would not want to let the program operate on.
When you check this box, every time RecursiveExecuter changes to a new directory, it will ask if you want to let the child process operate in that directory.
If you want to skip operating in that directory, just select "No" in the dialog box.
- Timeout (ms): By default, this is -1 which means an INFINITE wait state on the process being run (which means that RecursiveExecuter will patiently wait until the child process finishes). It's recommended that you leave it as it is.
However, you may have your reasons for tuning the time that RecursiveExecuter will wait for the child process to finish. If the child process is still alive after the amount of time specified in this field, RecursiveExecuter will simply kill it.
- Directory wildcard: This option will come to use if you want to fine tune the list of directories at and under which RecursiveExecuter will go into.
By default, it is empty, which means that RecursiveExecuter will go into the specified (sub)directory as mentioned in the 'Select the starting directory' field, as well as all its subdirectories.
Besides the above, it will be interpreted just as Windows Find does. For example, a value of '*' will make RecursiveExecuter operate only in the subdirectories of the directory mentioned, or a value of 'r*' will match all (sub)directories starting with 'r'.
- Current Dir value: The child process is spawned using a
CreateProcess()
call. One of the arguments to CreateProcess()
is a LPCTSTR lpCurrentDirectory
, which specifies the current drive and directory for the child process.
While this value may be NULL
, many programs work better if you pass a meaningful value (usually the current working directory) using this pointer.
In fact, most console programs carry out their desired operations in the directory passed though this argument.
You may treat this field like the "Working Directory" or "Start In" field that you fill in while creating shortcuts to programs.
If you are unsure, you should leave it to the default value of '<>'.
- Execution options:
- Select the program you want to execute : Choose the path to the program you wish to execute recursively. Type it in directly (it must be a fully qualified filename), or use the adjacent Browse button.
It's this program which will be spawned as a child process.
- Select the starting directory : Choose the starting directory from which you want RecursiveExecuter to start recursing from.
- Executable arguments : Enter the arguments (command line options) you want to pass to the child process.
Again, if you want to pass the operating directory to the command line, just place the "<>" token (without the quotes) in its place!
Points of Interest
Behavior of CreateProcess():
While this is well documented in 'Case 3' of "INFO: Understanding CreateProcess
and Command-line Arguments" at MSDN, I will nevertheless mention it here in brief.
Let us see the prototype of CreateProcess()
in a more detailed manner:
BOOL CreateProcess(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
....);
For spawning console programs, we may pass a NULL
to lpApplicationName
, but lpCommandLine
must at least have the (fully qualified) process filename. Additional command line arguments might follow as usual.
However, if we pass the fully qualified program name to lpApplicationName
and the rest of the command line to lpCommandLine
, then the console process will be spawned alright, but the arguments to main()
will be out of line.
To demonstrate what I mean to say, suppose we have a console program "prog.exe". It accepts many command line switches, one of which is "-h -m simple"
which makes it print out a simple help page.
If we spawn the process using CreateProcess()
like so:
....
char szArgs[MAX_ARGS];
lstrcpy(szArgs,"-h -m simple");
CreateProcess("X:\\prog.exe",szArgs,...);
Then the process will be spawned alright, but the command line parameters will have the following values:
argv[0] == "-h"
argv[1] == "-m"
argv[2] == "simple"
But of course, ANSI specifications require the values to look like:
argv[0] == "X:\\prog.exe"
argv[1] == "-h"
argv[2] == "-m"
argv[3] == "simple"
And we being good programmers have always coded keeping the ANSI specification in mind. So obviously, any console program which adheres to this ANSI specification will break.
Now, RecursiveExecuter takes this into consideration while launching programs by default, but incase you have programs which do not abide by the ANSI norms, but in fact rely on this behavior of CreateProcess
, you can manually delete the program name added automatically into the Executable arguments field of RecursiveExecuter, and all will be alright.
BUGs
Sometimes, while running a console application with Redirect STDIO enabled may apparently "hang" RecursiveExecuter. However, on killing the child process, RecursiveExecuter will resume the task.
Amazingly, the very same combination of field settings except with Redirect STDIO (now disabled), will produce no problems at all.
Fixing this will require some time, but I think it has to do something with the pipes. If anyone of you can spot the problem (and fix it?), please let me know.
Wrapping it up
This tool works fine for me, and I have not seen it work otherwise. If it worked, and you liked it, rate it and live happily, and remember I wrote it.
If you have a bug report, feature request or just anything else, post in this message board or give me a buzz (mail me).
If this program created a black hole in your computer or turned you invisible, remember that it was coded by someone who was also watching Whose Line Is It Anyway at the same time. You were warned. Go and blame Drew ;)
History
2004/04/20 - Initial release.