Win32 Self-Deletion Function

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

SelfDelete() is a simple Win API function for creating a self-deleting executable. It works by invoking the command shell as a serialized process to delete the program file.

The Command Shell

The command shell program is defined by the environment variable “COMSPEC”. This varies with the Windows OS: Win9x/ME use COMMAND.COM, while WinNT/2K/XP use CMD.EXE. The following string is passed to the command shell:

/c del Filename > nul

which translates to: “Run a single command to delete a file and redirect the output to nowhere.” Filename is the full path and name of the file we want to delete. This name needs to be converted into its short 8.3 name to change any extended characters into OEM equivalents.

The command shell is started as an independent process by calling the ShellExecuteEx() function. Its process handle is defined by the SHELLEXECUTEINFO struct member “hProcess”. NOTE: The “/c” switch is necessary for the command shell process to exit. Do not omit it from the parameters string.

Serializing Processes

Self-deletion presents us with a special problem: The main program must exit and close its opened file handle before the command shell deletes the file. To do this, we must serialize two independent, parallel processes—the current program process and the command shell process. This is done by manipulating CPU resource priorities to temporarily suppress the command shell process. As written, the code will allocate all CPU resources to the main program until it exits. This effectively blocks any command shell execution until the program has terminated.

CPU Allocation

Allocating the REALTIME_PRIORITY_CLASS and THREAD_PRIORITY_TIME_CRITICAL settings can interfere with driver functions and can cause Windows to “hang” if kept in a prolonged state. To avoid these problems, a self-deleting app must exit cleanly upon a boolean return of TRUE. Developers need to make sure all ancillary processes and threads have finished and all handles are closed before calling the function. SelfDelete() should only be called at a program’s main exit:

  INT APIENTRY WinMain(...)
  {
    ...

    // on program exit
    // close all handles etc.
    if(!SelfDelete())
    {
      // add error messaging
    }
    return 0;    // WinMain exit
  }

It may be desirable to use a lower CPU allocation, such as:

  SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS);
  SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_HIGHEST);

This allocation does not guarantee a complete cessation of the command shell process, but nevertheless works well.

Code Limitations

  • There is no error checking if the command shell cannot delete the file. Make sure the program file is not set with a “hidden,” “system,” or “read-only” attribute.
  • The explorer shell will always remove the program icon from view regardless of whether the command shell is able to actually delete the file. Pressing F5 will update the shell folder and correct any erroneous listings.
  • The command shell can only run a single command. Multiple commands such as to delete a file and remove its directory cannot be done without a script file.

Update

The code has been modified to work better on NT kernel computers with multiple processors. Windows allocates resources through a thread priority queue rather than by time-slicing CPU cycles. Two modifications have been made to the code:

  • The thread allocation to the program process has been increased prior to invoking the command shell process. This ensures the program process will always have priority in the resource queue. Threads created by the ShellExecuteEx function do not inherit the resource allocation of the calling process.
  • The NT kernel will transiently increase priority to whatever process is granted CPU resources. The SetProcessPriorityBoost function has been added to prevent this from happening to the command shell process.
////////////////////////////////////////////////////////

#include <windows.h>
#include <shlobj.h>

BOOL SelfDelete()
{
  SHELLEXECUTEINFO sei;

  TCHAR szModule [MAX_PATH],
        szComspec[MAX_PATH],
        szParams [MAX_PATH];

  // get file path names:
  if((GetModuleFileName(0,szModule,MAX_PATH)!=0) &&
     (GetShortPathName(szModule,szModule,MAX_PATH)!=0) &&
     (GetEnvironmentVariable("COMSPEC",szComspec,MAX_PATH)!=0))
  {
    // set command shell parameters
    lstrcpy(szParams,"/c del ");
    lstrcat(szParams, szModule);
    lstrcat(szParams, " > nul");

    // set struct members
    sei.cbSize       = sizeof(sei);
    sei.hwnd         = 0;
    sei.lpVerb       = "Open";
    sei.lpFile       = szComspec;
    sei.lpParameters = szParams;
    sei.lpDirectory  = 0;
    sei.nShow        = SW_HIDE;
    sei.fMask        = SEE_MASK_NOCLOSEPROCESS;

    // increase resource allocation to program
    SetPriorityClass(GetCurrentProcess(),
                     REALTIME_PRIORITY_CLASS);
    SetThreadPriority(GetCurrentThread(),
                      THREAD_PRIORITY_TIME_CRITICAL);

    // invoke command shell
    if(ShellExecuteEx(&sei))
    {
      // suppress command shell process until program exits
      SetPriorityClass(sei.hProcess,IDLE_PRIORITY_CLASS);
      SetProcessPriorityBoost(sei.hProcess,TRUE);

      // notify explorer shell of deletion
      SHChangeNotify(SHCNE_DELETE,SHCNF_PATH,szModule,0);
      return TRUE;
    }
    else // if error, normalize allocation
    {
      SetPriorityClass(GetCurrentProcess(),
                       NORMAL_PRIORITY_CLASS);
      SetThreadPriority(GetCurrentThread(),
                        THREAD_PRIORITY_NORMAL);
    }
  }
  return FALSE;
}

Downloads


Download source – 4 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read