Introduction
You open a command window automatically from the startup folder. You define a slew of shortcut commands with doskey. When pressed for time, you abandon Windows Explorer and turn to the dir, copy, rename, and move commands. You write elaborate Visual Studio macros to emulate an editor that disappeared 20 years ago from a long-forgotten operating system. In short you are a dinosaur. Congratulations! Windows may be rife with wonderful ways to help you whiz through the workday, but friend, whereis is for you. The command-line interface may be archaic, but in the wonderful world of Windows, we can still have archaic and eat it too.
Details
Whereis partially emulates the Unix whereis command, a useful utility that, to quote the standard Unix online manual pages, "...attempts to locate [a] desired program in a list of standard places". If invoked with the -w option, whereis more closely emulates the Unix which command, which displays the first executable file in the path -- that is, the executable that actually gets invoked when you specify a simple command at the command-line prompt. In short, whereis can tell you what program, batch file, or script Windows actually invokes when you enter a command at the command-line prompt. It can also tell you which copy of a dll is getting bound into a program at runtime if there are multiple copies of that dll present on the execution path.
When given a series of one or more simple file names without extensions -- notepad, telnet, ftp, cl, link, lib, mybatfile, myvbscript, etc. -- as command-line arguments, whereis prints out the fully qualified name of every executable file that contains that file name and that resides somewhere along the Windows executable search path. Whereis emulates the search algorithm that Windows uses to resolve command-line commands when there is no additional context information such as a directory name or a file-type extension. Windows uses this same algorithm to resolve the lpApplicationName
parameter to the CreateProcess() Platform SDK function.
You can also specify fileName.ext values as command-line arguments, in which case whereis just searches for instances of the specified file along the execution path.
Program Structure
Whereis is a simple program: structurally, it is a C utility masquerading as a C# assembly. It comprises one class that contains only static methods and fields. The only time whereis dips out of the safety of the CLR is when it calls the GetWindowsDirectory()
Platform SDK function. The code that surrounds the call to GetWindowsDirectory()
was borrowed from, and explained clearly in, another CodeProject article.
An ArrayList is defined and populated at the outset with a series of unique directory names using the code shown below. The order in which the directories are added to ArrayList
is important and is the same order in which Windows searches directories while attempting to locate a program or batch file.
Process oLocal =
System.Diagnostics.Process.GetCurrentProcess();
ProcessModule oMain = oLocal.MainModule;
string sOurDir = Path.GetDirectoryName(oMain.FileName);
if (sOurDir.Length > 1 &&
'\\' == sOurDir[sOurDir.Length-1])
{ sOurDir.Remove(sOurDir.Length-1,1); }
sSearchPath.Add(sOurDir);
string sCurDir = Environment.CurrentDirectory;
if (sCurDir.Length > 1 && '\\' == sCurDir[sCurDir.Length-1])
{ sCurDir.Remove(sCurDir.Length-1,1); }
addElementAsNeeded(sCurDir, sSearchPath);
string sSysDir = Environment.SystemDirectory;
if (sSysDir.Length > 1 && '\\' == sSysDir[sSysDir.Length-1])
{ sSysDir.Remove(sSysDir.Length-1,1); }
addElementAsNeeded(sSysDir, sSearchPath);
byte[] buf = new byte[512];
unsafe
{
fixed(byte* pBuf=buf)GetWindowsDirectory(pBuf, 512);
}
ASCIIEncoding ae = new ASCIIEncoding();
string sWinDir = ae.GetString(buf);
if (sWinDir.Length > 1 && '\\' == sWinDir[sWinDir.Length-1])
{ sWinDir.Remove(sWinDir.Length-1,1); }
addElementAsNeeded(sWinDir, sSearchPath);
string sPathVal = Environment.GetEnvironmentVariable("Path");
if (sPathVal != string.Empty)
{
string [] sPathElements = sPathVal.Split(semi);
int nPathEleCount = sPathElements.GetLength(0);
for (; i < nPathEleCount; i++)
{
string sThisEle = sPathElements[i];
if (sThisEle.Length > 1 &&
'\\' == sThisEle[sThisEle.Length-1])
{ sThisEle.Remove(sThisEle.Length-1,1); }
addElementAsNeeded(sThisEle, sSearchPath);
}
}
nNumDirectories = sSearchPath.Count;
A second ArrayList
is defined and populated with all of the executable-file extensions as specified with the PATHEXT environment variable. If PATHEXT is not defined, the following hardcoded extension sequence is used:
.COM; .EXE; .BAT; .CMD; .VBS; .VBE; .JS; .JSE; .WSF; .WSH
If a given command-line argument does not have a file-type extension, then for every combination of that argument plus each file-type extension, whereis iterates through the directory ArrayList
and constructs a fully qualified file name. If the fully qualified file name corresponds to an existing file, that file name is written to the console.
If a given command-line argument contains both a file name and a file-type extension, then whereis doesn't attempt to append any extensions to that file.
If whereis is invoked with the -w option (w stands for which), the search stops after the first match is found.
Potential Uses
As a standalone utility, whereis can be used to isolate problems with a program that doesn't behave as expected, especially if there are multiple versions of that program -- or multiple versions of a DLL linked into that program -- floating around.