diff --git a/dlls/kernel/ne_module.c b/dlls/kernel/ne_module.c index ca4e61f4bc0..b2dc0ccb202 100644 --- a/dlls/kernel/ne_module.c +++ b/dlls/kernel/ne_module.c @@ -1827,7 +1827,7 @@ HINSTANCE16 WINAPI WinExec16( LPCSTR lpCmdLine, UINT16 nCmdShow ) HeapFree( GetProcessHeap(), 0, cmdline ); if (name != lpCmdLine) HeapFree( GetProcessHeap(), 0, name ); - if (ret == 21) /* 32-bit module */ + if (ret == 21 || ret == 11) /* 32-bit module or unknown executable*/ { DWORD count; ReleaseThunkLock( &count ); diff --git a/dlls/kernel/process.c b/dlls/kernel/process.c index 147734788e5..a430ecc4aed 100644 --- a/dlls/kernel/process.c +++ b/dlls/kernel/process.c @@ -82,6 +82,7 @@ const WCHAR *DIR_System = NULL; static const WCHAR comW[] = {'.','c','o','m',0}; static const WCHAR batW[] = {'.','b','a','t',0}; +static const WCHAR pifW[] = {'.','p','i','f',0}; static const WCHAR winevdmW[] = {'w','i','n','e','v','d','m','.','e','x','e',0}; extern void SHELL_LoadRegistry(void); @@ -1952,7 +1953,7 @@ BOOL WINAPI CreateProcessW( LPCWSTR app_name, LPWSTR cmd_line, LPSECURITY_ATTRIB /* check for .com or .bat extension */ if ((p = strrchrW( name, '.' ))) { - if (!strcmpiW( p, comW )) + if (!strcmpiW( p, comW ) || !strcmpiW( p, pifW )) { TRACE( "starting %s as DOS binary\n", debugstr_w(name) ); retv = create_vdm_process( name, tidy_cmdline, envW, cur_dir, process_attr, thread_attr, diff --git a/programs/winevdm/Makefile.in b/programs/winevdm/Makefile.in index 941a756ee2f..cd19759810e 100644 --- a/programs/winevdm/Makefile.in +++ b/programs/winevdm/Makefile.in @@ -4,7 +4,7 @@ SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = winevdm.exe APPMODE = -mconsole -IMPORTS = winedos kernel32 +IMPORTS = winedos user32 kernel32 C_SRCS = \ winevdm.c diff --git a/programs/winevdm/winevdm.c b/programs/winevdm/winevdm.c index ede908c650e..baf3a67b7a3 100644 --- a/programs/winevdm/winevdm.c +++ b/programs/winevdm/winevdm.c @@ -19,11 +19,13 @@ */ #include +#include #include "windef.h" #include "winbase.h" #include "wine/winbase16.h" #include "winuser.h" +#include "wincon.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(winevdm); @@ -31,6 +33,223 @@ WINE_DEFAULT_DEBUG_CHANNEL(winevdm); extern void WINAPI wine_load_dos_exe( LPCSTR filename, LPCSTR cmdline ); +/*** PIF file structures ***/ +#include "pshpack1.h" + +/* header of a PIF file */ +typedef struct { + BYTE unk1[2]; /* 0x00 */ + CHAR windowtitle[ 30 ]; /* 0x02 seems to be padded with blanks*/ + WORD memmax; /* 0x20 */ + WORD memmin; /* 0x22 */ + CHAR program[63]; /* 0x24 seems to be zero terminated */ + BYTE hdrflags1; /* 0x63 various flags: + * 02 286: text mode selected + * 10 close window at exit + */ + BYTE startdrive; /* 0x64 */ + char startdir[64]; /* 0x65 */ + char optparams[64]; /* 0xa5 seems to be zero terminated */ + BYTE videomode; /* 0xe5 */ + BYTE unkn2; /* 0xe6 ?*/ + BYTE irqlow; /* 0xe7 */ + BYTE irqhigh; /* 0xe8 */ + BYTE rows; /* 0xe9 */ + BYTE cols; /* 0xea */ + BYTE winY; /* 0xeb */ + BYTE winX; /* 0xec */ + WORD unkn3; /* 0xed 7??? */ + CHAR unkn4[64]; /* 0xef */ + CHAR unkn5[64]; /* 0x12f */ + BYTE hdrflags2; /* 0x16f */ + BYTE hdrflags3; /* 0x170 */ + } pifhead_t; + +/* record header: present on every record */ +typedef struct { + CHAR recordname[16]; /* zero terminated */ + WORD posofnextrecord; /* file offset, 0xffff if last */ + WORD startofdata; /* file offset */ + WORD sizeofdata; /* data is expected to follow directly */ +} recordhead_t; + +/* 386 -enhanced mode- record */ +typedef struct { + WORD memmax; /* memory desired, overrides the pif header*/ + WORD memmin; /* memory required, overrides the pif header*/ + WORD prifg; /* foreground priority */ + WORD pribg; /* background priority */ + WORD emsmax; /* EMS memory limit */ + WORD emsmin; /* EMS memory required */ + WORD xmsmax; /* XMS memory limit */ + WORD xmsmin; /* XMS memory required */ + WORD optflags; /* option flags: + * 0008 full screen + * 0004 exclusive + * 0002 background + * 0001 close when active + */ + WORD memflags; /* various memory flags*/ + WORD videoflags; /* video flags: + * 0010 text + * 0020 med. res. graphics + * 0040 hi. res. graphics + */ + WORD hotkey[9]; /* Hot key info */ + CHAR optparams[64]; /* optional params, replaces those in the pif header */ +} pif386rec_t; + +#include "poppack.h" + +/*********************************************************************** + * read_pif_file + *pif386rec_tu + * Read a pif file and return the header and possibly the 286 (real mode) + * record or 386 (enhanced mode) record. Returns FALSE if the file is + * invalid otherwise TRUE. + */ +static BOOL read_pif_file( HANDLE hFile, char *progname, char *title, + char *optparams, char *startdir, int *closeonexit, int *textmode) +{ + DWORD nread; + LARGE_INTEGER filesize; + recordhead_t rhead; + BOOL found386rec = FALSE; + pif386rec_t pif386rec; + pifhead_t pifheader; + if( !GetFileSizeEx( hFile, &filesize) || + filesize.QuadPart < (sizeof(pifhead_t) + sizeof(recordhead_t))) { + WINE_ERR("Invalid pif file: size error %d must be >= %d\n", + (int)filesize.QuadPart, + (sizeof(pifhead_t) + sizeof(recordhead_t))); + return FALSE; + } + SetFilePointer( hFile, 0, NULL, FILE_BEGIN); + if( !ReadFile( hFile, &pifheader, sizeof(pifhead_t), &nread, NULL)) + return FALSE; + WINE_TRACE("header: program %s title %s startdir %s params %s\n", + wine_dbgstr_a(pifheader.program), + wine_dbgstr_an(pifheader.windowtitle, sizeof(pifheader.windowtitle)), + wine_dbgstr_a(pifheader.startdir), + wine_dbgstr_a(pifheader.optparams)); + WINE_TRACE("header: memory req'd %d desr'd %d drive %d videomode %d\n", + pifheader.memmin, pifheader.memmax, pifheader.startdrive, + pifheader.videomode); + WINE_TRACE("header: flags 0x%x 0x%x 0x%x\n", + pifheader.hdrflags1, pifheader.hdrflags2, pifheader.hdrflags3); + ReadFile( hFile, &rhead, sizeof(recordhead_t), &nread, NULL); + if( strncmp( rhead.recordname, "MICROSOFT PIFEX", 15)) { + WINE_ERR("Invalid pif file: magic string not found\n"); + return FALSE; + } + /* now process the following records */ + while( 1) { + WORD nextrecord = rhead.posofnextrecord; + if( (nextrecord & 0x8000) || + filesize.QuadPart <( nextrecord + sizeof(recordhead_t))) break; + if( !SetFilePointer( hFile, nextrecord, NULL, FILE_BEGIN) || + !ReadFile( hFile, &rhead, sizeof(recordhead_t), &nread, NULL)) + return FALSE; + if( !rhead.recordname[0]) continue; /* deleted record */ + WINE_TRACE("reading record %s size %d next 0x%x\n", + wine_dbgstr_a(rhead.recordname), rhead.sizeofdata, + rhead.posofnextrecord ); + if( !strncmp( rhead.recordname, "WINDOWS 386", 11)) { + found386rec = TRUE; + ReadFile( hFile, &pif386rec, sizeof(pif386rec_t), &nread, NULL); + WINE_TRACE("386rec: memory req'd %d des'd %d EMS req'd %d des'd %d XMS req'd %d des'd %d\n", + pif386rec.memmin, pif386rec.memmax, + pif386rec.emsmin, pif386rec.emsmax, + pif386rec.xmsmin, pif386rec.xmsmax); + WINE_TRACE("386rec: option 0x%x memory 0x%x video 0x%x \n", + pif386rec.optflags, pif386rec.memflags, + pif386rec.videoflags); + WINE_TRACE("386rec: optional parameters %s\n", + wine_dbgstr_a(pif386rec.optparams)); + } + } + /* prepare the return data */ + strncpy( progname, pifheader.program, sizeof(pifheader.program)); + memcpy( title, pifheader.windowtitle, sizeof(pifheader.windowtitle)); + title[ sizeof(pifheader.windowtitle) ] = '\0'; + if( found386rec) + strncpy( optparams, pif386rec.optparams, sizeof( pif386rec.optparams)); + else + strncpy( optparams, pifheader.optparams, sizeof(pifheader.optparams)); + strncpy( startdir, pifheader.startdir, sizeof(pifheader.startdir)); + *closeonexit = pifheader.hdrflags1 & 0x10; + *textmode = found386rec ? pif386rec.videoflags & 0x0010 + : pifheader.hdrflags1 & 0x0002; + return TRUE; +} + +/*********************************************************************** + * pif_cmd + * + * execute a pif file. + */ +static VOID pif_cmd( char *filename, char *cmdline) +{ + HANDLE hFile; + char progpath[MAX_PATH]; + char buf[128]; + char progname[64]; + char title[31]; + char optparams[64]; + char startdir[64]; + char *p; + int closeonexit; + int textmode; + if( (hFile = CreateFileA( filename, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE) + { + WINE_ERR("open file %s failed\n", wine_dbgstr_a(filename)); + return; + } + if( !read_pif_file( hFile, progname, title, optparams, startdir, + &closeonexit, &textmode)) { + WINE_ERR( "failed to read %s\n", wine_dbgstr_a(filename)); + CloseHandle( hFile); + sprintf( buf, "%s\nInvalid file format. Check your pif file.", + filename); + MessageBoxA( NULL, buf, "16 bit DOS subsystem", MB_OK|MB_ICONWARNING); + SetLastError( ERROR_BAD_FORMAT); + return; + } + CloseHandle( hFile); + if( (p = strrchr( progname, '.')) && !strcasecmp( p, ".bat")) + WINE_FIXME(".bat programs in pif files are not supported.\n"); + /* first change dir, so the search below can start from there */ + if( startdir[0] && !SetCurrentDirectory( startdir)) { + WINE_ERR("Cannot change directory %s\n", wine_dbgstr_a( startdir)); + sprintf( buf, "%s\nInvalid startup directory. Check your pif file.", + filename); + MessageBoxA( NULL, buf, "16 bit DOS subsystem", MB_OK|MB_ICONWARNING); + } + /* search for the program */ + if( !SearchPathA( NULL, progname, NULL, MAX_PATH, progpath, NULL )) { + sprintf( buf, "%s\nInvalid program file name. Check your pif file.", + filename); + MessageBoxA( NULL, buf, "16 bit DOS subsystem", MB_OK|MB_ICONERROR); + SetLastError( ERROR_FILE_NOT_FOUND); + return; + } + if( textmode) + if( AllocConsole()) + SetConsoleTitleA( title) ; + /* if no arguments on the commandline, use them from the pif file */ + if( !cmdline[0] && optparams[0]) + cmdline = optparams; + /* FIXME: do something with: + * - close on exit + * - graphic modes + * - hot key's + * - etc. + */ + wine_load_dos_exe( progpath, cmdline ); + return; +} + /*********************************************************************** * build_command_line * @@ -164,6 +383,7 @@ int main( int argc, char *argv[] ) char buffer[MAX_PATH]; STARTUPINFOA info; char *cmdline, *appname, **first_arg; + char *p; if (!argv[1]) usage(); @@ -217,10 +437,15 @@ int main( int argc, char *argv[] ) if ((instance = LoadModule16( appname, ¶ms )) < 32) { - if (instance == 11) /* try DOS format */ + if (instance == 11) { - /* loader expects arguments to be regular C strings */ - wine_load_dos_exe( appname, cmdline + 1 ); + /* first see if it is a .pif file */ + if( ( p = strrchr( appname, '.' )) && !strcasecmp( p, ".pif")) + pif_cmd( appname, cmdline + 1); + else + /* try DOS format */ + /* loader expects arguments to be regular C strings */ + wine_load_dos_exe( appname, cmdline + 1 ); /* if we get back here it failed */ instance = GetLastError(); } @@ -229,7 +454,7 @@ int main( int argc, char *argv[] ) switch (instance) { case 2: WINE_MESSAGE("file not found\n" ); break; - case 11: WINE_MESSAGE("invalid exe file\n" ); break; + case 11: WINE_MESSAGE("invalid program file\n" ); break; default: WINE_MESSAGE("error=%d\n", instance ); break; } ExitProcess(instance);