A whereis utility

Home
Back To Tips Page

This is a little utility that emulates the Unix whereis command.  Given the name of an executable, it will display the full path to the executable image.  However, in the case of Windows, there are several kinds of executable images, such as DLLs, ActiceX controls, even device drivers.  This little utility can find all of them.

Using whereis

I:\mvp_tips\whereis\Debug>whereis sc.exe
C:\WINDOWS\System32\sc.exe

If no parameter is supplied, the usual "usage" box is displayed

I:\mvp_tips\whereis\Debug>whereis
Usage:
whereis filename [options]
-c copy path to clipboard if successful
-e allow critical error box display
Version 1.0.0.4
Copyright (c) 2006 Joseph M. Newcomer, All Rights Reserved

Note the two options that can be supplied. 

-c
Copies the path to the clipboard, so it can be conveniently pasted to a target location
-e
Enables "critical errors" which will pop up a MessageBox if one of these errors is detected.  The most common cause is a file which is not a valid executable file.

If a name without an extension is provided, an attempt is made to look up various extensions

I:\mvp_tips\whereis\Debug>whereis sc
C:\WINDOWS\System32\sc.exe

The order of search, an explicit extension is not provided, is

.exe Executable file (application)
.dll Dynamic Link Library
.ocx ActiveX control
.sys Device Driver
.com 16-bit MS-DOS executable

Unlike the normal whereis utility that simulates the lookup algorithm, this uses whatever lookup algorithm is current on the current platform.  Different releases of Windows, and different service packs, have different search paths implemented.  Trying to keep updating the program by looking at the service pack level and hoping the simulation is correct seems to be a losing methodology.  So I decided to use the built-in Windows mechanisms for doing this. 

This program is an outgrowth of my work on another project, the LoadLlibrary Explorer, which is still under construction.

Using LoadLibraryEx to load images

The methodology here is to use LoadLibraryEx to load the executable image.  This will also load executable file images.  To avoid second-order effects caused by not having required DLLs in the search path, the DONT_RESOLVE_DLL_REFERENCES flag is specified.  This will load the executable but will not attempt to resolve any import records on the executable image.

 HMODULE h = ::LoadLibraryEx(fullname, NULL, DONT_RESOLVE_DLL_REFERENCES);

Having loaded the module, which is loaded using whatever the load resolution algorithm is on the current platform, the program then executes a GetModuleFileName on the handle returned.  The result is then printed.

The App Paths

When ShellExecute or ShellExecuteEx are called, two Registry keys come into play.  If a program is launched by an application calling ShellExecute(Ex), and the module name is not qualified by a full path, the registry key based on the filename is used.  So if there is an attempt to launch filename.exe  and the key is specified as

HKEY_LOCAL_MACHINE\
   Software\
      Microsoft\
         Windows\
            CurrentVersion\
                App Paths\
                    filename.exe

comes into play.  The (Default) value for this key gives the path to the executable image.  If this is present, it is used as the actual launch path.

If the Path variable is present, it represents a PATH string value which is appended to the end of the current PATH value, and this is used as the PATH for the execution of the process.  This can change the search path for DLLs, and this path is not taken into account by the whereis program.  However, if whereis finds a key that matches the executable name (only if the argument has either no extension, or has a .exe extension), it will print out the (Default) value of the key, and the Path value of the key.  For example

I:\mvp_tips\whereis\Debug>whereis mspub
The specified module could not be found.

HKLM\Software\Microsoft\Windows\CurrentVersion\App Paths\mspub.exe
         SaveURL => "1"
            Path => "C:\Program Files\Microsoft Office\OFFICE11\"
       (Default) => "C:\PROGRA~1\MICROS~3\OFFICE11\MSPUB.EXE"
          useURL => 0x00000001 (1)

I:\mvp_tips\whereis\Debug>

Note that there are many possible key values under the indicated key.  ShellExecute(Ex) is primarily concerned with the (Default) and Path values; note that whereis does not account for these when determining the location of an executable or where a DLL is loaded from.

The command shell (cmd.exe) does not use ShellExecute to launch a program. 

Building the project

download.gif (1234 bytes)This project builds Release and Debug versions for both 8-bit (ANSI) apps and Unicode apps, and works in both VS6 and VS.NET.  The executables for VS.NET go into directories with suffix "7", e.g., Debug7 or Unicode Release7.

 

Supplement: Why is this better than the traditional whereis command implementation?

I got a post where someone posted the usual let's-simulate-what-we-think-the-path-is code and said "Why isn't this good enough?"  The person posted the complete source code. Note the difference in these two examples.

My whereis utility

I:\mvp_tips\whereis\Unicode Debug7>whereis kernel32.dll
C:\WINDOWS\system32\kernel32.dll

I:\mvp_tips\whereis\Unicode Debug7>copy C:\WINDOWS\system32\kernel32.dll .
1 file(s) copied.

I:\mvp_tips\whereis\Unicode Debug7>whereis kernel32.dll
C:\WINDOWS\system32\kernel32.dll

I:\mvp_tips\whereis\Unicode Debug7>dir *.dll
Volume in drive I is RAID5
Volume Serial Number is 9C66-A773

Directory of I:\mvp_tips\whereis\Unicode Debug7

08/29/2002 03:41 AM 930,304 kernel32.dll
1 File(s) 930,304 bytes
0 Dir(s) 39,066,800,128 bytes free

I:\mvp_tips\whereis\Unicode Debug7>del kernel32.dll

I:\mvp_tips\whereis\Unicode Debug7>

The classic "let's pretend" code

I copied the code posted, called it a project badwhereis, built it, and ran it:.  Note that it does not properly account for the "known DLLs" feature.  It erroneously claims that the kernel32.dll file would be loaded from the local directory, but LoadLibrary(Ex) in fact does not load it from that directory!  Read about the various search paths LoadLibrary uses, under what settings, with what releases of Windows, under which service packs, and this shows that relying upon simulation of the behavior of LoadLibrary(Ex) has to be precise, knowing which platform, which service pack, which Registry settings apply, etc.  I rest my case.  Simulation is not sufficient; you have to rely on what the kernel call does in your environment.

C:\tests\badwhereis\Debug>badwhereis kernel32.dll
C:\WINDOWS\system32\kernel32.dll


C:\tests\badwhereis\Debug>copy C:\WINDOWS\system32\kernel32.dll .
1 file(s) copied.

C:\tests\badwhereis\Debug>badwhereis kernel32.dll
C:\tests\badwhereis\Debug\kernel32.dll


C:\tests\badwhereis\Debug>del kernel32.dll

C:\tests\badwhereis\Debug>

See what I mean about bad simulations?

[Dividing Line Image]

The views expressed in these essays are those of the author, and in no way represent, nor are they endorsed by, Microsoft.

Send mail to newcomer@flounder.com with questions or comments about this web site.
Copyright 2006, The Joseph M. Newcomer Co. All Rights Reserved.
Last modified: May 14, 2011