Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Whereis for Windows

0.00/5 (No votes)
11 Mar 2003 1  
A UNIX-like Whereis utility written for Windows in C#.

screen shot - whereis output

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.

      //

      //  Build an array of locations to be searched. The search

      //  order mostly emulates the Win32 search-for-an-executable 

      //  algorithm. The only directory that is not searched is the 

      //  Windows NT Win16 directory (SYSTEM).

      //

      //  Search directories in the following order:

      //  1  The directory where this executable is located

      //  2. The current directory.

      //  3. The value of GetSystemDirectory()

      //  4. The value of GetWindowsDirectory()

      //  5. The directories along the path

      //

      //  Get our fully qualified executable name and extract the

      //  directory name

      //

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

      //

      //  Add current directory and system directory as needed

      //

      string sCurDir = Environment.CurrentDirectory;
      if (sCurDir.Length > 1 && '\\' == sCurDir[sCurDir.Length-1])
      { sCurDir.Remove(sCurDir.Length-1,1); }

      //

      //  addElementAsNeeded() is a simple helper function that

      //  ensures that the specified element (in this case, 

      //  sCurDir) does not already exist in the ArrayList 

      //  before adding that element to the ArrayList

      //

      addElementAsNeeded(sCurDir, sSearchPath);

      string sSysDir = Environment.SystemDirectory;
      if (sSysDir.Length > 1 && '\\' == sSysDir[sSysDir.Length-1])
      { sSysDir.Remove(sSysDir.Length-1,1); }
      addElementAsNeeded(sSysDir, sSearchPath);
      //

      //  Add windows directory (thanks to Nishant S for the 

      //  the following code)

      //

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

      //  Finally, tack on the components of the path

      //

      string sPathVal = Environment.GetEnvironmentVariable("Path");
      if (sPathVal != string.Empty)
      { 
        //

        //  semi is defined outside of Main() as 

        //    static char[] semi = new char [] {';'};

        //

        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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here