Click here to Skip to main content
15,923,789 members
Articles / Programming Languages / C++
Article

ExecApp, ExecRegisteredApp, and LookupRegisteredApp - non-MFC functions to execute an application

Rate me:
Please Sign up or sign in to vote.
4.83/5 (25 votes)
8 Jun 2008CPOL4 min read 53.2K   888   47   13
ExecApp is a replacement for WinExec(). ExecRegisteredApp executes the app that is registered for the specified file extension. LookupRegisteredApp retrieves the application file path that is registered for the specified file extension.

Introduction

I created these functions to handle common cases where you want to run an application and know application file name (the WinExec() case), or you don't know application file name, but do know type of file you want to open (the registered file extension case).

The functions described below have been tested on Windows 98, 2000, XP, and Vista.

ExecApp

Since we are being told that WinExec is now deprecated, it is necessary to use CreateProcess instead. There are a few things to watch out for with CreateProcess. The first is that CreateProcess creates two handles that you must close or you will have leaks. These are the process and thread handles, which are typically useful only if you are performing timing studies, monitoring critical threads, or something similar. The second thing to be aware of is how CreateProcess deals with arguments passed to the target application - the CreateProcess lpCommandLine parameter. It turns out that the simplest, most reliable way to use CreateProcess is to use NULL for the first parameter, and pass the application file path and arguments (each enclosed in quotes, separated by a space) in the second parameter.

Here is function header of ExecApp():

///////////////////////////////////////////////////////////////////////////////
//
// ExecApp()
//
// Purpose:     Runs the specified application (replacement for WinExec)
//
// Parameters:  lpszCommandLine - [in] command line (including exe file path) 
//                                that is passed to CreateProcess()
//              wShowCmd        - [in] Specifies how app window is to be shown.
//                                See ShowWindow() in MSDN for possible values.
//
// Returns:     BOOL            - TRUE = CreateProcess() succeeded
//
BOOL ExecApp(LPCTSTR lpszCommandLine, WORD wShowCmd /*= SW_SHOWNORMAL*/)

LookupRegisteredApp

LookupRegisteredApp() retrieves the application file path that is registered for the specified file extension. LookupRegisteredApp() does its work by calling the shell function AssocQueryString. This function is exported via the import lib shlwapi.lib, which is automatically linked to in ExecApp.cpp. (Note: requires Internet Explorer 5 or later).

Here is function header of LookupRegisteredApp():

///////////////////////////////////////////////////////////////////////////////
//
// LookupRegisteredApp()
//
// Purpose:     Retrieves the application registered for the specified file 
//              extension
//
// Parameters:  lpszExt         - [in] file extension (e.g., ".txt") used to 
//                                look up application file path. Preceding '.' 
//                                is necessary.
//              lpszApplication - [out] application path buffer
//              nSize           - [in/out] size of path buffer in TCHARs
//              
// Returns:     BOOL            - TRUE = found registered app
//
// Notes:       AssocQueryString() is broken in Vista. If no application is 
//              associated with the file extension, in Vista the function returns 
//              the "Unknown" application, rather than an error code (as in XP).
//              Adding ASSOCF_IGNOREUNKNOWN to the flags parameter will make the
//              function behave as in XP. ASSOCF_IGNOREUNKNOWN is defined in the 
//              latest Platform SDK.
//
BOOL LookupRegisteredApp(LPCTSTR lpszExt,
                         LPTSTR lpszApplication, 
                         DWORD *nSize)

Aside from the small glitch in using AssocQueryString on Vista (see Notes in preceding header), there is another, more serious problem with AssocQueryString: the ANSI version of this function (AssocQueryStringA) doesn't work. I don't know what the exact problem is, but the workaround is to convert the parameters to Unicode, call AssocQueryStringW, and then convert the result to ANSI. This is why ExecApp.cpp contains the private (static) function _AssocQueryString.

ExecRegisteredApp

Here is a common scenario: you want to allow user to edit/view a file (such as a log file). But the ".log" file extension might not be registered to an editor on the user's system. What to do? Of course you could just throw the file at ShellExecute, but you know what will happen: the user will be presented with a dialog, and then have to go through some clunky browse sequence, and then you will get a support call. Since you know the log file is just plain ASCII, it's better to ask the system to execute the application that is associated with .txt files, passing it the name of the log file. That is what ExecRegisteredApp() does.

Here is function header of ExecRegisteredApp():

///////////////////////////////////////////////////////////////////////////////
//
// ExecRegisteredApp()
//
// Purpose:     Runs the application registered for the specified file extension
//
// Parameters:  lpszArgs - [in] command line arguments that are passed to app
//                         via CreateProcess();  if not already in quotes ("),
//                         they will be enclosed in quotes before CreateProcess()
//                         is called.  May be NULL.
//              lpszExt  - [in] file extension (e.g., ".txt") used to look up
//                         application file path.  Preceding '.' is necessary.
//              wShowCmd - [in] Specifies how the app window is to be shown.
//                         See ShowWindow() in MSDN for possible values.
//
// Returns:     BOOL     - TRUE = found registered app; CreateProcess() succeeded
//
BOOL ExecRegisteredApp(LPCTSTR lpszArgs,    // may be NULL
                       LPCTSTR lpszExt, 
                       WORD wShowCmd /*= SW_SHOWNORMAL*/)

