Implementation of 'start', take 2

Dan Kegel dank at kegel.com
Fri Jan 17 21:12:32 CST 2003


I had a look at cygstart, but it doesn't even try to be
commandline-compatible with Microsoft's start.exe.  Oh, well.

I've verified that this implementation of start can do both
   start notepad
and
   start foo.txt
and that it obeys all the options obeyed by WindowsMe's start.exe.
It also implements the /L option to print out the LGPL.
(Do we have a convention for what this option, required by
the LGPL, should be?)

I'd rather compile it as a .exe rather than a winelib app -
is there a way to do that in the Wine tree?
Also, what do I have to do to cause the resulting .exe to end
up as %windir%/command/start.exe, so it's in the same place
as it is in real Microsoft Windows?

BTW, to make 'start foo.txt' work, you need the registry entries

[HKEY_CLASSES_ROOT\.txt]
@="txtfile"
"Content Type"="text/plain"

[HKEY_CLASSES_ROOT\txtfile\shell\open\command]
@="C:\\WINDOWS\\NOTEPAD.EXE %1"

I verified that removing either of these on WinMe caused
'start foo.txt' to fail.

I did not verify that, when you use the /w flag, the exit
status of the subprocess is returned in the same way
as it's done in Windows.

Thanks,
Dan

-- 
Dan Kegel
http://www.kegel.com
http://counter.li.org/cgi-bin/runscript/display-person.cgi?user=78045
-------------- next part --------------
Index: configure.ac
===================================================================
RCS file: /home/wine/wine/configure.ac,v
retrieving revision 1.116
diff -u -r1.116 configure.ac
--- configure.ac	4 Jan 2003 02:52:05 -0000	1.116
+++ configure.ac	18 Jan 2003 02:41:32 -0000
@@ -1497,6 +1497,7 @@
 programs/regtest/Makefile
 programs/rpcss/Makefile
 programs/rundll32/Makefile
+programs/start/Makefile
 programs/uninstaller/Makefile
 programs/view/Makefile
 programs/wcmd/Makefile
Index: programs/Makefile.in
===================================================================
RCS file: /home/wine/wine/programs/Makefile.in,v
retrieving revision 1.33
diff -u -r1.33 Makefile.in
--- programs/Makefile.in	4 Jan 2003 02:52:05 -0000	1.33
+++ programs/Makefile.in	18 Jan 2003 02:41:32 -0000
@@ -19,6 +19,7 @@
 	regtest \
 	rpcss \
 	rundll32 \
+	start \
 	uninstaller \
 	view \
 	wcmd \
@@ -43,6 +44,7 @@
 	regsvr32 \
 	rpcss \
 	rundll32 \
+	start \
 	uninstaller \
 	wcmd \
 	wineboot \
@@ -73,6 +75,7 @@
 
 # Symlinks to apps that we want to run from inside the source tree
 SYMLINKS = \
+	start.exe \
 	rpcss.exe \
 	wcmd.exe \
 	wineconsole.exe \
@@ -136,6 +139,9 @@
 
 winhelp.exe$(DLLEXT): winhelp/winhelp.exe$(DLLEXT)
 	$(RM) $@ && $(LN_S) winhelp/winhelp.exe$(DLLEXT) $@
+
+start.exe$(DLLEXT): start/start.exe$(DLLEXT)
+	$(RM) $@ && $(LN_S) start/start.exe$(DLLEXT) $@
 
 wcmd/wcmd.exe$(DLLEXT): wcmd
 wineconsole/wineconsole.exe$(DLLEXT): wineconsole
--- /dev/null	2002-08-30 16:31:37.000000000 -0700
+++ programs/start/Makefile.in	2003-01-16 08:20:35.000000000 -0800
@@ -0,0 +1,13 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = start.exe
+APPMODE   = cui
+IMPORTS   = shell32
+
+C_SRCS = start.c
+
+ at MAKE_PROG_RULES@
+
+### Dependencies:
--- /dev/null	2002-08-30 16:31:37.000000000 -0700
+++ programs/start/start.c	2003-01-17 17:34:36.000000000 -0800
@@ -0,0 +1,199 @@
+/*
+ * 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 "config.h"
+#include "wine/debug.h"
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <shlobj.h>
+
+#define VERSIONMSG "start.exe version 0.1 Copyright (C) 2003, Dan Kegel"
+
+/**
+ 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);
+}
+
+static void usage()
+{
+	output("\
+Start a program, or open a document in the program normally used for files with that suffix.\n\
+Usage:\n\
+start [options] program_filename [...]\n\
+start [options] document_filename\n\
+\n\
+Options:\n\
+/M[inimized] Start the program minimized.\n\
+/MAX[imized] Start the program maximized.\n\
+/R[estored]  Start the program normally (neither minimized nor maximized).\n\
+/W[ait]      Wait for started program to finish, then exit with its exit code.\n\
+/L           Show end-user license.\n\
+\n\
+" VERSIONMSG "\n\
+Start comes with ABSOLUTELY NO WARRANTY; for details run with /L option.\n\
+This is free software, and you are welcome to redistribute it\n\
+under certain conditions; run 'start /L' for details.\n\
+");
+	ExitProcess(1);
+}
+
+static void license()
+{
+	output("\
+" VERSIONMSG "\n\n\
+This program is free software; you can redistribute it and/or\n\
+modify it under the terms of the GNU Lesser Public License\n\
+as published by the Free Software Foundation; either version 2.1\n\
+of the License, or (at your option) any later version.\n\
+\n\
+This program is distributed in the hope that it will be useful,\n\
+but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
+GNU Lesser Public License for more details.\n\
+\n\
+You should have received a copy of the GNU Lesser Public License\n\
+along with this program; if not, write to the Free Software\n\
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n\
+\n\
+See the COPYING.LIB file for license information.\n\
+");
+	ExitProcess(1);
+}
+
+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_error("Application could not be started, or no application associated with the specified file.  ShellExecuteEx failed", 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);
+}


More information about the wine-devel mailing list