Figure 1   ReplaceFileOnReboot

RFOR.H

 /******************************************************************************
Module name: RFoR.h
Written by:  Jeffrey Richter
Note:        This function works on both Windows 95 and Windows NT.
******************************************************************************/


BOOL WINAPI ReplaceFileOnReboot (LPCTSTR pszExisting, LPCTSTR pszNew);


///////////////////////////////// End of File /////////////////////////////////

RFOR.C

 /******************************************************************************
Module name: RFoR.c
Written by:  Jeffrey Richter
Note:        This function abstracts the differences between Windows 95 and 
             Windows NT for replacing/deleting a file when the system reboots.
******************************************************************************/


#define STRICT
#include <windows.h>


///////////////////////////////////////////////////////////////////////////////


#include "RFoR.h"


///////////////////////////////////////////////////////////////////////////////


BOOL WINAPI ReplaceFileOnReboot (LPCTSTR pszExisting, LPCTSTR pszNew) {

   // First, attempt to use the MoveFileEx function.
   BOOL fOk = MoveFileEx(pszExisting, pszNew, MOVEFILE_DELAY_UNTIL_REBOOT);
   if (fOk) return(fOk);

   // If MoveFileEx failed, we are running on Windows 95 and need to add
   // entries to the WININIT.INI file (an ANSI file).
   // Start a new scope for local variables.
   {
   char szRenameLine[1024];   

   int cchRenameLine = wsprintfA(szRenameLine, 
#ifdef UNICODE
      "%ls=%ls\r\n", 
#else
      "%hs=%hs\r\n", 
#endif
      (pszNew == NULL) ? __TEXT("NUL") : pszNew, pszExisting);
      char szRenameSec[] = "[Rename]\r\n";
      int cchRenameSec = sizeof(szRenameSec) - 1;
      HANDLE hfile, hfilemap;
      DWORD dwFileSize, dwRenameLinePos;
      TCHAR szPathnameWinInit[_MAX_PATH];

      // Construct the full pathname of the WININIT.INI file.
      GetWindowsDirectory(szPathnameWinInit, _MAX_PATH);
      lstrcat(szPathnameWinInit, __TEXT("\\WinInit.Ini"));

      // Open/Create the WININIT.INI file.
      hfile = CreateFile(szPathnameWinInit,      
         GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 
         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);

      if (hfile == INVALID_HANDLE_VALUE) 
         return(fOk);

      // Create a file mapping object that is the current size of 
      // the WININIT.INI file plus the length of the additional string
      // that we're about to insert into it plus the length of the section
      // header (which we might have to add).
      dwFileSize = GetFileSize(hfile, NULL);
      hfilemap = CreateFileMapping(hfile, NULL, PAGE_READWRITE, 0, 
         dwFileSize + cchRenameLine + cchRenameSec, NULL);

      if (hfilemap != NULL) {

         // Map the WININIT.INI file into memory.  Note: The contents 
         // of WININIT.INI are always ANSI; never Unicode.
         LPSTR pszWinInit = (LPSTR) MapViewOfFile(hfilemap, 
            FILE_MAP_WRITE, 0, 0, 0);

         if (pszWinInit != NULL) {

            // Search for the [Rename] section in the file.
            LPSTR pszRenameSecInFile = strstr(pszWinInit, szRenameSec);

            if (pszRenameSecInFile == NULL) {

               // There is no [Rename] section in the WININIT.INI file.
               // We must add the section too.
               dwFileSize += wsprintfA(&pszWinInit[dwFileSize], "%s",
szRenameSec); dwRenameLinePos = dwFileSize; } else { // We found the [Rename] section, shift all the lines down PSTR pszFirstRenameLine = strchr(pszRenameSecInFile, '\n'); // Shift the contents of the file down to make room for // the newly added line. The new line is always added // to the top of the list. pszFirstRenameLine++; // 1st char on the next line memmove(pszFirstRenameLine + cchRenameLine, pszFirstRenameLine, pszWinInit + dwFileSize - pszFirstRenameLine); dwRenameLinePos = pszFirstRenameLine - pszWinInit; } // Insert the new line memcpy(&pszWinInit[dwRenameLinePos], szRenameLine,cchRenameLine); UnmapViewOfFile(pszWinInit); // Calculate the true, new size of the file. dwFileSize += cchRenameLine; // Everything was successful. fOk = TRUE; } CloseHandle(hfilemap); } // Force the end of the file to be the calculated, new size. SetFilePointer(hfile, dwFileSize, NULL, FILE_BEGIN); SetEndOfFile(hfile); CloseHandle(hfile); } return(fOk); } ///////////////////////////////// End of File /////////////////////////////////

Figure 2   DELEXE

DELEXE.H

 /******************************************************************************
Module name: DelExe.h
Written by:  Jeffrey Richter
Note:        This function works on Windows 95 but does NOT work on Windows NT.
******************************************************************************/


VOID WINAPI DeleteExecutable (DWORD dwExitCode, BOOL fRemoveDir);


///////////////////////////////// End of File /////////////////////////////////

DELEXE.C

 /******************************************************************************
Module name: DelExe.c
Written by:  Jeffrey Richter
Note:        This function works on Windows 95 but does NOT work on Windows NT.
******************************************************************************/


#define STRICT
#include <Windows.h>
#include <tchar.h>


///////////////////////////////////////////////////////////////////////////////


#include "DelExe.h"


///////////////////////////////////////////////////////////////////////////////


// Prototypes for functions that we explicitly import from Kernel32.DLL
typedef BOOL (WINAPI *PROCFREELIBRARY)(HINSTANCE);
typedef BOOL (WINAPI *PROCDELETEFILE)(LPCTSTR);
typedef BOOL (WINAPI *PROCREMOVEDIRECTORY)(LPCTSTR);
typedef VOID (WINAPI *PROCEXITPROCESS)(DWORD);


// Data structure containing all the information we need to delete ourselves,
// remove our containing directory, and terminate ourselves.
typedef struct {

   HINSTANCE hinstExe;
   PROCFREELIBRARY pfnFreeLibrary;

   PROCDELETEFILE pfnDeleteFile;
   TCHAR szFile[_MAX_PATH];

   PROCREMOVEDIRECTORY pfnRemoveDirectory;
   TCHAR szDir[_MAX_PATH];

   PROCEXITPROCESS pfnExitProcess;
   DWORD dwExitCode;
} DELEXEINFO, *PDELEXEINFO;

typedef VOID (WINAPI *PROCDELEXE)(PDELEXEINFO);


///////////////////////////////////////////////////////////////////////////////


// Code to be injected into our own address space.
static void WINAPI DelExeInjCode (PDELEXEINFO pdei) {

      // Remove the EXE file from our address space
      pdei->pfnFreeLibrary(pdei->hinstExe);

      // Delete the EXE file now that it is no longer in use
      pdei->pfnDeleteFile(pdei->szFile);

      if (pdei->pfnRemoveDirectory != NULL) {
            // Remove the directory (which is now empty)
            pdei->pfnRemoveDirectory(pdei->szDir);
      }

      // Terminate our process
      pdei->pfnExitProcess(pdei->dwExitCode);
}


///////////////////////////////////////////////////////////////////////////////


// This function just marks the end of the previous function
static void WINAPI AfterDelExeInjCode (void) {
}


///////////////////////////////////////////////////////////////////////////////


void WINAPI DeleteExecutable (DWORD dwExitCode, BOOL fRemoveDir) {

      HINSTANCE hinstKrnl = GetModuleHandle(__TEXT("KERNEL32"));
      HANDLE hheap = GetProcessHeap();

      // Calculate the number of bytes in the DelExeInjCode function.
      const int cbFuncSize = ((LPBYTE)(DWORD)
      AfterDelExeInjCode - (LPBYTE)(DWORD) DelExeInjCode);

      DELEXEINFO dei;

      // From our process's default heap, allocate memory where we can 
      // inject our own function.
      PROCDELEXE pfnDelExe = HeapAlloc(hheap, HEAP_ZERO_MEMORY, cbFuncSize);

      // Inject the DelExeInjCode function into the memory block
      memcpy(pfnDelExe, DelExeInjCode, cbFuncSize);

      // Initialize the DELEXEINFO structure.
      dei.hinstExe = GetModuleHandle(NULL);
      dei.pfnFreeLibrary = (PROCFREELIBRARY) 
      GetProcAddress(hinstKrnl, "FreeLibrary");

      // Assume that the subdirectory is NOT to be removed.
      dei.pfnRemoveDirectory = NULL;
#ifdef UNICODE
      dei.pfnDeleteFile = (PROCDELETEFILE) 
      GetProcAddress(hinstKrnl, "DeleteFileW");
#else
      dei.pfnDeleteFile = (PROCDELETEFILE) 
      GetProcAddress(hinstKrnl, "DeleteFileA");
#endif
      GetModuleFileName(dei.hinstExe, dei.szFile, _MAX_PATH);

   if (fRemoveDir) {

            // The subdirectory should be removed.
#ifdef UNICODE
            dei.pfnRemoveDirectory = (PROCREMOVEDIRECTORY) 
            GetProcAddress(hinstKrnl, "RemoveDirectoryW");
#else
            dei.pfnRemoveDirectory = (PROCREMOVEDIRECTORY) 
            GetProcAddress(hinstKrnl, "RemoveDirectoryA");
#endif
            lstrcpy(dei.szDir, dei.szFile);
            *_tcsrchr(dei.szDir, __TEXT('\\')) = 0;
   }

      dei.pfnExitProcess = (PROCEXITPROCESS) 
      GetProcAddress(hinstKrnl, "ExitProcess");
      dei.dwExitCode = dwExitCode;

      pfnDelExe(&dei);
      // We never get here because pfnDelExe never returns
}


///////////////////////////////// End of File /////////////////////////////////

Figure 3   DeleteExecutableBF

DELEXEBF.H

 /******************************************************************************
Module name: DelExeBF.h
Written by:  Jeffrey Richter
Note:        This function works on both Windows 95 and Windows NT.
******************************************************************************/


VOID WINAPI DeleteExecutableBF (void);


///////////////////////////////// End of File /////////////////////////////////

DELEXEBF.C

 /******************************************************************************
Module name: DelExeBF.c
Written by:  Jeffrey Richter
Note:        This function works on both Windows 95 and Windows NT.
******************************************************************************/


#define STRICT
#include <Windows.h>
#include <tchar.h>


///////////////////////////////////////////////////////////////////////////////


#include "DelExeBF.h"


///////////////////////////////////////////////////////////////////////////////


// The name of the temporary batch file
#define DELUNSETUPBAT     __TEXT("\\DelUS.bat")


///////////////////////////////////////////////////////////////////////////////


void WINAPI DeleteExecutableBF (void) {
   HANDLE hfile;
   STARTUPINFO si;
   PROCESS_INFORMATION pi;

   // Create a batch file that continuously attempts to delete our executable
   // file.  When the executable no longer exists, remove its containing
   // subdirectory, and then delete the batch file too.
   hfile = CreateFile(DELUNSETUPBAT, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,                             FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
   if (hfile != INVALID_HANDLE_VALUE) {

      TCHAR szBatFile[1000];
      TCHAR szUnsetupPathname[_MAX_PATH];
      TCHAR szUnsetupPath[_MAX_PATH];
      DWORD dwNumberOfBytesWritten;

      // Get the full pathname of our executable file.
      GetModuleFileName(NULL, szUnsetupPathname, _MAX_PATH);

      // Get the path of the executable file (without the filename)
      lstrcpy(szUnsetupPath, szUnsetupPathname);
      *_tcsrchr(szUnsetupPath, __TEXT('\\')) = 0;     // Chop off the name

      // Construct the lines for the batch file.
      wsprintf(szBatFile,
         __TEXT(":Repeat\r\n")
         __TEXT("del \"%s\"\r\n")
         __TEXT("if exist \"%s\" goto Repeat\r\n")
         __TEXT("rmdir \"%s\"\r\n")
         __TEXT("del \"%s\"\r\n"), 
         szUnsetupPathname, szUnsetupPathname, szUnsetupPath, DELUNSETUPBAT);

      // Write the batch file and close it.
      WriteFile(hfile, szBatFile, lstrlen(szBatFile) * sizeof(TCHAR),
         &dwNumberOfBytesWritten, NULL);
      CloseHandle(hfile);

      // Get ready to spawn the batch file we just created.
      ZeroMemory(&si, sizeof(si));
      si.cb = sizeof(si);

      // We want its console window to be invisible to the user.
      si.dwFlags = STARTF_USESHOWWINDOW;
      si.wShowWindow = SW_HIDE;

      // Spawn the batch file with low-priority and suspended.
      if (CreateProcess(NULL, DELUNSETUPBAT, NULL, NULL, FALSE,
         CREATE_SUSPENDED | IDLE_PRIORITY_CLASS, NULL, __TEXT("\\"), &si, &pi)) {

         // Lower the batch file's priority even more.
         SetThreadPriority(pi.hThread, THREAD_PRIORITY_IDLE);

         // Raise our priority so that we terminate as quickly as possible.
         SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
         SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);

         // Allow the batch file to run and clean-up our handles.
         CloseHandle(pi.hProcess);
         ResumeThread(pi.hThread);
         // We want to terminate right away now so that we can be deleted
         CloseHandle(pi.hThread);
      }
   }
}


///////////////////////////////// End of File /////////////////////////////////

Figure 4   TDELEXE.C

 /******************************************************************************
Module name: TDelExe.c
Written by:  Jeffrey Richter
Note:        This function tests the functions that delete our own executable.
******************************************************************************/


#define STRICT
#include <Windows.h>


///////////////////////////////////////////////////////////////////////////////


#include "RFoR.h"       // to delete this executable file on reboot.
#include "DelExe.h"     // to delete this executable file using a batch file.
#include "DelExeBF.h"   // to delete this executable file using code.


///////////////////////////////////////////////////////////////////////////////


int WINAPI WinMain (HINSTANCE hinstExe, HINSTANCE hinstExePrev, 
                    LPSTR lpszCmdLine, int nCmdShow) {

   int nId = MessageBox(NULL, 
      __TEXT("Choose Yes to delete this executable file on reboot.\n")
      __TEXT("Choose No to delete this executable file using a batch file.\n")
      __TEXT("Choose Cancel to delete this executable file using code.\n"),
      __TEXT("Delete Executable"), MB_YESNOCANCEL);

   switch (nId) {
      case IDYES:
         {
         TCHAR szPathname[_MAX_PATH];
         GetModuleFileName(NULL, szPathname, _MAX_PATH);
         ReplaceFileOnReboot(szPathname, NULL);
         }
         break;

      case IDNO:
         DeleteExecutableBF();
         // We want to terminate right away so that we can be deleted
         break;

      case IDCANCEL:
        DeleteExecutable(0, TRUE);
        // DeleteExecutable never returns back to us.
         break;
   }

       return(0);
}


///////////////////////////////// End of File /////////////////////////////////