Is ExecRegisteredApp() a replacement for ShellExecute? No, absolutely not. If you're 100% sure that a file type (for example, .html) is registered on the target system, then you should certainly use ShellExecute. But there are times when you can't be sure, and it's not possible to register a custom file type. This is where ExecRegisteredApp() is useful.

ExecApp Demo

Here is what the ExecApp demo looks like:

screenshot

How to use

Step 1 - Add Files

To integrate ExecApp into your app, you first need to add following files to your project:

  • ExecApp.cpp
  • ExecApp.h

The .cpp file should be set to Not using precompiled header in Visual Studio. Otherwise, you will get error

fatal error C1010: unexpected end of file while looking for precompiled header directive

Step 2 - Add Header File to Your Source Module

In the module where you want to use ExecApp, include header file ExecApp.h .

Step 3 - Add Code

See ExecAppTestDlg.cpp for examples of how to use.

The file ExecApp.cpp contains definitions necessary to use the AssocQueryString API. If you have the latest Platform SDK, you can use that instead of the embedded definitions by un-commenting the line #include <shlwapi.h> in ExecApp.cpp.

Revision History

Version 1.0 - 2008 June 8

  • Initial public release

Usage

This software is released into the public domain. You are free to use it in any way you like, except that you may not sell this source code. If you modify it or extend it, please to consider posting new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause.


License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Hans Dietrich Software
United States United States
I attended St. Michael's College of the University of Toronto, with the intention of becoming a priest. A friend in the University's Computer Science Department got me interested in programming, and I have been hooked ever since.

Recently, I have moved to Los Angeles where I am doing consulting and development work.

For consulting and custom software development, please see www.hdsoft.org.






Comments and Discussions

 
GeneralCan't get the full path use app name on windows vista(windows7) Pin
liquanhai30-Sep-10 21:24
liquanhai30-Sep-10 21:24 
Generalweird results in Win7 Pin
Sonar10-Sep-10 14:07
Sonar10-Sep-10 14:07 
GeneralDoesn't work on Win 7 64-bit Pin
Super Garrison12-Dec-09 8:38
Super Garrison12-Dec-09 8:38 
GeneralRe: Doesn't work on Win 7 64-bit Pin
Hans Dietrich12-Dec-09 8:46
mentorHans Dietrich12-Dec-09 8:46 
GeneralRe: Doesn't work on Win 7 64-bit Pin
Super Garrison12-Dec-09 10:56
Super Garrison12-Dec-09 10:56 
GeneralRe: Doesn't work on Win 7 64-bit [modified] Pin
Hans Dietrich12-Dec-09 11:34
mentorHans Dietrich12-Dec-09 11:34 
GeneralRe: Doesn't work on Win 7 64-bit Pin
Super Garrison12-Dec-09 17:45
Super Garrison12-Dec-09 17:45 
GeneralError while compiling Pin
danandu12-Jun-08 2:15
danandu12-Jun-08 2:15 
GeneralRe: Error while compiling Pin
Hans Dietrich12-Jun-08 2:32
mentorHans Dietrich12-Jun-08 2:32 
GeneralWinExec... Pin
AlexEvans10-Jun-08 17:11
AlexEvans10-Jun-08 17:11 
GeneralRe: WinExec... Pin
Hans Dietrich10-Jun-08 22:55
mentorHans Dietrich10-Jun-08 22:55 
Here is what MSDN says about the two functions:

WinExec

WinExec uses this algorithm to find the app:

1. The directory from which the application loaded.
2. The current directory.
3. The Windows system directory. The GetSystemDirectory function retrieves the path of this directory.
4. The Windows directory. The GetWindowsDirectory function retrieves the path of this directory.
5. The directories listed in the PATH environment variable.

The executable name is treated as the first white space-delimited string in lpCmdLine. If the executable or path name has a space in it, there is a risk that a different executable could be run because of the way the function parses spaces. The following example is dangerous because the function will attempt to run "Program.exe", if it exists, instead of "MyApp.exe".

WinExec("C:\\Program Files\\MyApp", ...)

CreateProcess

CreateProcess on the other hand, uses some heuristics to try to find the app. For example, consider the string "c:\program files\sub dir\program name". This string can be interpreted in a number of ways. The system tries to interpret the possibilities in the following order:

c:\program.exe files\sub dir\program name
c:\program files\sub.exe dir\program name
c:\program files\sub dir\program.exe name
c:\program files\sub dir\program name.exe

The CreateProcess search algorithm is nearly identical to that of WinExec:
1. The directory from which the application loaded.
2. The current directory for the parent process.
3. The 32-bit Windows system directory. Use the GetSystemDirectory function to get the path of this directory.
4. The 16-bit Windows system directory. There is no function that obtains the path of this directory, but it is searched. The name of this directory is System.
5. The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
6. The directories that are listed in the PATH environment variable. Note that this function does not search the per-application path specified by the App Paths registry key. To include this per-application path in the search sequence, use the ShellExecute function.

Summary

If WinExec can find the app, then ExecApp should be able to find it as well. When I was using WinExec previously, I always enclosed the app path and the arguments in quotes (separately). For example, I would write

WinExec("\"C:\\Program Files\\MyApp\" \"my args\"", ...)

If you do this, then you will avoid the vulnerability that MSDN refers to.


GeneralUseful Pin
vobject8-Jun-08 20:56
vobject8-Jun-08 20:56 
GeneralRe: Useful Pin
Hans Dietrich8-Jun-08 21:22
mentorHans Dietrich8-Jun-08 21:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.