/*
 * onceonly.c
 *
 * Only allow the specified programs to have a single instance running at a time.
 * To use this program globally, set the following registry key:
 *   HKEY_CLASSES_ROOT\exefile\shell\open\command (Default)
 * to be this:
 *   "path\to\this\program.exe" "%1" %*
 * You may want to expand the list of programs that are checked; to do so, simply
 *   add the executable name (not path) into the list before the NULL. For example,
 *   to only allow one instance each of Outlook, MS Word, and Internet Explorer, it
 *   should be this:
 *   char *programs[] = {"outlook.exe", "winword.exe", "iexplore.exe", NULL};
 * To compile, make sure MinGW GCC is installed (http://www.mingw.org/) and the bin
 *   directory from it is in your path. Then type:
 *   gcc onceonly.c -c -O2
 *   gcc onceonly.o -O2 -o onceonly
 * Copy "onceonly.exe" to each machine that you want protected, and make the
 *   registry change noted above.
 *
 * Jonathan Pearson
 * April 3, 2007
 *
 */

#include <windows.h>
#include <Tlhelp32.h>

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

char *programs[] = {"outlook.exe", NULL};

static void startProgram(int argc, char **argv);
static int addUp(char **array);

int main(int argc, char **argv) {
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 curProc;
    int i = 0;
    char drive[_MAX_DRIVE];
    char dir[_MAX_DIR];
    char fname[_MAX_FNAME];
    char ext[_MAX_EXT];
    char progname[_MAX_FNAME + _MAX_EXT];
    
    if (hSnapshot == INVALID_HANDLE_VALUE) {
        // Since there was an error, allow the program through just to prevent locking up the system
        startProgram(argc, argv);
        return 1;
    }
    
    if (argc == 0) {
        CloseHandle(hSnapshot);
        return 1;
    }
    
    _splitpath(argv[1], drive, dir, fname, ext);
    sprintf(progname, "%s%s", fname, ext);
    
    // Check if we should be filtering this one
    i = 0;
    while (programs[i]) {
        if (_stricmp(programs[i], progname) == 0) break;
        
        i++;
    }
    
    if (!programs[i]) { // Made it to the end of the list, not filtering this program
        startProgram(argc, argv);
        
        CloseHandle(hSnapshot);
        return 0;
    }
    
    curProc.dwSize = sizeof(curProc);
    
    if (Process32First(hSnapshot, &curProc)) {
        do {
            i = 0;
            while (programs[i]) {
                if (_stricmp(programs[i], curProc.szExeFile) == 0) {
                    CloseHandle(hSnapshot);
                    
                    MessageBox(NULL, // No owner window
                               "That program is already running.",
                               "Sorry",
                               MB_OK | MB_ICONHAND | MB_TOPMOST);
                    
                    return 0;
                }
                
                i++;
            }
        } while (Process32Next(hSnapshot, &curProc));
    }
    
    startProgram(argc, argv);
    
    CloseHandle(hSnapshot);
    return 0;
}

static void startProgram(int argc, char **argv) {
    char *cmdLine;
    int len = addUp(argv + 1);
    char **curArg = argv + 1;
    char *linePos;
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    
    cmdLine = (char *)malloc(len + 1 + argc); // add in some spaces too
    linePos = cmdLine;
    
    while (*curArg) {
        strcpy(linePos, *curArg);
        linePos += strlen(*curArg);
        
        strcpy(linePos, " ");
        linePos += strlen(" ");
        
        curArg++;
    }
    
    memset(&si, 0, sizeof(si));
    si.cb = sizeof(si);
    
    CreateProcess(NULL, // Pull app name from command line
                  cmdLine, // Command line
                  NULL, // Process security attributes
                  NULL, // Thread security attributes
                  FALSE, // Do not inherit handles
                  0, // No special creation flags
                  NULL, // The environment we have
                  NULL, // Same current directory as ours
                  &si, // Startup info
                  &pi); // Process info (out)
}

static int addUp(char **array) {
    int len = 0;
    
    while (*array) {
        len += strlen(*array);
        array++;
    }
    
    return len;
}
