/* * Start a program using ShellExecuteEx, optionally wait for it to finish * Compatible with Microsoft's "c:\windows\command\start.exe" * * Copyright 2003 Dan Kegel * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <windows.h> #include <winuser.h> #include <stdio.h> #include <stdlib.h> #include <shlobj.h> #include "resources.h" /** Output given message to stdout without formatting. */ static void output(const char *message) { DWORD count; WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), message, strlen(message), &count, NULL); } /** Output given message, followed by ": ", followed by description of given GetLastError() value to stdout, followed by a trailing newline, then terminate. */ static void fatal_error(const char *msg, DWORD error_code) { LPVOID lpMsgBuf; int status; output(msg); output(": "); status = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code, 0, (LPTSTR) & lpMsgBuf, 0, NULL); if (!status) { output("FormatMessage failed\n"); } else { output(lpMsgBuf); LocalFree((HLOCAL) lpMsgBuf); output("\n"); } ExitProcess(1); } /** Output given message from string table, followed by ": ", followed by description of given GetLastError() value to stdout, followed by a trailing newline, then terminate. */ static void fatal_string_error(int which, DWORD error_code) { char msg[2048]; if (!LoadString(GetModuleHandle(NULL), which, msg, sizeof(msg))) fatal_error("LoadString failed", GetLastError()); fatal_error(msg, error_code); } static void fatal_string(int which) { char msg[2048]; if (!LoadString(GetModuleHandle(NULL), which, msg, sizeof(msg))) fatal_error("LoadString failed", GetLastError()); output(msg); ExitProcess(1); } static void usage() { fatal_string(STRING_USAGE); } static void license() { fatal_string(STRING_LICENSE); } int main(int argc, char *argv[]) { char arguments[MAX_PATH]; char *p; SHELLEXECUTEINFO sei; int argi; memset(&sei, 0, sizeof(sei)); sei.cbSize = sizeof(sei); sei.lpVerb = "open"; sei.nShow = SW_SHOWNORMAL; /* Dunno what these mean, but it looks like winMe's start uses them */ sei.fMask = SEE_MASK_FLAG_DDEWAIT|SEE_MASK_FLAG_NO_UI; /* Canonical Microsoft commandline flag processing: * flags start with /, are case insensitive, * and may be run together in same word. */ for (argi=1; argi<argc; argi++) { int ci; if (argv[argi][0] != '/') break; /* Handle all options in this word */ for (ci=0; argv[argi][ci]; ) { /* Skip slash */ ci++; switch(argv[argi][ci]) { case 'l': case 'L': license(); break; /* notreached */ case 'm': case 'M': if (argv[argi][ci+1] == 'a' || argv[argi][ci+1] == 'A') sei.nShow = SW_SHOWMAXIMIZED; else sei.nShow = SW_SHOWMINIMIZED; break; case 'r': case 'R': /* sei.nShow = SW_SHOWNORMAL; */ break; case 'w': case 'W': sei.fMask |= SEE_MASK_NOCLOSEPROCESS; break; default: printf("Option '%s' not recognized\n", argv[argi]+ci-1); usage(); } /* Skip to next slash */ while (argv[argi][ci] && (argv[argi][ci] != '/')) ci++; } } if (argi == argc) usage(); sei.lpFile = argv[argi++]; /* FIXME - prone to overflow */ arguments[0] = 0; for (p = arguments; argi < argc; argi++) p += sprintf(p, " %s", argv[argi]); sei.lpParameters = arguments; if (!ShellExecuteEx(&sei)) fatal_string_error(STRING_EXECFAIL, GetLastError()); if (sei.fMask & SEE_MASK_NOCLOSEPROCESS) { DWORD exitcode; DWORD waitcode; waitcode = WaitForSingleObject(sei.hProcess, INFINITE); if (waitcode) fatal_error("WaitForSingleObject", GetLastError()); if (!GetExitCodeProcess(sei.hProcess, &exitcode)) fatal_error("GetExitCodeProcess", GetLastError()); /* fixme: haven't tested whether exit code works properly */ ExitProcess(exitcode); } ExitProcess(0); }