|
|
/*
* Microsoft Confidential * Copyright (C) Microsoft Corporation 1991 * All Rights Reserved. * * * PIFMGR.C * Main module for PIFMGR.DLL * * History: * Created 31-Jul-1992 3:30pm by Jeff Parsons * * Exported Program Information File (PIF) Manager services: * * PifMgr_OpenProperties() * Give it the name of an DOS application (com, exe, or bat), * and it will open the PIF associated with that application * and return a "handle" to the app's "properties". Use this * handle when calling any of the other "properties" services (ie, * Get, Set, and Close). * * If no PIF exists, it will still allocate a PIF data block * in memory and initialize it, either with data from _DEFAULT.PIF * or its internal defaults. It will also construct the PIF name * it was looking for but couldn')t find and save that in its internal * PIF data structure, so that if PifMgr_SetProperties is ever called, the * data can be saved to disk. * * PifMgr_GetProperties() * Returns the specified block of data from the associated PIF. * If it is a "named" block, it must be the name of a linked * extension inside the PIF, which can be any predefined name * (eg, "WINDOWS 386 3.0") or the name of your own block. You can * create your own named data blocks using the PifMgr_SetProperties() * service. "Named" data can also be thought of as "raw" data, * because it is returned to the caller as-is -- without translation. * * The size of a named block can be determined by calling * PifMgr_GetProperties with a size of zero; no data is copied, but the size * of the requested block is returned (0 if not found). * * All named blocks can be enumerated by passing NULL for the name, * a pointer to a 16-byte buffer for the requested block name, and a * 0-based block index in the size parameter. The size returned * is the size of the block (0 if none). * * If an unnamed property block is requested (ie, the selector of * the name parameter is NULL, and the offset is a property group * ordinal), then the associated structure is returned. For example, * PifMgr_GetProperties(GROUP_TSK) returns a predefined structure (see * PROPTSK in PIF.H) containing all the tasking-related information, * in a format that is PIF-independent. This is a valuable service, * because it relieves callers from having to cope with PIFs * containing a wide variety of sections (known as PIF extensions), * only one of which is required. Think of this as "cooked" data. * * A third variation is raw read/write of the entire PIF data block, * if lpszGroup is NULL. This must be used with extreme caution, and * will only be allowed if the properties were opened with the * OPENPROPS_RAWIO flag specified. * * PifMgr_SetProperties() * This is pretty much the opposite of PifMgr_GetProperties, except that it * also takes a flags parameter that can specify that the changes * be made immediately, or deferred to PifMgr_CloseProperties. * * PifMgr_CloseProperties() * Flushes any dirty PIF data in memory, and frees the local heap * storage. * */
#include "shellprv.h"
#pragma hdrstop
/* Global R/W DLL data
*/
PPROPLINK g_pplHead; // pointer to first prop entry
HANDLE g_offHighestPropLink; // highest offset of a prop thus far recorded
TCHAR g_szNone[16]; // initialized by LibMainP,
TCHAR g_szAuto[16]; // and 16 chars to allow for localization
char g_szMSDOSSTSFile[] = "C:\\MSDOSSYS.STS";
TCHAR g_szConfigFile[] = TEXT("C:") CONFIGFILE; TCHAR g_szAutoexecFile[] = TEXT("C:") AUTOEXECFILE;
TCHAR g_szMConfigFile[] = TEXT("C:") MCONFIGFILE; TCHAR g_szMAutoexecFile[] = TEXT("C:") MAUTOEXECFILE;
TCHAR g_szWConfigFile[] = TEXT("C:") WCONFIGFILE; TCHAR g_szWAutoexecFile[] = TEXT("C:") WAUTOEXECFILE;
#ifdef DBCS
char ImeBatchFile[] = "DOSIME\0"; #endif
#define NT_CONFIG_FILE "%SystemRoot%\\SYSTEM32\\CONFIG.NT"
#define NT_AUTOEXEC_FILE "%SystemRoot%\\SYSTEM32\\AUTOEXEC.NT"
#define LPPH_OFF(off) ((LPBYTE)lpph + off)
#define LPPIF_FIELDOFF(off) ((LPBYTE)ppl->lpPIFData + FIELD_OFFSET(PIFDATA,off))
#define LPPIF_OFF(off) ((LPBYTE)ppl->lpPIFData + off)
//
// g_szDefaultPIF can be in one of three states:
//
// 1. "_DEFAULT.PIF", which means that we have never needed to search
// for a _default.pif yet. The next time we need to locate
// _default.pif, we must perform a full search. On success,
// move to state 2. On failure, move to state 3.
//
// 2. A fully-qualified path to _default.pif, which means that we have
// searched for a _default.pif and found it in the specified
// location. The next time we need to locate _default.pif, we
// will look here. If found, remain in state 2, else move to
// state 3.
//
// 3. The null string, which means that we searched for a _default.pif
// and didn't find one. The next time we need to locate
// _default.pif, we just fail without even looking on the disk.
// (This is the common case for a clean install.)
//
// Note that all the cases are "sticky"; once you reach a state, you
// can never move back to a previous state. This sacrifices flexibility
// for performance.
//
// The macro fTryDefaultPif() returns nonzero if we are in cases
// 1 or 2.
//
// The macro fDefaultPifFound() returns nonzero if we are in case 2.
//
// WARNING! WARNING! WARNING! WARNING!
//
// Evil hack relies on the fact that the three states can be
// distinguished by the first character of g_szDefaultPIF, which
// in turn relies on the fact that `_' cannot be the first character
// of a fully-qualified path. (It is not a valid drive letter,
// and it cannot start a UNC.)
//
//
#define INIT_INIDATA 0x01
#define INIT_PIFDIR 0x02
CHAR fbInit = 0; // see INIT_* flags
INT iPIFName = (12*sizeof(TCHAR)); // strlen(g_szPIFDir)
INT iWinName = (12*sizeof(TCHAR)); // strlen(g_szPIFDir)
TCHAR g_szPIFDir[MAXPATHNAME] = TEXT("\\WINDOWS\\PIF"); TCHAR g_szDefaultPIF[MAXPATHNAME] = TEXT("_DEFAULT.PIF");
#define fTryDefaultPif() g_szDefaultPIF[0]
#define fDefaultPifFound() (g_szDefaultPIF[0] != TEXT('_'))
//
// szComspec is the name of the COMSPEC program, usually "COMMAND.COM"
// or "CMD.EXE".
//
TCHAR szComspec[8+1+3+1];
/* Global R/O DLL data
*/
extern const TCHAR c_szNULL[]; // A string so nice...
const TCHAR szZero[] = TEXT("0");
const int acbData[] = { sizeof(PROPPRG), sizeof(PROPTSK), sizeof(PROPVID), sizeof(PROPMEM), sizeof(PROPKBD), sizeof(PROPMSE), sizeof(PROPSND), sizeof(PROPFNT), sizeof(PROPWIN), sizeof(PROPENV), sizeof(PROPNT31), sizeof(PROPNT40), };
/*
* The casts are used because we intentionally mis-prototyped the GetXxxData * and SetXxxData functions to receive their third argument as a LPXXX instead * of a LPVOID. */
const DATAGETFN afnGetData[] = { (DATAGETFN)GetPrgData, (DATAGETFN)GetTskData, (DATAGETFN)GetVidData, (DATAGETFN)GetMemData, (DATAGETFN)GetKbdData, (DATAGETFN)GetMseData, (DATAGETFN)GetSndData, (DATAGETFN)GetFntData, (DATAGETFN)GetWinData, (DATAGETFN)GetEnvData, (DATAGETFN)GetNt31Data, (DATAGETFN)GetNt40Data, };
const DATASETFN afnSetData[] = { (DATASETFN)SetPrgData, (DATASETFN)SetTskData, (DATASETFN)SetVidData, (DATASETFN)SetMemData, (DATASETFN)SetKbdData, (DATASETFN)SetMseData, (DATASETFN)SetSndData, (DATASETFN)SetFntData, (DATASETFN)SetWinData, (DATASETFN)SetEnvData, (DATASETFN)SetNt31Data, (DATASETFN)SetNt40Data, };
// WIN.INI things of interest
// Note: some of these NEED to be ANSI strings, and other TCHAR
// strings. Please do not arbitrarily change the type casts of
// these strings!!!! (RickTu)
const TCHAR szMemory[] = TEXT("MEMORY"); const TCHAR szComp[] = TEXT("COMPATIBLE");
CHAR szSingle[] = "DOS=SINGLE\r\n"; CHAR szCRLF[] = "\r\n"; CHAR szEcho[] = "ECHO "; CHAR szPause[] = "\r\nPAUSE\r\n"; CHAR szCall[] = "CALL "; CHAR szCD[] = "CD "; CHAR szWin[] = "WIN";
// SYSTEM.INI things of interest
const TCHAR szSystemINI[] = TEXT("SYSTEM.INI"); const TCHAR sz386EnhSection[] = TEXT("386Enh"); const TCHAR szWOAFontKey[] = TEXT("WOAFont"); const TCHAR szWOADBCSFontKey[] = TEXT("WOADBCSFont"); const TCHAR szNonWinSection[] = TEXT("NonWindowsApp"); const TCHAR szTTInitialSizes[] = TEXT("TTInitialSizes"); #ifdef CUSTOMIZABLE_HEURISTICS
const TCHAR szTTHeuristics[] = TEXT("TTHeuristics"); const TCHAR szTTNonAspectMin[] = TEXT("TTNonAspectMin"); #endif
TCHAR szTTCacheSection[2][32] = {TEXT("TTFontDimenCache"), TEXT("TTFontDimenCacheDBCS")};
//
// These are because they are accessed only when we need to create
// a new PIF file or convert a 3.1 PIF file into a 4.0 PIF file.
//
const TCHAR szDOSAPPINI[] = TEXT("DOSAPP.INI"); const TCHAR szDOSAPPSection[] = TEXT("DOS Applications"); const TCHAR szDOSAPPDefault[] = TEXT("Default");
const TCHAR szDisplay[] = TEXT("DISPLAY"); const TCHAR szDefIconFile[] = ICONFILE_DEFAULT;
const TCHAR szDotExe[] = TEXT(".EXE"); const TCHAR szDotCom[] = TEXT(".COM"); const TCHAR szDotBat[] = TEXT(".BAT"); const TCHAR szDotPif[] = TEXT(".PIF"); const TCHAR szDotCmd[] = TEXT(".CMD"); const TCHAR * apszAppType[] = { szDotExe, szDotCom, szDotBat, szDotCmd, szDotPif };
CHAR szSTDHDRSIG[] = STDHDRSIG; CHAR szW286HDRSIG30[] = W286HDRSIG30; CHAR szW386HDRSIG30[] = W386HDRSIG30; CHAR szWENHHDRSIG40[] = WENHHDRSIG40; CHAR szWNTHDRSIG31[] = WNTHDRSIG31; CHAR szWNTHDRSIG40[] = WNTHDRSIG40;
CHAR szCONFIGHDRSIG40[] = CONFIGHDRSIG40; CHAR szAUTOEXECHDRSIG40[] = AUTOEXECHDRSIG40; const TCHAR szRunOnceKey[] = REGSTR_PATH_RUNONCE;
const TCHAR szPIFConvert[] = TEXT("PIFConvert"); const TCHAR szPIFConvertExe[] = TEXT("RUNDLL.EXE PIFMGR.DLL,ProcessStartupProperties"); const TCHAR szPIFConvertKey[] = REGSTR_PATH_PIFCONVERT; const TCHAR szMSDOSMode[] = REGSTR_VAL_MSDOSMODE; const TCHAR szMSDOSModeDiscard[] = REGSTR_VAL_MSDOSMODEDISCARD;
// wsprintf formatting strings
const TCHAR szDotPercent03d[] = TEXT(".%03d");
// miscellaneous hack-o-ramas
const TCHAR szPP4[] = TEXT("PP4"); // MS Powerpoint 4.0
PROPTSK tskDefault ={TSK_DEFAULT, TSKINIT_DEFAULT, TSKFGNDBOOST_DEFAULT, TSKBGNDBOOST_DEFAULT, 0, 0, TSKIDLESENS_DEFAULT, };
PROPVID vidDefault ={VID_DEFAULT, VIDINIT_DEFAULT, 0, 0, 0, };
PROPMEM memDefault ={MEM_DEFAULT, MEMINIT_DEFAULT, MEMLOW_DEFAULT, // ignore stdpifdata.minmem?
MEMLOW_MAX, // ignore stdpifdata.maxmem?
MEMEMS_DEFAULT, MEMEMS_MAX, MEMXMS_DEFAULT, MEMXMS_MAX, };
PROPKBD kbdDefault ={KBD_DEFAULT, KBDINIT_DEFAULT, KBDALTDELAY_DEFAULT, KBDALTPASTEDELAY_DEFAULT, KBDPASTEDELAY_DEFAULT, KBDPASTEFULLDELAY_DEFAULT, KBDPASTETIMEOUT_DEFAULT, KBDPASTESKIP_DEFAULT, KBDPASTECRSKIP_DEFAULT, };
PROPMSE mseDefault ={MSE_DEFAULT, MSEINIT_DEFAULT, };
PROPENV envDefault ={ENV_DEFAULT, ENVINIT_DEFAULT, "", ENVSIZE_DEFAULT, ENVDPMI_DEFAULT, };
WORD flWinDefault = WIN_DEFAULT;
/*
* Default face name to use for Raster fonts. Currently, this is * just a hard-coded value (ie, not maintained in any INI file). */ CHAR szRasterFaceName[LF_FACESIZE] = "Terminal";
/*
* Default face name to use for TrueType fonts. It must be a monospace * font, and it must be a font that everyone is guaranteed to have. Currently, * this can be changed by setting TTFont in [NonWindowsApp] in SYSTEM.INI. */ // now this is initialized with string resource. The 2nd element will get
// the native typeface for the bilingual dos prompt
CHAR szTTFaceName[2][LF_FACESIZE] = {"Lucida Console", "Courier New"};
const TCHAR szAltKeyDelay [] = TEXT("AltKeyDelay"); const TCHAR szAltPasteDelay [] = TEXT("AltPasteDelay"); const TCHAR szKeyPasteDelay [] = TEXT("KeyPasteDelay"); const TCHAR szKeyBufferDelay [] = TEXT("KeyBufferDelay"); const TCHAR szKeyPasteTimeout [] = TEXT("KeyPasteTimeout"); const TCHAR szKeyPasteSkipCount [] = TEXT("KeyPasteSkipCount"); const TCHAR szKeyPasteCRSkipCount[] = TEXT("KeyPasteCRSkipCount"); const TCHAR szMouseInDosBox [] = TEXT("MouseInDosBox"); const TCHAR szDisablePositionSave[] = TEXT("DisablePositionSave"); const TCHAR szDOSPromptExitInst [] = TEXT("DOSPromptExitInstruc"); const TCHAR szCommandEnvSize [] = TEXT("CommandEnvSize"); const TCHAR szScreenLines [] = TEXT("ScreenLines");
const INIDATA aINIData[] = { {sz386EnhSection, szAltKeyDelay, &kbdDefault.msAltDelay, INIDATA_FIXEDPOINT}, {sz386EnhSection, szAltPasteDelay, &kbdDefault.msAltPasteDelay, INIDATA_FIXEDPOINT}, {sz386EnhSection, szKeyPasteDelay, &kbdDefault.msPasteDelay, INIDATA_FIXEDPOINT}, {sz386EnhSection, szKeyBufferDelay, &kbdDefault.msPasteFullDelay,INIDATA_FIXEDPOINT}, {sz386EnhSection, szKeyPasteTimeout, &kbdDefault.msPasteTimeout, INIDATA_FIXEDPOINT}, {sz386EnhSection, szKeyPasteSkipCount, &kbdDefault.cPasteSkip, INIDATA_DECINT}, {sz386EnhSection, szKeyPasteCRSkipCount, &kbdDefault.cPasteCRSkip, INIDATA_DECINT}, {szNonWinSection, szMouseInDosBox, &mseDefault.flMse, INIDATA_BOOLEAN, MSE_WINDOWENABLE}, {szNonWinSection, szDisablePositionSave, &flWinDefault, INIDATA_BOOLEAN | INIDATA_INVERT, WIN_SAVESETTINGS}, #ifdef ENVINIT_INSTRUCTIONS
{sz386EnhSection, szDOSPromptExitInst, &envDefault.flEnvInit, INIDATA_BOOLEAN, ENVINIT_INSTRUCTIONS}, #endif
{szNonWinSection, szCommandEnvSize, &envDefault.cbEnvironment, INIDATA_DECINT}, {szNonWinSection, szScreenLines, &vidDefault.cScreenLines, INIDATA_DECINT}, };
/**************************************************************************
* * OVERVIEW OF INI FILE USAGE * * * SYSTEM.INI * * [386Enh] * * WOAFont=<fon filename> * * Status: Public * Default: dosapp.fon * Purpose: * * This setting allows the user to specify which Terminal font * file should be loaded when DOS box is started. * * To change: * * Use Notepad to edit the SYSTEM.INI file. * * * [NonWindowsApp] * * DisablePositionSave=<Boolean> * * Status: Public * Default: 0 (FALSE) * Purpose: * * When FALSE, the position and font used in a non-Windows * application is saved in the application's PIF file when * you exit the application. When TRUE, the position, fonts, and * toolbar state of a non-Windows application whose settings * have not been previously saved in the DOSAPP.INI file will * not be saved. * * If enabled, the setting can be overridden for each * non-Windows application by selecting the Save Settings On * Exit check box in the Font dialog box. * * To change: * * Use Notepad to edit the SYSTEM.INI file. * * Compatibility notes: * * In Windows 3.x, the "position save" (and font) information was * saved in DOSAPP.INI, and although we will still read DOSAPP.INI * in the absence of any information in the PIF file, we only *write* * settings back to the PIF file. DOSAPP.INI should be considered * obsolete. * * * TTFont=<fontname> * * Status: ? * Default: Courier New // FEATURE -- this should be a TT OEM font
* Purpose: * * This setting allows the user to specify which TrueType font * will be used in a DOS box. It must be an OEM font. * * To change: * * Use Notepad to edit the SYSTEM.INI file. * * * TTInitialSizes=<i1 i2 i3 i4 ... i16> * * Status: ? * Default: 4 5 6 7 8 9 10 11 12 14 16 18 20 22 36 72 * Purpose: * * This setting allows the user to specify which font sizes * WinOldAp initially builds for the TrueType fonts in a DOS * application window. * * At most 16 font sizes can be requested. * * Note that this INI entry is consulted only the first time * Windows is restarted after changing video drivers or fonts. * * To change: * * Use Notepad to edit the SYSTEM.INI file. * * * TTHeuristics=<i1 i2 i3 i4 i5 i6 i7 i8 i9> * * Status: Public * Default: 5000 1000 0 1000 5000 1000 0 1000 1 * Purpose: * * These integers control the way Windows chooses the font to * display for DOS applications running inside a window if you * have chosen "Auto" as the font size. * * The parameters are named as follows: * * i1=XOvershootInitial * i2=XOvershootScale * i3=XShortfallInitial * i4=XShortfallScale * i5=YOvershootInitial * i6=YOvershootScale * i7=YShortfallInitial * i8=YShortfallScale * i9=TrueTypePenalty * * Each penalty value may not exceed 5000. * * When Windows needs to select a font for use in a DOS * application's window, it goes through the list of font * sizes available and computes the "penalty" associated * with using that font. Windows then selects the font with * the smallest penalty. * * The horizontal penalty is computed as follows: * * Let dxActual = <actual window width> * Let dxDesired = <font width> * <characters per line> * * If dxActual = dxDesired: * xPenalty = 0 * If dxActual < dxDesired: * Let Ratio = 1 - dxDesired / dxActual * xPenalty = XOvershootInitial + Ratio * XOvershootScale * If dxActual > dxDesired: * Let Ratio = 1 - dxActual / dxDesired * xPenalty = XShortfallInitial + Ratio * XShortfallScale * * The vertical penalty is computed similarly. * * Note that the Ratio is always a fraction between 0 and 1. * * The penalty associated with a font is the sum of the vertical * and horizontal penalties, plus the TrueTypePenalty if the font * is a TrueType font. * * The default value of 1 for the TrueTypePenalty means that, * all other things being equal, Windows will select a raster * font in preference to a TrueType font. You can set this * value to -1 if you wish the opposite preference. * * To change: * * Use Notepad to edit the SYSTEM.INI file. * * Internals: * * Even though floating point appears in the computations, * everything is really done in integer arithmetic. * * Pixels are NEVER MENTIONED anywhere in the penalty computations. * (All pixel values are divided by other pixel values, so that * we get a dimensionless number as a result.) * This keeps us independent of the display resolution as well * as the display aspect ratio. * * Since the stretch and shrink are taken as fractions of the * larger dimension, this keeps us from penalizing large * differences by too much. This is important because there * isn't much visible difference between being ten times too * big and being eleven times too big, but there is a big * difference between being just right and being twice as big. * * We must be careful not to let the maximum possible penalty * exceed 32767. This is done by making sure that each * dimension cannot produce a penalty of greater than 10000 * (5000+5000), and that the TrueTypePenalty is at most 5000. * This makes the maximum possible penalty 25000. * This range checking is done by FontSelInit. * * * TTNonAspectMin=<x y> * * Status: Public * Default: 3 3 * Purpose: * * These integers control the minimum width and height font that * Windows will attempt to create automatically in response to a * resize operation when TrueType fonts in DOS boxes are enabled * and the "Auto" font size is selected. * * These values prevent Windows from creating visually useless * fonts like 10 x 1 or 1 x 10. The default values prevent Windows * from trying to create X x Y fonts if X < 3 or Y < 3. * * TTNonAspectMin is not consulted if the font is being created at * its default aspect ratio. In other words, Windows will create, * for example, a 1 x 3 font, if 1 x 3 is the standard aspect ratio * for a 3-pixel-high font. * * To permit all aspect ratios, set the values to "0 0". * * To forbid all aspect ratios except for the standard aspect ratio, * set the values to "-1 -1". * * [TTFontDimenCache] * * dxWidthRequested dyHeightRequested=dxWidthActual dyWidthActual * * Status: Private * Default: Null * Purpose: * * The [FontDimenCache] section contains information about * TrueType font sizes that have been created. Each entry * has as the keyname the width and height that were passed * to CreateFont and has as the value the width and height of * the font that was actually created. * * Internals: * * Inspected by AddTrueTypeFontsToFontList. * Set by AddOneNewTrueTypeFontToFontList. * * ************************************************************************** * * DOSAPP.INI (obsolete, supported on a read-only basis) * * [Dos Applications] * * C:\FULL\PATH\TO\EXE\COM\BAT\OR.PIF=<wFlags wFontWidth wFontHeight * wWinWidth wWinHeight length flags showCmd ptMinPositionX * ptMinPositionY ptMaxPositionX ptMaxPositionY * rcNormalLeft rcNormalTop rcNormalRight rcNormalBottom> * * Status: Private * Purpose: * * These values are used to restore a DOS application's window * to the state it was in when the DOS app last exited normally. * * The values are taken directly from the INIINFO structure, qv. * * The values of ptMinPositionX and ptMinPositionY are always -1, * since we do not try to preserve the icon position. * * If wFontHeight has the high bit set, then the font that * should be used is a TrueType font. * * If wFontWidth = 1 and wFontHeight = -1, then * Auto-font-selection is active. * * Compatibility notes: * * In Windows 3.x, the "position save" (and font) information was * saved in DOSAPP.INI, and although we will still read DOSAPP.INI * in the absence of any information in the PIF file, we only *write* * settings back to the PIF file. DOSAPP.INI should be considered * obsolete. * * ************************************************************************** * * THE NEXT INI VAR IS NOT IMPLEMENTED BUT SHOULD BE * ************************************************************************** * * SYSTEM.INI * * [NonWindowsApp] * * TTFontTolerance=<i> * * Status: Public * Default: 200 * Purpose: * * This setting indicates how large a penalty (see TTHeuristics) * Windows should tolerate before trying to synthesize new font * sizes from TrueType fonts. * * Decreasing this value will result in a tighter fit of the * Windows-selected font to the actual window size, but at a * cost in speed and memory. * * To change: * * Use Notepad to edit the SYSTEM.INI file. * * * Internals: * * Inspected by ChooseBestFont, if implemented. * **************************************************************************/
void PifMgrDLL_Init() { static BOOL fInit = FALSE; if (!fInit) { LoadString(g_hinst, IDS_PIF_NONE, g_szNone, ARRAYSIZE(g_szNone)); LoadString(g_hinst, IDS_AUTONORMAL, g_szAuto, ARRAYSIZE(g_szAuto)); LoadGlobalFontData(); fInit = TRUE; } }
void PifMgrDLL_Term() { FreeGlobalFontData(); }
/** GetPIFDir - Form default PIF directory name + name of given file
* * INPUT * None * * OUTPUT * None */
void GetPIFDir(LPTSTR pszName) { int i; static const TCHAR szBackslashPIF[] = TEXT("\\PIF"); FunctionName(GetPIFDir);
if (!(fbInit & INIT_PIFDIR)) {
// Set up g_szPIFDir, less space for a filename, less space for \PIF
i = ARRAYSIZE(g_szPIFDir)-lstrlen(pszName)-ARRAYSIZE(szBackslashPIF); if (i <= 0) // sanity check
return;
GetWindowsDirectory(g_szPIFDir, i); iPIFName = lstrlen(g_szPIFDir); if (StrRChr(g_szPIFDir, NULL, TEXT('\\')) == &g_szPIFDir[iPIFName-1]) iPIFName--; iWinName = iPIFName;
lstrcpy(g_szPIFDir+iPIFName, szBackslashPIF); iPIFName += ARRAYSIZE(szBackslashPIF)-1;
i = (int)GetFileAttributes(g_szPIFDir);
if (i == -1) {
// It didn't exist, so try to create it (returns TRUE if success)
i = CreateDirectory(g_szPIFDir, NULL); if (i) SetFileAttributes(g_szPIFDir, FILE_ATTRIBUTE_HIDDEN); } else if (i & FILE_ATTRIBUTE_DIRECTORY) i = TRUE; // directory already exists, cool!
else i = FALSE; // some sort of file is in the way...
if (i) { g_szPIFDir[iPIFName++] = TEXT('\\'); // append the slash we'll need
// to separate future filenames (the
// space after is already zero-init'ed)
} else // we'll just have to use the Windows dir
iPIFName -= ARRAYSIZE(szBackslashPIF)-2;
fbInit |= INIT_PIFDIR; }
// Now initialize g_szPIFDir with the name of the file we're processing
if (pszName) lstrcpyn(g_szPIFDir+iPIFName, pszName, ARRAYSIZE(g_szPIFDir)-iPIFName); }
/** GetINIData - Read WIN.INI/SYSTEM.INI/DOSAPP.INI for default settings
* * INPUT * Nothing * * OUTPUT * Nothing; global defaults (re)set * * NOTES * We only do this work now if GetPIFData couldn't open a PIF file, or * could but it contained no enhanced section. And we never do it more than * once per fresh load of this DLL. */
void GetINIData() { int t; const INIDATA *pid; LPCTSTR lpsz; DWORD dwRet; TCHAR szTemp[MAX_PATH]; FunctionName(GetINIData);
if (fbInit & INIT_INIDATA) // if already done
return; // then go away
for (pid=aINIData; pid-aINIData < ARRAYSIZE(aINIData); pid++) {
t = *(INT UNALIGNED *)pid->pValue; if (pid->iFlags & (INIDATA_DECINT | INIDATA_BOOLEAN)) {
if (pid->iFlags & INIDATA_BOOLEAN) { t &= pid->iMask; if (pid->iFlags & INIDATA_INVERT) t ^= pid->iMask; } t = GetPrivateProfileInt(pid->pszSection, pid->pszKey, t, szSystemINI); if (pid->iFlags & INIDATA_BOOLEAN) { if (t) t = pid->iMask; if (pid->iFlags & INIDATA_INVERT) t ^= pid->iMask; t |= *(INT UNALIGNED *)pid->pValue & ~pid->iMask; } *(INT UNALIGNED *)pid->pValue = t; } else if (pid->iFlags & INIDATA_FIXEDPOINT) { wsprintf(szTemp, szDotPercent03d, t); GetPrivateProfileString(pid->pszSection, pid->pszKey, szTemp, szTemp, ARRAYSIZE(szTemp), szSystemINI); *(INT UNALIGNED *)pid->pValue = StrToInt(szTemp+1); } else ASSERTFAIL(); }
//
// Locate COMSPEC once and for all.
//
dwRet = GetEnvironmentVariable(TEXT("COMSPEC"), szTemp, ARRAYSIZE(szTemp)); if (dwRet < ARRAYSIZE(szTemp) && dwRet > 0) { lpsz = StrRChr(szTemp, NULL, TEXT('\\')); if (lpsz) { lstrcpyn(szComspec, lpsz+1, ARRAYSIZE(szComspec)); } }
fbInit |= INIT_INIDATA; }
/** InitProperties - initialize new property structure
* * INPUT * ppl -> property * fLocked == TRUE to return data locked, FALSE unlocked * * OUTPUT * Nothing (if successful, ppl->hPIFData will become non-zero) */
void InitProperties(PPROPLINK ppl, BOOL fLocked) { LPSTDPIF lpstd; LPW386PIF30 lp386 = NULL; CHAR achPathName[ARRAYSIZE(ppl->szPathName)]; BYTE behavior = 0; FunctionName(InitProperties);
GetINIData(); // make sure we have all the right defaults
if (ResizePIFData(ppl, sizeof(STDPIF)) != -1) {
// We're no longer called *only* after a fresh ZERO'd HeapAlloc
// by ResizePIFData. We could be getting called because PifMgr_OpenProperties
// was told to punt on an ambiguous PIF and create new settings.
// Hence, we always zero-init the buffer ourselves now.
BZero(ppl->lpPIFData, ppl->cbPIFData);
lpstd = (LPSTDPIF)ppl->lpPIFData; lpstd->id = 0x78; PifMgr_WCtoMBPath( ppl->szPathName, achPathName, ARRAYSIZE(achPathName) ); lstrcpyncharA(lpstd->appname, achPathName+ppl->iFileName, ARRAYSIZE(lpstd->appname), '.'); CharToOemA(lpstd->appname, lpstd->appname);
// NOTE: When 3.x Setup creates PIF files, it sets maxmem to 640;
// that's typically what memDefault.wMaxLow will be too....
lpstd->minmem = memDefault.wMinLow; lpstd->maxmem = (WORD) GetProfileInt(apszAppType[APPTYPE_PIF]+1, szMemory, memDefault.wMaxLow); lstrcpynA(lpstd->startfile, achPathName, ARRAYSIZE(lpstd->startfile)); CharToOemA(lpstd->startfile, lpstd->startfile);
//
// New for 4.0: fDestroy (close on exit) is disabled by default
// for most apps, but is enabled by default for COMSPEC.
//
lpstd->MSflags = 0; if (!lstrcmpi(ppl->szPathName+ppl->iFileName, szComspec)) { lpstd->MSflags = fDestroy; }
// Initialize various goofy non-zero stuff just to make it
// look like a backward-compatible PIF file -- not that we use
// or particularly care about any of it
// NOTE: When 3.x Setup creates PIF files, it sets screen to 0x7F
lpstd->cPages = 1; lpstd->highVector = 0xFF; lpstd->rows = 25; lpstd->cols = 80; lpstd->sysmem = 0x0007;
// fFullScreen is no longer default, so only if an explicit
// COMPATIBLE=FALSE exists in the PIF section of WIN.INI will
// we set fScreen in behavior and fFullScreen in PfW386Flags
// Similarly, fDestroy is no longer default, but we'll go
// back to the old way if the switch tells us to.
if (!GetProfileInt(apszAppType[APPTYPE_PIF]+1, szComp, TRUE)) { lpstd->behavior = behavior = fScreen; lpstd->MSflags = fDestroy; }
if (ppl->ckbMem != -1 && ppl->ckbMem != 1) lpstd->minmem = lpstd->maxmem = (WORD) ppl->ckbMem;
if (AddGroupData(ppl, szW386HDRSIG30, NULL, sizeof(W386PIF30))) { if (NULL != (lp386 = GetGroupData(ppl, szW386HDRSIG30, NULL, NULL))) { lp386->PfW386minmem = lpstd->minmem; lp386->PfW386maxmem = lpstd->maxmem; lp386->PfFPriority = TSKFGND_OLD_DEFAULT; lp386->PfBPriority = TSKBGND_OLD_DEFAULT; lp386->PfMinEMMK = memDefault.wMinEMS; lp386->PfMaxEMMK = memDefault.wMaxEMS; lp386->PfMinXmsK = memDefault.wMinXMS; lp386->PfMaxXmsK = memDefault.wMaxXMS; lp386->PfW386Flags = fBackground + fPollingDetect + fINT16Paste; if (behavior & fScreen) lp386->PfW386Flags |= fFullScreen; lp386->PfW386Flags2 = fVidTxtEmulate + fVidNoTrpTxt + fVidNoTrpLRGrfx + fVidNoTrpHRGrfx + fVidTextMd; } } VERIFYTRUE(AddEnhancedData(ppl, lp386)); if (AddGroupData(ppl, szWNTHDRSIG31, NULL, sizeof(WNTPIF31))) { LPWNTPIF31 lpnt31;
if (NULL != (lpnt31 = GetGroupData(ppl, szWNTHDRSIG31, NULL, NULL))) { lstrcpyA( lpnt31->nt31Prop.achConfigFile, NT_CONFIG_FILE ); lstrcpyA( lpnt31->nt31Prop.achAutoexecFile, NT_AUTOEXEC_FILE ); } } VERIFYTRUE(AddGroupData(ppl, szWNTHDRSIG40, NULL, sizeof(WNTPIF40)));
// Can't be dirty anymore, 'cause we just set everything to defaults
ppl->flProp &= ~PROP_DIRTY;
if (!fLocked) ppl->cLocks--; } else ASSERTFAIL(); }
/** OpenPIFFile - Wrapper around CreateFile for opening PIF files
* * The wrapper handles the following things: * * Passing the proper access and sharing flags to CreateFile. * Setting pof->nErrCode = 0 on success. * Converting ERROR_PATH_NOT_FOUND to ERROR_FILE_NOT_FOUND. * * INPUT * * pszFile -> name of file to attempt to open * pof -> PIFOFSTRUCT to fill in * * OUTPUT * * Same return code as CreateFile. * */
HANDLE OpenPIFFile(LPCTSTR pszFile, LPPIFOFSTRUCT pof) { HANDLE hf; TCHAR pszFullFile[ MAX_PATH ]; LPTSTR pszTheFile; DWORD dwRet;
//
// CreateFile does not search the path, so do that first, then
// give CreateFile a fully qualified file name to open...
//
dwRet = SearchPath( NULL, pszFile, NULL, ARRAYSIZE(pszFullFile), pszFullFile, &pszTheFile );
if ((dwRet==0) || (dwRet > ARRAYSIZE(pszFullFile))) { pszTheFile = (LPTSTR)pszFile; } else { pszTheFile = pszFullFile; }
hf = CreateFile( pszTheFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if (hf == INVALID_HANDLE_VALUE) { pof->nErrCode = GetLastError(); if (pof->nErrCode == ERROR_PATH_NOT_FOUND) pof->nErrCode = ERROR_FILE_NOT_FOUND; } else { LPTSTR lpDummy;
//
// NOTE: Special hack for creating shortcuts. If the PIF file
// that we find is 0 bytes long, pretend we did not find one at all.
// This is because appwiz renames a 0 length file from "New shortcut.lnk"
// to "appname.pif" and we end up finding it. We'll ignore this file.
//
if (SetFilePointer( hf, 0, NULL, FILE_END) == 0) { CloseHandle( hf ); hf = INVALID_HANDLE_VALUE; pof->nErrCode = ERROR_FILE_NOT_FOUND; } else { LPCTSTR pszNewFile; TCHAR szTemp[ ARRAYSIZE(pof->szPathName) ];
SetFilePointer( hf, 0, NULL, FILE_BEGIN ); pof->nErrCode = ERROR_SUCCESS;
// In some cases, people pass in two pointers to the same
// buffer. This will hose GetFullPathName, so if they
// are the same, then make a copy before calling GetFullPathName.
if (pszTheFile==pof->szPathName) { FillMemory( szTemp, sizeof(szTemp), 0 ); lstrcpy( szTemp, pszTheFile ); pszNewFile = szTemp; } else { pszNewFile = pszTheFile; } GetFullPathName( pszNewFile, ARRAYSIZE(pof->szPathName), pof->szPathName, &lpDummy ); } }
return hf; }
/** PifMgr_OpenProperties - return handle to property info for application
* * INPUT * lpszApp -> name of application * lpszPIF -> name of PIF file to use/create * hInf = Inf handle, 0 if none, -1 to inhibit Inf processing * flOpt = OPENPROPS_RAWIO to allow raw file updates; otherwise, 0 * * OUTPUT * handle to properties, FALSE if could not be opened, or out of memory * * REMARKS * This should not be thought of as a function that opens a file somewhere * on the disk (although that's usually the effect), but rather as an * property structure allocator that is optionally initialized by disk data * (currently, the file does not even remain open after this call). So the * main reason for failure in this function will be either a low memory * condition *or* inability to open a specific PIF file. * * The rules for PIF file searching are as follows: * * If not a .PIF file: * Search in current directory. * Endif. * * If path components were specified: * Search in specified directory. * Endif. * * Search in PIF directory. * Search the path. * * Note that this differs from the Windows 3.1 PIF search algorithm, which * was... * * Search current directory. * Search the path. * Search in application directory. * * This was a really bogus search order. Fortunately, it seems that * very few people relied on it. * * Things to watch out for when dorking the PIF file search order: * * Make sure editing PIF properties from the shell works. (I.e., * if a full path to a PIF is given, then use it; don't search.) * * Extra special thing to watch out for when dorking the PIF file * search order: * * MS Delta execs its child process as follows: * * CreatePif("C:\DELTA\DELTABAT.PIF"); * SetCurrentDirectory("C:\RANDOM\PLACE"); * WinExec("C:\TMP\DELTABAT.BAT", SW_HIDE); * * It expects the PIF search to pick up C:\DELTA\DELTABAT.PIF * from the path, even though the WinExec supplied a full path. * */
HANDLE WINAPI PifMgr_OpenProperties(LPCTSTR lpszApp, LPCTSTR lpszPIF, UINT hInf, UINT flOpt) { PPROPLINK ppl; LPTSTR pszExt; BOOL fError = FALSE; BOOL fFixedDisk = FALSE; BOOL fSearchInf = FALSE; BOOL fExplicitPIF = FALSE; PROPPRG prg; PROPNT40 nt40; LPTSTR pszName, pszFullName; #ifdef DBCS
PROPENV env; #endif
FunctionName(PifMgr_OpenProperties); // Allocate new prop
if (!(ppl = (PPROPLINK)LocalAlloc(LPTR, sizeof(PROPLINK)))) return 0;
if (!(pszFullName = (LPTSTR)LocalAlloc(LPTR, MAXPATHNAME*sizeof(TCHAR)))) { EVAL(LocalFree(ppl) == NULL); return 0; }
if ((HANDLE)ppl > g_offHighestPropLink) { g_offHighestPropLink = (HANDLE)ppl;
}
// Initialize the new prop
ppl->ppl = ppl; ppl->ckbMem = -1; ppl->iSig = PROP_SIG; ppl->hPIF = INVALID_HANDLE_VALUE; if (flOpt & OPENPROPS_RAWIO) ppl->flProp |= PROP_RAWIO;
#if (PRGINIT_INHIBITPIF != PROP_INHIBITPIF)
#error PRGINIT_INIHIBITPIF and PROP_INHIBITPIF out of sync!
#endif
ppl->flProp |= (flOpt & PROP_INHIBITPIF);
// Link into the global list
if (NULL != (ppl->pplNext = g_pplHead)) g_pplHead->pplPrev = ppl; g_pplHead = ppl;
// Copy app name to both temp and perm buffers, and record location
// of base filename, and extension if any, within the buffer
lstrcpyn(pszFullName, lpszApp, MAXPATHNAME-4); lstrcpyn(ppl->szPathName, pszFullName, ARRAYSIZE(ppl->szPathName));
if (NULL != (pszName = StrRChr(pszFullName, NULL, TEXT('\\'))) || NULL != (pszName = StrRChr(pszFullName, NULL, TEXT(':')))) pszName++; else pszName = pszFullName;
if (!(pszExt = StrRChr(pszName, NULL, TEXT('.')))) pszExt = pszFullName + lstrlen(pszFullName);
ppl->iFileName = (UINT) (pszName - pszFullName); ppl->iFileExt = (UINT) (pszExt - pszFullName);
// Check the application's file extension
if (!*pszExt) { lstrcat(pszFullName, apszAppType[APPTYPE_PIF]); } else if (!lstrcmpi(pszExt, apszAppType[APPTYPE_EXE]) || !lstrcmpi(pszExt, apszAppType[APPTYPE_COM]) || !lstrcmpi(pszExt, apszAppType[APPTYPE_BAT])) { // !lstrcmpi(pszExt, apszAppType[APPTYPE_CMD])) {
lstrcpy(pszExt, apszAppType[APPTYPE_PIF]); } else if (!lstrcmpi(pszExt, apszAppType[APPTYPE_PIF])) fExplicitPIF = TRUE; else { // Let's disallow random file extensions, since WinOldAp never
// allowed them either
goto Error; }
// INFONLY means the caller just wants to search the INF, so ignore
// any WIN.INI garbage and any PIFs laying around. We still look for
// _DEFAULT.PIF, since that code takes care of other important
// initialization that needs to happen when no PIF was found at all.
if (flOpt & OPENPROPS_INFONLY) goto FindDefault;
// Backward compatibility requires that if the app is not a PIF,
// then we must check the PIF section of WIN.INI for an entry matching
// the base name of the app. If the entry exists, then we have to skip
// the PIF search, and pass the value of the entry to InitProperties,
// which it uses to establish default memory requirements
//
// Also note that if IGNOREPIF is set, then ofPIF.szPathName is nothing
// more than the name of the app that was given to PifMgr_OpenProperties; this
// may give us the opportunity to do something more intelligent later...
if (!fExplicitPIF) { ppl->ckbMem = GetProfileInt(apszAppType[APPTYPE_PIF]+1, ppl->szPathName+ppl->iFileName, -1); if (ppl->ckbMem != -1) { ppl->flProp |= PROP_IGNOREPIF | PROP_SKIPPIF; lstrcpyn(ppl->ofPIF.szPathName, lpszApp, ARRAYSIZE(ppl->ofPIF.szPathName)); goto IgnorePIF; // entry exists, skip PIF file search
} }
//
// Initialize default error return code. Once we get a successful
// open, it will be set to zero.
//
ppl->flProp |= PROP_NOCREATEPIF; ppl->ofPIF.nErrCode = ERROR_FILE_NOT_FOUND;
//
// We must search in the current directory if not given a path to a PIF.
// We need to prefix `.\' to the filename so that OpenFile will not do
// a path search.
//
if (!fExplicitPIF || pszName == pszFullName) { //
// This relies on a feature of OpenFile, that it copies the input
// buffer to a private buffer before stomping the output buffer,
// thus permitting precisely the stunt we are pulling here, namely,
// passing an input buffer equal to the output buffer.
//
*(LPDWORD)(ppl->ofPIF.szPathName) = 0x005C002E; /*dot backslash prefix */ lstrcpyn( &ppl->ofPIF.szPathName[2], pszName, ARRAYSIZE(ppl->ofPIF.szPathName) - 2); ppl->hPIF = OpenPIFFile(ppl->ofPIF.szPathName, &ppl->ofPIF); }
//
// If we were given a path component, then look in that directory.
// (The fact that we have a backslash or drive letter will suppress
// the path search.)
//
if (pszName != pszFullName && ppl->ofPIF.nErrCode == ERROR_FILE_NOT_FOUND) {
ppl->hPIF = OpenPIFFile(pszFullName, &ppl->ofPIF);
// If we didn't find a PIF there, we'd probably still like to create
// one there if the media is a fixed disk. Network shares, CD-ROM
// drives, and floppies are not good targets for PIF files in general.
//
// So, if the media is a fixed disk, set the fFixedDisk flag so that
// we'll leave pszFullName alone.
if (ppl->hPIF == INVALID_HANDLE_VALUE && pszFullName[1] == TEXT(':')) { TCHAR szTemp[4];
lstrcpyn( szTemp, pszFullName, 4 );
szTemp[3] = (TCHAR)0;
if (GetDriveType(szTemp) == DRIVE_FIXED) fFixedDisk++; } }
// PERF: replace this PIF dir search with a registry search -JTP
//
// Failing that, let's look in the PIF directory. Again, since we're
// supplying a full pathname, OpenFile won't try to search the PATH again.
if (ppl->ofPIF.nErrCode == ERROR_FILE_NOT_FOUND) { GetPIFDir(pszName); ppl->hPIF = OpenPIFFile(g_szPIFDir, &ppl->ofPIF); if (ppl->hPIF != INVALID_HANDLE_VALUE) ppl->flProp |= PROP_PIFDIR; }
// If we're still in trouble, our last chance is to do a path
// search. This is an unconditional search, thanks to the
// wonders of MS-Delta.
if (ppl->ofPIF.nErrCode == ERROR_FILE_NOT_FOUND) { ppl->hPIF = OpenPIFFile(pszName, &ppl->ofPIF); }
if (ppl->hPIF == INVALID_HANDLE_VALUE) {
if (ppl->ofPIF.nErrCode != ERROR_FILE_NOT_FOUND || fExplicitPIF) {
// Hmmm, file *may* exist, but it cannot be opened; if it's a
// strange error, or we were specifically told to open that file,
// then return error
goto Error; }
FindDefault:
fSearchInf = TRUE; ppl->flProp &= ~PROP_NOCREATEPIF;
// Any files we find now are NOT really what we wanted, so save
// the name we'd like to use in the future, in case we need to save
// updated properties later.
//
// We must save the name now because we might stomp g_szPIFDir while
// searching for the _default.pif. Furthermore, we must save it in
// the buffer we HeapAlloc'ed (pszFullName) temporarily, because
// the following calls to OpenPIFFile can still stomp on szPathName
// in our OpenFile structure (ofPIF.szPathName).
GetPIFDir(pszName); if (!fFixedDisk) // save desired name in
lstrcpy(pszFullName, g_szPIFDir); // temp buffer (pszFullName)
//
// Try to locate the _default.pif.
//
if (fTryDefaultPif()) {
if (!fDefaultPifFound()) { // Must search for it
// First try PIFDir
lstrcpy(g_szPIFDir+iPIFName, g_szDefaultPIF); ppl->hPIF = OpenPIFFile(g_szPIFDir, &ppl->ofPIF);
if (ppl->ofPIF.nErrCode == ERROR_FILE_NOT_FOUND) { // try PATH
ppl->hPIF = OpenPIFFile(g_szDefaultPIF, &ppl->ofPIF); }
} else { // Look in cached path
// We've already found it once, so just open it
ppl->hPIF = OpenPIFFile(g_szDefaultPIF, &ppl->ofPIF); } }
if (ppl->hPIF != INVALID_HANDLE_VALUE) {
ppl->flProp |= PROP_DEFAULTPIF;
// Save the fully-qualified pathname of the default PIF file,
// so that subsequent OpenFile() calls will be faster (note that
// we don't specify OF_SEARCH on that particular call)
lstrcpy(g_szDefaultPIF, ppl->ofPIF.szPathName); } else {
// Not only could we not open any sort of PIF, we also need to
// tell GetPIFData to not bother trying to open the file itself
// (since it is unlikely someone created one in this short time)
ppl->flProp |= PROP_NOPIF | PROP_SKIPPIF;
if (ppl->ofPIF.nErrCode == ERROR_FILE_NOT_FOUND) g_szDefaultPIF[0] = 0; // Invalidate cache.
}
// NOW we can set ppl->ofPIF.szPathName to the filename we REALLY
// wanted, since we're done with all the calls to OpenPIFFile.
lstrcpy(ppl->ofPIF.szPathName, pszFullName); }
// Initialize the properties by PIF if we have one, by hand if not
IgnorePIF:
// We don't need to check the return code from GetPIFData() here,
// because we validate hPIFData below anyway. Please also note that
// this GetPIFData call uses the handle we supplied (if any), and closes
// it for us when it's done. Furthermore, if we didn't supply a handle,
// then we should have set PROP_SKIPPIF, so that GetPIFData won't try to
// open anything (since we just tried!)
GetPIFData(ppl, FALSE);
// Now that the original file from which we obtained settings (if any) is
// closed, we need to see if the caller wants us to create a new PIF file
// using a specific name. If so, force it to be created now.
if (lpszPIF) { lstrcpy(ppl->ofPIF.szPathName, lpszPIF); ppl->flProp |= PROP_DIRTY; ppl->flProp &= ~PROP_NOCREATEPIF; fError = !FlushPIFData(ppl, FALSE); }
// Apply INF data to the PIF data we just retrieved, as appropriate,
// as long as it's an app file and not a PIF file (and if, in the case of
// creating a specific PIF, we were actually able to create one).
if (!fError && !fExplicitPIF && (hInf != -1)) {
if (PifMgr_GetProperties(ppl, MAKELP(0,GROUP_PRG), &prg, sizeof(prg), GETPROPS_NONE)) {
// In the PRGINIT_AMBIGUOUSPIF case, GetAppsInfData must
// again look for a matching entry; however, if the entry it
// finds is the same as what we've already got (based on Other
// File), then it will leave the PIF data alone (ie, it doesn't
// reinitialize it, it doesn't call AppWiz to silently
// reconfigure it, etc).
if (fSearchInf || (prg.flPrgInit & PRGINIT_AMBIGUOUSPIF)) {
if (PifMgr_GetProperties(ppl, MAKELP(0,GROUP_NT40), &nt40, sizeof(nt40), GETPROPS_NONE)) {
if (!GetAppsInfData(ppl, &prg, &nt40, (HINF)IntToPtr( hInf ), lpszApp, fFixedDisk, flOpt)) {
// When GetAppsInfData fails and the PIF is ambiguous, then
// we need to restart the PIF search process at the point where
// it searches for _DEFAULT.PIF, so that the ambiguous PIF is
// effectively ignored now.
// Also, we avoid the ugly possibility of getting to this
// point again and infinitely jumping back FindDefault, by
// only jumping if fSearchInf was FALSE. FindDefault sets
// it to TRUE.
if (!fSearchInf && (prg.flPrgInit & PRGINIT_AMBIGUOUSPIF)) { goto FindDefault; } #ifdef DBCS
if (GetSystemDefaultLangID() == 0x0411) { ZeroMemory(&env, sizeof(env)); lstrcpyA(env.achBatchFile, ImeBatchFile); PifMgr_SetProperties(ppl, MAKELP(0,GROUP_ENV), &env, sizeof(env), SETPROPS_NONE); } #endif
} } } } }
Error: LocalFree(pszFullName);
if (fError || !ppl->lpPIFData) { PifMgr_CloseProperties(ppl, 0); return 0; }
// We should never leave PIFMGR with outstanding locks
ASSERTTRUE(!ppl->cLocks);
return ppl; }
/** PifMgr_GetProperties - get property info by name
* * INPUT * hProps = handle to properties * lpszGroup -> property group; may be one of the following: * "WINDOWS 286 3.0" * "WINDOWS 386 3.0" * "WINDOWS VMM 4.0" * "WINDOWS NT 3.1" * "WINDOWS NT 4.0" * or any other group name that is the name of a valid PIF extension; * if NULL, then cbProps is a 0-based index of a named group, and lpProps * must point to a 16-byte buffer to receive the name of the group (this * enables the caller to enumerate the names of all the named groups) * lpProps -> property group record to receive the data * cbProps = size of property group record to get; if cbProps is zero * and a named group is requested, lpProps is ignored, no data is copied, * and the size of the group record is returned (this enables the caller * to determine the size of a named group) * flOpt = GETPROPS_RAWIO to perform raw file read (lpszGroup ignored) * * Alternatively, if the high word (selector) of lpszGroup is 0, the low * word must be a group ordinal (eg, GROUP_PRG, GROUP_TSK, etc) * * OUTPUT * If the group is not found, or an error occurs, 0 is returned. * Otherwise, the size of the group info transferred in bytes is returned. */
int WINAPI PifMgr_GetProperties(HANDLE hProps, LPCSTR lpszGroup, void *lpProps, int cbProps, UINT flOpt) { int cb, i; void *lp; LPW386PIF30 lp386; LPWENHPIF40 lpenh; LPWNTPIF40 lpnt40; LPWNTPIF31 lpnt31;
PPROPLINK ppl; FunctionName(PifMgr_GetProperties);
cb = 0;
if (!(ppl = ValidPropHandle(hProps))) return cb;
// We should never enter PIFMGR with outstanding locks (we also call
// here from *inside* PIFMGR, but none of those cases should require a
// lock either)
ASSERTTRUE(!ppl->cLocks);
ppl->cLocks++;
if (flOpt & GETPROPS_RAWIO) { if (ppl->flProp & PROP_RAWIO) { cb = min(ppl->cbPIFData, cbProps); hmemcpy(lpProps, ppl->lpPIFData, cb); } ppl->cLocks--; return cb; }
if (!lpszGroup) { if (lpProps) { lp = GetGroupData(ppl, NULL, &cbProps, NULL); if (lp) { cb = cbProps; hmemcpy(lpProps, lp, PIFEXTSIGSIZE); } } } else if (IS_INTRESOURCE(lpszGroup) && lpProps) {
// Special case: if GROUP_ICON, then do a nested call to
// PifMgr_GetProperties to get GROUP_PRG data, then feed it to load
// LoadPIFIcon, and finally return the hIcon, if any, to the user.
if (LOWORD((DWORD_PTR) lpszGroup) == GROUP_ICON) { PPROPPRG pprg; PPROPNT40 pnt40 = (void *)LocalAlloc(LPTR, sizeof(PROPNT40)); if ( pnt40 ) { pprg = (void *)LocalAlloc(LPTR, sizeof(PROPPRG)); if (pprg) { if ( PifMgr_GetProperties(ppl, MAKELP(0,GROUP_PRG), pprg, sizeof(PROPPRG), GETPROPS_NONE) && PifMgr_GetProperties(ppl, MAKELP(0,GROUP_NT40), pnt40, sizeof(PROPNT40), GETPROPS_NONE) ) { *(HICON *)lpProps = LoadPIFIcon(pprg, pnt40); cb = 2; } EVAL(LocalFree(pprg) == NULL); } EVAL(LocalFree(pnt40) == NULL); } } else { lp386 = GetGroupData(ppl, szW386HDRSIG30, NULL, NULL); lpenh = GetGroupData(ppl, szWENHHDRSIG40, NULL, NULL); lpnt40 = GetGroupData(ppl, szWNTHDRSIG40, NULL, NULL); lpnt31 = GetGroupData(ppl, szWNTHDRSIG31, NULL, NULL);
//
// Fix anything from down-level PIF files. Since this
// is the first revision of the WENHPIF40 format, we
// don't have anything to worry about (yet).
//
// Don't muck with PIF files from the future!
//
if (lpenh && lpenh->wInternalRevision != WENHPIF40_VERSION) { lpenh->wInternalRevision = WENHPIF40_VERSION; ppl->flProp |= PROP_DIRTY;
//
// Old (pre-M7) PIFs did not zero-initialize the reserved
// fields of PIF files, so zero them out now.
//
lpenh->tskProp.wReserved1 = 0; lpenh->tskProp.wReserved2 = 0; lpenh->tskProp.wReserved3 = 0; lpenh->tskProp.wReserved4 = 0; lpenh->vidProp.wReserved1 = 0; lpenh->vidProp.wReserved2 = 0; lpenh->vidProp.wReserved3 = 0; lpenh->envProp.wMaxDPMI = 0;
// Turn off bits that have been deleted during the development
// cycle.
lpenh->envProp.flEnv = 0; lpenh->envProp.flEnvInit = 0; if (lp386) lp386->PfW386Flags &= ~0x00400000; } // End of "Remove this after M8"
// Zero the input buffer first, so that the Get* functions
// need not initialize every byte to obtain consistent results
BZero(lpProps, cbProps);
// The GetData functions CANNOT rely on either lp386 or lpenh
i = LOWORD((DWORD_PTR) lpszGroup)-1; if (i >= 0 && i < ARRAYSIZE(afnGetData) && cbProps >= acbData[i]) { void *aDataPtrs[NUM_DATA_PTRS];
aDataPtrs[ LP386_INDEX ] = (LPVOID)lp386; aDataPtrs[ LPENH_INDEX ] = (LPVOID)lpenh; aDataPtrs[ LPNT40_INDEX ] = (LPVOID)lpnt40; aDataPtrs[ LPNT31_INDEX ] = (LPVOID)lpnt31;
cb = (afnGetData[i])(ppl, aDataPtrs, lpProps, cbProps, flOpt ); } } } else if (NULL != (lp = GetGroupData(ppl, lpszGroup, &cb, NULL))) { if (lpProps && cbProps != 0) { cb = min(cb, cbProps); hmemcpy(lpProps, lp, cb); } } ppl->cLocks--;
#ifdef EXTENDED_DATA_SUPPORT
// Note that for GETPROPS_EXTENDED, both the normal and extended
// sections are returned, and that the return code reflects the success
// or failure of reading the normal portion only. We return both because
// that's the most convenient thing to do for the caller.
if (flOpt & GETPROPS_EXTENDED) { if (ppl->hVM) { WORD wGroup = EXT_GROUP_QUERY; if (!HIWORD(lpszGroup) && LOWORD(lpszGroup) <= MAX_GROUP) wGroup |= LOWORD(lpszGroup); GetSetExtendedData(ppl->hVM, wGroup, lpszGroup, lpProps); } } #endif
// We should never leave PIFMGR with outstanding locks (we also call
// here from *inside* PIFMGR, but none of those cases should require a
// lock either)
ASSERTTRUE(!ppl->cLocks);
return cb; }
/** PifMgr_SetProperties - set property info by name
* * INPUT * hProps = handle to properties * lpszGroup -> property group; may be one of the following: * "WINDOWS 286 3.0" * "WINDOWS 386 3.0" * "WINDOWS PIF.400" * or any other group name that is the name of a valid PIF extension * lpProps -> property group record to copy the data from * cbProps = size of property group record to set; if cbProps is * zero and lpszGroup is a group name, the group will be removed * flOpt = SETPROPS_RAWIO to perform raw file write (lpszGroup ignored) * SETPROPS_CACHE to cache changes until properties are closed * * Alternatively, if the high word (selector) of lpszGroup is 0, the low * word must be a group ordinal (eg, GROUP_PRG, GROUP_TSK, etc) * * OUTPUT * If the group is not found, or an error occurs, 0 is returned. * Otherwise, the size of the group info transferred in bytes is returned. */
int WINAPI PifMgr_SetProperties(HANDLE hProps, LPCSTR lpszGroup, void *lpProps, int cbProps, UINT flOpt) { void *p = NULL; void *lp = NULL; LPW386PIF30 lp386; LPWENHPIF40 lpenh; LPWNTPIF40 lpnt40; LPWNTPIF31 lpnt31; int i, cb = 0; PPROPLINK ppl;
FunctionName(PifMgr_SetProperties);
// Can't set a NULL name (nor set-by-index)--causes squirlly behavior in RemoveGroupData
if (!lpProps || !lpszGroup) return 0;
ppl = ValidPropHandle(hProps); if (!ppl) return 0;
// We should never enter PIFMGR with outstanding locks (we also call
// here from *inside* PIFMGR, but none of those cases should require a
// lock either)
ASSERTTRUE(!ppl->cLocks);
if (flOpt & SETPROPS_RAWIO) { if (ppl->flProp & PROP_RAWIO) { ppl->cLocks++; cb = min(ppl->cbPIFData, cbProps); if (IsBufferDifferent(ppl->lpPIFData, lpProps, cb)) { hmemcpy(ppl->lpPIFData, lpProps, cb); ppl->flProp |= PROP_DIRTY; } if (cb < ppl->cbPIFData) ppl->flProp |= PROP_DIRTY | PROP_TRUNCATE; ppl->cbPIFData = cb; ppl->cLocks--; } return cb; }
#ifdef EXTENDED_DATA_SUPPORT
// Note that, unlike GETPROPS_EXTENDED, SETPROPS_EXTENDED only updates
// the extended section, and that the return code reflects the existence
// of a VM only. This is because there's a performance hit associated
// with setting the normal portion, and because the caller generally only
// wants to set one or the other.
if (flOpt & SETPROPS_EXTENDED) { if (ppl->hVM) { WORD wGroup = EXT_GROUP_UPDATE; cb = cbProps; if (!HIWORD(lpszGroup) && LOWORD(lpszGroup) <= MAX_GROUP) wGroup |= LOWORD(lpszGroup); GetSetExtendedData(ppl->hVM, wGroup, lpszGroup, lpProps); } return cb; } #endif
// For named groups, if the group does NOT exist, or DOES but is
// a different size, then we have to remove the old data, if any, and
// then add the new.
if (!IS_INTRESOURCE(lpszGroup)) {
cb = PifMgr_GetProperties(hProps, lpszGroup, NULL, 0, GETPROPS_NONE);
if (cb == 0 || cb != cbProps) { if (cb) { RemoveGroupData(ppl, lpszGroup); cb = 0; } if (cbProps) { if (AddGroupData(ppl, lpszGroup, lpProps, cbProps)) cb = cbProps; } goto done; } }
if (cbProps) { if (!lpszGroup) return cb;
p = (void *)LocalAlloc(LPTR, cbProps); if (!p) return cb; }
cb = PifMgr_GetProperties(hProps, lpszGroup, p, cbProps, GETPROPS_NONE);
// If the group to set DOES exist, and if the data given is
// different, copy into the appropriate group(s) in the PIF data
if (cb != 0) { cbProps = min(cb, cbProps); if (IsBufferDifferent(p, lpProps, cbProps)) { cb = 0; ppl->cLocks++; i = LOWORD((DWORD_PTR) lpszGroup)-1; if (!IS_INTRESOURCE(lpszGroup)) { lp = GetGroupData(ppl, lpszGroup, NULL, NULL); if (lp) { cb = cbProps; hmemcpy(lp, lpProps, cbProps); ppl->flProp |= PROP_DIRTY; } } else if (i >= 0 && i < ARRAYSIZE(afnSetData) && cbProps >= acbData[i]) {
// Insure that both 386 and enhanced sections of PIF
// file are present. There are some exceptions: all
// groups from GROUP_MSE on up do not use the 386 section,
// and GROUP_MEM does not need the enh section....
lp386 = GetGroupData(ppl, szW386HDRSIG30, NULL, NULL); if (i < GROUP_MSE-1 && !lp386) { if (AddGroupData(ppl, szW386HDRSIG30, NULL, sizeof(W386PIF30))) { lp386 = GetGroupData(ppl, szW386HDRSIG30, NULL, NULL); if (!lp386) { ASSERTFAIL(); cbProps = 0; // indicate error
} } } if (cbProps) { lpenh = GetGroupData(ppl, szWENHHDRSIG40, NULL, NULL); if (i != GROUP_MEM-1 && !lpenh) { if (!(lpenh = AddEnhancedData(ppl, lp386))) { ASSERTFAIL(); cbProps = 0; // indicate error
} } lpnt40 = GetGroupData(ppl, szWNTHDRSIG40, NULL, NULL); if (!lpnt40) { if (AddGroupData(ppl, szWNTHDRSIG40, NULL, sizeof(WNTPIF40))) { lpnt40 = GetGroupData(ppl, szWNTHDRSIG40, NULL, NULL); } } ASSERT(lpnt40);
lpnt31 = GetGroupData(ppl, szWNTHDRSIG31, NULL, NULL); if (!lpnt31) { if (AddGroupData(ppl, szWNTHDRSIG31, NULL, sizeof(WNTPIF31))) { if (NULL != (lpnt31 = GetGroupData(ppl, szWNTHDRSIG31, NULL, NULL))) { lstrcpyA( lpnt31->nt31Prop.achConfigFile, NT_CONFIG_FILE ); lstrcpyA( lpnt31->nt31Prop.achAutoexecFile, NT_AUTOEXEC_FILE ); } } } ASSERT(lpnt31); } if (cbProps) {
void *aDataPtrs[NUM_DATA_PTRS];
//
// We need to re-establish the pointers because any of
// the AddGroupData's could have moved the block (via
// a HeapReAlloc call), so do that now...
//
lp386 = GetGroupData( ppl, szW386HDRSIG30, NULL, NULL ); lpenh = GetGroupData( ppl, szWENHHDRSIG40, NULL, NULL ); lpnt40 = GetGroupData( ppl, szWNTHDRSIG40, NULL, NULL ); lpnt31 = GetGroupData( ppl, szWNTHDRSIG31, NULL, NULL );
aDataPtrs[ LP386_INDEX ] = (LPVOID)lp386; aDataPtrs[ LPENH_INDEX ] = (LPVOID)lpenh; aDataPtrs[ LPNT40_INDEX ] = (LPVOID)lpnt40; aDataPtrs[ LPNT31_INDEX ] = (LPVOID)lpnt31; cb = (afnSetData[i])(ppl, aDataPtrs, lpProps, cbProps, flOpt ); } } ppl->cLocks--; } } EVAL(LocalFree(p) == NULL);
done: if (!(flOpt & SETPROPS_CACHE)) if (!FlushPIFData(ppl, FALSE)) cb = 0;
// We should never leave PIFMGR with outstanding locks (we also call
// here from *inside* PIFMGR, but none of those cases should require a
// lock either)
ASSERTTRUE(!ppl->cLocks);
return cb; }
/** FlushProperties - flush (or discard) any cached property info
* * INPUT * hProps = handle to properties * flOpt = FLUSHPROPS_DISCARD to abandon cached PIF data, otherwise flush it * * OUTPUT * TRUE if successful, FALSE otherwise */
int WINAPI FlushProperties(HANDLE hProps, UINT flOpt) { PPROPLINK ppl; FunctionName(FlushProperties);
if (!(ppl = ValidPropHandle(hProps))) return FALSE;
return FlushPIFData(ppl, (flOpt & FLUSHPROPS_DISCARD)); }
/** EnumProperties - enumerate open properties
* * INPUT * hProps = handle to previous properties (NULL to start) * * OUTPUT * next property handle, 0 if none */
HANDLE WINAPI EnumProperties(HANDLE hProps) { PPROPLINK ppl; FunctionName(EnumProperties);
if (!hProps) return g_pplHead;
if (!(ppl = ValidPropHandle(hProps))) return NULL;
return ppl->pplNext; }
/** AssociateProperties - associate data with property info
* * INPUT * hProps = handle to properties * iAssociate = association index (eg, HVM_ASSOCIATION) * lData = new associated data * * OUTPUT * previously associated data for the index, 0 if none (or error) * * NOTES * If iAssociate is a negative association index, then the current * associated value is returned and not modified (ie, lData is ignored) */
LONG_PTR WINAPI AssociateProperties(HANDLE hProps, int iAssociate, LONG_PTR lData) { LONG_PTR l; int iIndex; PPROPLINK ppl; FunctionName(AssociateProperties);
if (!(ppl = ValidPropHandle(hProps))) return FALSE;
iIndex = iAssociate; if (iIndex < 0) iIndex *= -1;
switch(iIndex) { case HVM_ASSOCIATION: l = ppl->hVM; break; case HWND_ASSOCIATION: l = (LONG_PTR)ppl->hwndTty; break; case LPARGS_ASSOCIATION: l = (LONG_PTR)ppl->lpArgs; break; default: return FALSE; }
switch(iAssociate) { case HVM_ASSOCIATION: ppl->hVM = (DWORD) lData; break; case HWND_ASSOCIATION: ppl->hwndTty = (HWND)lData; break; case LPARGS_ASSOCIATION: ppl->lpArgs = (LPTSTR)lData; break; } return l; }
void SetBootDrive(LPTSTR pszFile) { TCHAR szPath[10]; DWORD cbPath = sizeof(szPath);
szPath[0] = 0; SHGetValue(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Setup"), TEXT("BootDir"), NULL, szPath, &cbPath);
pszFile[0] = szPath[0] ? szPath[0] : TEXT('C'); }
#define RENAME_DELETENEW 0x0001
#define RENAME_CREATEOLD 0x0002
#define RENAME_DELETEOLD 0x0004
/** RenameStartupFiles - rename startup files (for real-mode only)
* * INPUT * pszOld -> old filename (eg, C:CONFIG.SYS) * pszNew -> new filename (eg, C:CONFIG.WOS) * flRename == see RENAME_* above * * OUTPUT * None */
void RenameStartupFiles(LPTSTR pszOld, LPTSTR pszNew, int flRename) { SetBootDrive(pszOld); SetBootDrive(pszNew);
// For RENAME_DELETENEW: make sure there's no way an existing new filename
// could block the rename process, not even a R/H/S file!
if (flRename & RENAME_DELETENEW) { SetFileAttributes(pszNew, 0); DeleteFile(pszNew); }
// For RENAME_CREATEOLD: if the old filename doesn't exist, then we
// want to create a hidden 0-length copy of the new filename, as a way
// of telling ourselves later that the old file didn't exist. Then,
// when it's later (see RENAME_DELETEOLD), if the file we're to rename
// is a hidden 0-length file, just delete it.
if (flRename & RENAME_CREATEOLD) {
int i; HANDLE hFile;
i = (int)GetFileAttributes(pszOld); if (i == -1) { hFile = CreateFile( pszNew, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL ); if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); return; } }
// For RENAME_DELETEOLD: if the old file is a hidden 0-length
// dummy file that we presumably created ourselves, then simply delete it.
if (flRename & RENAME_DELETEOLD) {
WIN32_FIND_DATA dFind; HANDLE hFind = FindFirstFile(pszOld, &dFind); if (hFind != INVALID_HFINDFILE) { FindClose(hFind); if ((dFind.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) && dFind.nFileSizeLow == 0) { DeleteFile(pszOld); return; } } } MoveFile(pszOld, pszNew); }
/** CheckForceReboot - return TRUE if exit to real-mode has been disabled
* * This function largely copied from win\core\user\inexit.c -JTP * * The key being examined is: * * HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\Shutdown * * The value-name is: * * ForceReboot * * The value to force a reboot is: * * "1" * * INPUT * None * * OUTPUT * TRUE if reboot must be forced, FALSE if not */
BOOL CheckForceReboot() { // Check to see if we have to force a reboot of the system. One reason is
// so that the double space utilities can tell us the system must be reboot
// since we can't exit to real mode double space...
HKEY hkey; static TCHAR szShutdown[] = REGSTR_PATH_SHUTDOWN; BOOL fForce=FALSE; TCHAR sz[12]; DWORD dwType;
if (!RegOpenKey(HKEY_LOCAL_MACHINE, szShutdown, &hkey)) {
LONG cb = sizeof(sz); static TCHAR szForceReboot[] = REGSTR_VAL_FORCEREBOOT;
if (!SHQueryValueEx(hkey, szForceReboot, NULL, &dwType, (LPBYTE)sz, &cb)) { if (*sz==TEXT('1')) fForce = TRUE; }
RegCloseKey(hkey); }
return(fForce); }
/** CreateStartupProperties - create startup files (for real-mode only)
* * INPUT * hProps = handle to properties * flOpt = CREATEPROPS_NONE (all bits reserved) * * OUTPUT * TRUE (1) if any files were created, FALSE (0) if there was an error * creating 1 or more of the necessary files, and -1 there was no error and * no files needed to be created. */
int WINAPI CreateStartupProperties(HANDLE hProps, UINT flOpt) { BOOL fForceReboot; int fSuccess = FALSE; PPROPLINK ppl; FunctionName(CreateStartupProperties);
if (NULL != (ppl = ValidPropHandle(hProps))) {
InitRealModeFlag(ppl);
if (ppl->flProp & PROP_REALMODE) {
// WriteAdvPrgData only returns the exact value TRUE (1) if it
// actually created a file. If there was no need to create
// a particular file, and therefore no error, it returns -1.
fForceReboot = CheckForceReboot(); fSuccess = WriteAdvPrgData(ppl, MAX_CONFIG_SIZE, szCONFIGHDRSIG40, g_szMConfigFile, g_szConfigFile, fForceReboot, fForceReboot); fSuccess &= WriteAdvPrgData(ppl, MAX_AUTOEXEC_SIZE, szAUTOEXECHDRSIG40, g_szMAutoexecFile, g_szAutoexecFile, fSuccess, fForceReboot);
} } return fSuccess; }
/** DoLargeEnvironmentSubstA - do large environment substitution
* * NOTE: the below comment is no longer true. DoEnvironmentSubst * calls ExpandEnvironmentStrings which does not have this restriction! * * This is a wrapper around shell's DoEnvironmentSubst that can handle * larger buffers. Although not well-documented, DoEnvironmentSubst will * fail if the input buffer simply contains more than 256 chars, hence * it is not entirely suitable for our purposes. * * The basic idea is: find CR/LF-terminated lines, copy them to szTmp * with a null terminator instead of the CR/LF, do environment substitution * on szTmp, move the data in psz up by the number of bytes szTmp grew (if * any), and then copy the expanded string back into psz. Repeat until no * more lines. */
int DoLargeEnvironmentSubstA(LPSTR psz, int cchsz, BOOL fStripWIN) { int cch, cchNew, cchDiff; DWORD dwResult; CHAR szTmp[256]; CHAR *pszCRLF, *pszCRLFNext;
pszCRLF = psz; while (NULL != (pszCRLFNext = (PBYTE)(UINT_PTR)(ULONG_PTR)_fstrstr(pszCRLF, szCRLF))) {
cch = (int)(pszCRLFNext - pszCRLF); pszCRLFNext += ARRAYSIZE(szCRLF)-1;
if (cch >= ARRAYSIZE(szTmp)) goto Next;
hmemcpy(szTmp, pszCRLF, cch); szTmp[cch] = '\0';
// fStripWIN means: strip WIN wherever you find it and do NOTHING else
if (fStripWIN) { if (lstrcmpiA(szTmp, szWin) == 0) { cchNew = 0; goto Strip; } goto Next; }
cchNew = cch; dwResult = DoEnvironmentSubstA(szTmp, ARRAYSIZE(szTmp)); if (HIWORD(dwResult)) cchNew = LOWORD(dwResult);
Strip: cchDiff = cchNew - cch; // cchDiff == # chars needed
if (cchDiff) {
if (cchDiff > cchsz - (lstrlenA(psz)+1)) goto Next; // not enough room left
cch = lstrlenA(pszCRLFNext)+1;// cch == # chars required to copy
hmemcpy(pszCRLFNext+(cchDiff*sizeof(CHAR)), pszCRLFNext, cch*sizeof(CHAR)); pszCRLFNext += cchDiff; }
hmemcpy(pszCRLF, szTmp, cchNew*sizeof(CHAR)); hmemcpy(pszCRLF+cchNew, szCRLF, (ARRAYSIZE(szCRLF)-1)*sizeof(CHAR));
Next: pszCRLF = pszCRLFNext; } return lstrlenA(psz); }
void ReadAdvPrgData(PPROPLINK ppl, int cbMax, LPCSTR lpszName, LPTSTR pszFile, UINT flOpt) { HANDLE hFile; int i; DWORD cbData = 0, cb; CHAR szTmp[256]; PBYTE pb, pbLast; PBYTE pbData = NULL, pbDataTmp, pbDataOrig = NULL;
SetBootDrive(pszFile);
if (flOpt & DELETEPROPS_DISCARD) { DeleteFile(pszFile); return; }
hFile = CreateFile( pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if (hFile != INVALID_HANDLE_VALUE) {
pbData = (PBYTE)LocalAlloc(LPTR, cbMax);
if (pbData) {
pbDataOrig = pbData;
if (ReadFile( hFile, (PVOID)pbData, (DWORD)cbMax-1, &cbData, NULL)) {
// Guess what, this routine isn't so general purpose after all:
// in the autoexec case, we must find the signature that precedes
// the command-line that we added to the file. For expediency,
// we simply delete it and everything after it.
if (pszFile == g_szMConfigFile) {
// If "DOS=SINGLE" is present, skip over it
if (pbData == (PBYTE)(UINT_PTR)(ULONG_PTR)_fstrstr(pbData, szSingle)) { cb = lstrlenA(szSingle); pbData += cb; cbData -= cb; } }
if (pszFile == g_szMAutoexecFile) {
// If "@echo off" is present, skip over it
if (LoadStringA(g_hinst, IDS_AUTOEXECTOP, szTmp, ARRAYSIZE(szTmp))) {
if (pbData == (PBYTE)(UINT_PTR)(ULONG_PTR)_fstrstr(pbData, szTmp)) { cb = lstrlenA(szTmp); pbData += cb; cbData -= cb; } }
// Try to find our signature at the end of the file now
if (LoadStringA(g_hinst, IDS_AUTOEXECBOTTOM, szTmp, ARRAYSIZE(szTmp))) {
pb = pbData; pbLast = NULL; while (NULL != (pb = (PBYTE)(UINT_PTR)(ULONG_PTR)_fstrstr(pb, szTmp))) { pbLast = pb++; }
// If there was a (last) occurrence of the signature
// in the file, then it's starting point is where we cut
// off the data we're about to rewrite.
if (pbLast) cbData = (DWORD) (pbLast - pbData); } } } } CloseHandle(hFile);
DeleteFile(pszFile); }
// Before we simply blast the data back out to the PIF, we check to
// see if the data we're about to write is the same as what's already
// there after environment string substitution. If so, we leave the
// data alone, so that environment strings are left in their pre-expanded
// form whenever possible (and with a minimum of work on our part).
i = -1; if (NULL != (pbDataTmp = (PBYTE)LocalAlloc(LPTR, cbMax))) { if (PifMgr_GetProperties(ppl, lpszName, pbDataTmp, cbMax-1, GETPROPS_NONE)) { DoLargeEnvironmentSubstA(pbDataTmp, cbMax, FALSE); i = lstrcmpA(pbDataTmp, pbData); } EVAL(LocalFree(pbDataTmp) == NULL); }
// Note that if the file no longer exists, or does but is zero bytes
// long, this call effectively removes the data from the PIF. Otherwise,
// the PIF gets refreshed with the contents of the startup file, which
// we deleted immediately after pulling its contents out.
if (i != 0) PifMgr_SetProperties(ppl, lpszName, pbData, cbData, SETPROPS_NONE);
if (pbDataOrig) EVAL(LocalFree(pbDataOrig) == NULL); }
BOOL WriteAdvPrgData(PPROPLINK ppl, int cbMax, LPCSTR lpszName, LPTSTR pszFile, LPTSTR pszOrigFile, BOOL fCreateAnyway, BOOL fForceReboot) { HANDLE hFile; int cbData, cb, i, j; PBYTE pbData; PROPPRG prg; PROPENV env; DWORD dwBytes; BOOL fPause = FALSE; BOOL fSuccess = FALSE; CHAR szTmp[MAXPATHNAME];
cbData = PifMgr_GetProperties(ppl, lpszName, NULL, 0, GETPROPS_NONE);
if (!cbData) { // if there's no autoexec data
if (fCreateAnyway != 1) // and no previous file was created
return -1; // then there's no need to create this file
}
// If fCreateAnyway is -1, then we know are being called for AUTOEXEC
// *and* that no file was created for CONFIG. Since we are committed to
// creating an AUTOEXEC now, we therefore *force* the CONFIG file to
// be created as well. That way, the user won't end up with the default
// CONFIG (he may have just not have needed/cared about device drivers, but
// still wanted to run in real-mode).
if (fCreateAnyway == -1) { if (!WriteAdvPrgData(ppl, MAX_CONFIG_SIZE, szCONFIGHDRSIG40, g_szMConfigFile, g_szConfigFile, TRUE, fForceReboot)) return FALSE; }
pbData = (PBYTE)LocalAlloc(LPTR, cbMax); if (pbData) { if (cbData) { if (cbData >= cbMax) cbData = cbMax-1; PifMgr_GetProperties(ppl, lpszName, pbData, cbData, GETPROPS_NONE);
} else if (fForceReboot) {
SetBootDrive(pszOrigFile);
hFile = CreateFile( pszOrigFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if (hFile != INVALID_HANDLE_VALUE) {
cbData = (int)SetFilePointer( hFile, 0, NULL, FILE_END ); if (cbData) { if (cbData >= cbMax) cbData = cbMax-1; SetFilePointer( hFile, 0, NULL, FILE_BEGIN ); if (ReadFile( hFile, pbData, cbData, &dwBytes, NULL)) { if (dwBytes != (UINT)cbData) { cbData = 0; } else { cbData = (int)dwBytes; }
} }
CloseHandle(hFile); } } } else cbData = 0;
SetBootDrive( pszFile );
hFile = CreateFile( pszFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if (hFile!=INVALID_HANDLE_VALUE) {
// Do environment string substitution on the buffer before writing
// it out. Treat it like the huge null-terminated string that it is.
if (pbData) cbData = DoLargeEnvironmentSubstA(pbData, cbMax, fForceReboot);
// Do the simple thing: ALWAYS write out the "@echo off" line;
// if someone really wants to see the batch file execute, let them
// put an explicit "echo on" in their autoexec edit control; since it
// comes after ours, it will override as appropriate.
//
// This rule also simplifies conversion on the way back: if the
// first line is "@echo off", we delete it.
if (pszFile == g_szMConfigFile) { WriteFile( hFile, szSingle, lstrlenA(szSingle), &dwBytes, NULL ); }
if (pszFile == g_szMAutoexecFile) { szTmp[0] = '\0'; LoadStringA(g_hinst, IDS_AUTOEXECTOP, szTmp, ARRAYSIZE(szTmp)); WriteFile( hFile, szTmp, lstrlenA(szTmp), &dwBytes, NULL ); }
if (WriteFile(hFile, pbData, cbData, &dwBytes, NULL) && (dwBytes==(DWORD)cbData)) fSuccess++; // return TRUE to indicate file created
// Make sure the user-defined gob-o-goo is CR/LF-terminated;
// if not, spit one out.
if (cbData != 0) if (cbData < 2 || lstrcmpA(pbData+cbData-(ARRAYSIZE(szCRLF)-1), szCRLF) != 0) WriteFile( hFile, szCRLF, sizeof(szCRLF)-1, &dwBytes, NULL);
// Guess what, this routine isn't so general purpose after all:
// in the autoexec case, we must append the command-line to
// the file, followed by C:\WINDOWS\WIN.COM /W to restore everything
if (pszFile == g_szMAutoexecFile) {
if (PifMgr_GetProperties(ppl, MAKELP(0,GROUP_PRG), &prg, sizeof(prg), GETPROPS_NONE)) {
szTmp[0] = '\0'; LoadStringA(g_hinst, IDS_AUTOEXECBOTTOM, szTmp, ARRAYSIZE(szTmp)); WriteFile(hFile, szTmp, lstrlenA(szTmp), &dwBytes, NULL);
// If the program is on drive A, then it is time emit:
//
// ECHO Please insert the diskette for Mickey's ABCs in drive A now
// PAUSE
//
// since WinOldAp will have asked the user to remove it.
if ((ppl->szPathName[0] == TEXT('a') || ppl->szPathName[0] == TEXT('A')) && ppl->szPathName[1] == TEXT(':')) {
CHAR szTmpFmt[128];
fPause++; WriteFile(hFile, szEcho, sizeof(szEcho)-1, &dwBytes, NULL);
szTmpFmt[0] = '\0'; LoadStringA(g_hinst, IDS_DISKINSERT, szTmpFmt, ARRAYSIZE(szTmpFmt));
// The DISKINSERT string should have one %s for the title...
wsprintfA(szTmp, szTmpFmt, prg.achTitle);
WriteFile(hFile, szTmp, lstrlenA(szTmp), &dwBytes, NULL); WriteFile(hFile, szPause, sizeof(szPause)-1, &dwBytes, NULL); }
if (PifMgr_GetProperties(ppl, MAKELP(0,GROUP_ENV), &env, sizeof(env), GETPROPS_NONE)) {
if (env.achBatchFile[0]) {
WriteFile(hFile, szCall, sizeof(szCall)-1, &dwBytes, NULL);
// env.achBatchFile could be a long filename and/or
// quoted (since it could be followed by args). Take
// care of it now.
i = lstrunquotefnameA(szTmp, env.achBatchFile, ARRAYSIZE(szTmp), TRUE);
j = lstrskipfnameA(env.achBatchFile);
if (env.achBatchFile[j]) lstrcpynA(szTmp+i, env.achBatchFile+j, ARRAYSIZE(szTmp)-i);
WriteFile(hFile, szTmp, lstrlenA(szTmp), &dwBytes, NULL); WriteFile(hFile, szCRLF, sizeof(szCRLF)-1, &dwBytes, NULL); } } #ifdef DBCS
// else if (!GetProperties(ppl, lpszName, NULL, 0, GETPROPS_NONE))
// _lwrite(hFile, szDOSIME, lstrlen(szDOSIME));
#endif
if (!prg.achWorkDir[0]) {
// If there's no hard-coded working directory,
// create one. It's ok to party on prg.achWorkDir because
// we're not going to call PifMgr_SetProperties and inadvertently
// change it.
InitWorkDir(ppl, &prg, NULL); }
if (prg.achWorkDir[0]) {
cb = lstrunquotefnameA(szTmp, prg.achWorkDir, ARRAYSIZE(szTmp), TRUE);
if (szTmp[1] == ':') { WriteFile(hFile, szTmp, 2, &dwBytes, NULL); WriteFile(hFile, szCRLF, ARRAYSIZE(szCRLF)-1, &dwBytes, NULL); } WriteFile(hFile, szCD, sizeof(szCD)-1, &dwBytes, NULL); WriteFile(hFile, szTmp, cb, &dwBytes, NULL); WriteFile(hFile, szCRLF, sizeof(szCRLF)-1, &dwBytes, NULL); }
// We now always CALL the cmd-line, in case it's a batch file.
// If it isn't, no biggie, because command.com is smart enough
// to ignore it.
WriteFile(hFile, szCall, sizeof(szCall)-1, &dwBytes, NULL);
// If the properties we have are ones *we* created in the
// PIF dir, and it looks like the application name passed to
// PifMgr_OpenProperties is a "qualified" pathname, then we'll use
// that instead of the path stored in the PIF, since the app
// might have moved since *we* created the PIF.
if ((ppl->flProp & PROP_PIFDIR) && (ppl->szPathName[0] == TEXT('\\') || ppl->szPathName[0] && ppl->szPathName[1] == TEXT(':'))) {
// We're making this call not because ppl->szPathName
// is quoted (it shouldn't be), but because it could still
// be a long filename, so translate it.
CHAR achPathName[ ARRAYSIZE(ppl->szPathName) ]; PifMgr_WCtoMBPath( ppl->szPathName, achPathName, ARRAYSIZE(achPathName) ); i = lstrunquotefnameA(szTmp, achPathName, ARRAYSIZE(szTmp), -1); } else {
// As for the thing stored in the PIF, now *that* could
// both long and quoted. Take care of it now.
i = lstrunquotefnameA(szTmp, prg.achCmdLine, ARRAYSIZE(szTmp), TRUE); }
// NOTE: There is an obscure case where the preceding call
// to lstrunquotefname can fail, returning i == 0, even though
// WinOldAp already verified the app exists. That case is when
// the app name is NOT fully-qualified, so it exists somewhere
// on the PATH, but NOT in the current directory, and the app
// name is an LFN. Because lstrunquotefname must call INT 21h,
// function 7160h, subfunction 1 (NAMTRN_DO83QUERY), IFSMGR must
// find the equivalent 8.3 name for the LFN app name, but all
// IFSMGR has to work with is the current directory, and that's
// not where the app is, so the INT 21h fails, returning error
// #3 (ERROR_PATH_NOT_FOUND).
//
// To solve, we could do another PATH search for the app file,
// via OpenFileEx; if found, it would return the fully-qualifed
// long-file-name of the app, in the OEM char set, which we
// could then perform NameTrans on, to get full 8.3. Yuck! -JP
// If there are any arguments in the PIF, find them
// and append them, giving PREFERENCE to any args that WINOLDAP
// specifically associated with this instance of the app.
if (ppl->lpArgs) { // any associated args?
j = lstrlen(ppl->lpArgs); if (j) { szTmp[i++] = ' '; if (ppl->lpArgs[j-1] == TEXT('\r')) j--; { CHAR achArgs[ ARRAYSIZE(ppl->lpArgs) ];
WideCharToMultiByte( CP_ACP, 0, ppl->lpArgs, -1, achArgs, ARRAYSIZE(achArgs), NULL, NULL ); lstrcpynA(szTmp+i, achArgs, min(sizeof(szTmp)-i,(unsigned)j+1)); } } } else { j = lstrskipfnameA(prg.achCmdLine);
if (prg.achCmdLine[j]) lstrcpynA(szTmp+i, prg.achCmdLine+j, ARRAYSIZE(szTmp)-i); }
WriteFile(hFile, szTmp, lstrlenA(szTmp), &dwBytes, NULL); WriteFile(hFile, szCRLF, sizeof(szCRLF)-1, &dwBytes, NULL);
// If we paused above to ask for a disk, we should pause
// again to ask them to remove the disk. We won't actually
// emit a PAUSE though unless close-on-exit is set, since
// WIN.COM should already pause for them when the cursor isn't
// at 0,0.
if (fPause) { WriteFile(hFile, szEcho, sizeof(szEcho)-1, &dwBytes, NULL);
szTmp[0] = TEXT('\0'); LoadStringA(g_hinst, IDS_DISKREMOVE, szTmp, ARRAYSIZE(szTmp));
WriteFile(hFile, szTmp, lstrlenA(szTmp), &dwBytes, NULL);
if (prg.flPrg & PRG_CLOSEONEXIT) WriteFile(hFile, szPause, sizeof(szPause)-1, &dwBytes, NULL); else WriteFile(hFile, szCRLF, sizeof(szCRLF)-1, &dwBytes, NULL); } } } CloseHandle(hFile); } if (pbData) EVAL(LocalFree(pbData) == NULL);
return fSuccess; }
/** PifMgr_CloseProperties - close property info for application
* * INPUT * hProps = handle to properties * flOpt = CLOSEPROPS_DISCARD to abandon cached PIF data, otherwise save it * * OUTPUT * NULL if successful, otherwise hProps is returned as given */
HANDLE WINAPI PifMgr_CloseProperties(HANDLE hProps, UINT flOpt) { PPROPLINK ppl; FunctionName(PifMgr_CloseProperties);
if (!(ppl = ValidPropHandle(hProps))) return hProps;
// When discarding on a close, set the SKIPPIF flag, so that the
// flush code won't say "oh, not only should I throw away my current
// set of data, but I should read in clean data" -- new data is no use
// since the caller is closing.
if (flOpt & CLOSEPROPS_DISCARD) ppl->flProp |= PROP_SKIPPIF;
if (ppl->flProp & PROP_DIRTY) { // this redundant check added
// to avoid making FlushPIFData PRELOAD -JTP
// Note that we avoid calling FlushPIFData if INHIBITPIF is set,
// since FlushPIFData will just return a fake TRUE result anyway.
// But we don't want to be fooled, we want to make sure the block
// gets unlocked now.
if ((ppl->flProp & PROP_INHIBITPIF) || !FlushPIFData(ppl, (flOpt & CLOSEPROPS_DISCARD))) {
// If FlushPIFData failed, then if we still have an outstanding
// dirty lock, force the data to become unlocked, by clearing the
// dirty flag in the middle of a pair otherwise pointless lock/unlock
// calls (because that's the nice, clean way to do it!)
if (ppl->flProp & PROP_DIRTYLOCK) { ppl->cLocks++; ppl->flProp &= ~PROP_DIRTY; ppl->cLocks--; } } }
if (ppl->lpPIFData) { LocalFree(ppl->lpPIFData); ppl->lpPIFData = NULL; }
if (ppl->hPIF != INVALID_HANDLE_VALUE) CloseHandle(ppl->hPIF);
// Unlink from the global list
if (ppl->pplPrev) ppl->pplPrev->pplNext = ppl->pplNext; else g_pplHead = ppl->pplNext;
if (ppl->pplNext) ppl->pplNext->pplPrev = ppl->pplPrev;
LocalFree(ppl); return NULL; }
/** ValidPropHandle - verify handle
* * INPUT * hProps = handle to properties * * OUTPUT * pointer to prop, NULL otherwise */
PPROPLINK ValidPropHandle(HANDLE hProps) { FunctionName(ValidPropHandle); if (!hProps || (HANDLE)hProps > g_offHighestPropLink || ((PPROPLINK)hProps)->iSig != PROP_SIG) { ASSERTFAIL(); return NULL; } return (PPROPLINK)hProps; }
/** ResizePIFData - verify handle and resize PIF data
* * INPUT * ppl -> property * cbResize = bytes to resize PIF data by * * OUTPUT * previous size of PIF data if successful, -1 if not * * on success, the PIF data is returned LOCKED, so successful * ResizePIFData calls should be matched with UnlockPIFData calls. */
int ResizePIFData(PPROPLINK ppl, INT cbResize) { INT cbOld, cbNew; void *lpNew; BOOL fInitStdHdr = FALSE; FunctionName(ResizePIFData);
ASSERTTRUE(cbResize != 0);
// Cope with empty or old PIF files
cbOld = ppl->cbPIFData; cbNew = ppl->cbPIFData + cbResize;
if ((cbNew < cbOld) == (cbResize > 0)) return -1; // underflow/overflow
if (!ppl->lpPIFData && cbOld == 0) { if (cbNew >= sizeof(STDPIF) + sizeof(PIFEXTHDR)) fInitStdHdr = TRUE; lpNew = LocalAlloc(LPTR, cbNew); } else {
if (cbOld == sizeof(STDPIF)) { fInitStdHdr = TRUE; cbOld += sizeof(PIFEXTHDR); cbNew += sizeof(PIFEXTHDR); }
lpNew = LocalReAlloc( ppl->lpPIFData, cbNew, LMEM_MOVEABLE|LMEM_ZEROINIT);
}
if (lpNew) { ppl->cbPIFData = cbNew; ppl->lpPIFData = (LPPIFDATA)lpNew; ppl->cLocks++; if (fInitStdHdr) { lstrcpyA(ppl->lpPIFData->stdpifext.extsig, szSTDHDRSIG); ppl->lpPIFData->stdpifext.extnxthdrfloff = LASTHDRPTR; ppl->lpPIFData->stdpifext.extfileoffset = 0x0000; ppl->lpPIFData->stdpifext.extsizebytes = sizeof(STDPIF); } return cbOld; } return -1; }
/** GetPIFData - read PIF data back from PIF
* * INPUT * ppl -> property * fLocked == TRUE to return data locked, FALSE unlocked * * OUTPUT * TRUE if succeeded, FALSE if not */
BOOL GetPIFData(PPROPLINK ppl, BOOL fLocked) { DWORD dwOff; LPTSTR pszOpen; BOOL fSuccess = FALSE; FunctionName(GetPIFData);
// Since we're going to (re)load the property data now, reset
// the current size, so that ResizePIFData will resize it from zero
ppl->cbPIFData = 0;
// If SKIPPIF is set (eg, by PifMgr_OpenProperties), then don't
// try to open anything (since PifMgr_OpenProperties already tried!),
if (ppl->hPIF == INVALID_HANDLE_VALUE && !(ppl->flProp & PROP_SKIPPIF)) { pszOpen = g_szDefaultPIF; if (!(ppl->flProp & PROP_DEFAULTPIF)) pszOpen = ppl->ofPIF.szPathName; ppl->hPIF = CreateFile( pszOpen, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); } if (ppl->hPIF == INVALID_HANDLE_VALUE) {
// The following warning is disabled because the presence of
// the dialog box got WINOLDAP stuck in an infinite message loop -JTP
InitProperties(ppl, fLocked); goto Exit; } dwOff = SetFilePointer(ppl->hPIF, 0, NULL, FILE_END); if (dwOff >= sizeof(STDPIF)) {
ppl->flProp |= PROP_REGEN;
if (ResizePIFData(ppl, dwOff) != -1) {
SetFilePointer(ppl->hPIF, 0, NULL, FILE_BEGIN); if (ReadFile( ppl->hPIF, ppl->lpPIFData, ppl->cbPIFData, &ppl->cbPIFData, NULL )) {
// Can't be dirty anymore, 'cause we just read the PIF back in
ppl->flProp &= ~PROP_DIRTY;
if (ppl->flProp & PROP_DEFAULTPIF) {
WideCharToMultiByte( CP_ACP, 0, ppl->szPathName+ppl->iFileName, -1, ppl->lpPIFData->stdpifdata.appname, ARRAYSIZE(ppl->lpPIFData->stdpifdata.appname), NULL, NULL );
PifMgr_WCtoMBPath( ppl->szPathName, ppl->lpPIFData->stdpifdata.startfile, ARRAYSIZE(ppl->lpPIFData->stdpifdata.startfile) ); // I don't think this is generally worth dirtying the
// property info for, because otherwise every app that used
// _DEFAULT.PIF initially would get its own PIF file created
// later; PIF file creation should only take place when
// substantive changes have been made
// ppl->flProp |= PROP_DIRTY;
}
// If we're not dealing with an enhanced PIF, then we
// go to the various INI files to retrieve DOS app defaults
if (!GetGroupData(ppl, szWENHHDRSIG40, NULL, NULL)) { GetINIData(); }
// If we're not dealing with a new NT/UNICODE PIF, then
// we add a new section so it's ALWAYS there when we're
// UNICODE enabled.
if (!GetGroupData(ppl, szWNTHDRSIG40, NULL, NULL)) { VERIFYTRUE(AddGroupData(ppl, szWNTHDRSIG40, NULL, sizeof(WNTPIF40))); } // If we're not dealing with a NT PIF, then
// we add the NT sections so it's ALWAYS there when we're
// running on NT.
if (!GetGroupData(ppl, szWNTHDRSIG31, NULL, NULL)) { LPWNTPIF31 lpnt31;
VERIFYTRUE(AddGroupData(ppl, szWNTHDRSIG31, NULL, sizeof(WNTPIF31))); if (NULL != (lpnt31 = GetGroupData(ppl, szWNTHDRSIG31, NULL, NULL))) { lstrcpyA( lpnt31->nt31Prop.achConfigFile, NT_CONFIG_FILE ); lstrcpyA( lpnt31->nt31Prop.achAutoexecFile, NT_AUTOEXEC_FILE ); } }
if (!fLocked) ppl->cLocks--; // UnlockPIFData(ppl);
fSuccess++; } } else ASSERTFAIL();
ppl->flProp &= ~PROP_REGEN; } CloseHandle(ppl->hPIF); ppl->hPIF = INVALID_HANDLE_VALUE;
// As long as IGNOREPIF isn't set, clear SKIPPIF, because even if we
// already knew the PIF didn't exist on *this* call, one may be created
// (by someone else) by the next time we're called
Exit: if (!(ppl->flProp & PROP_IGNOREPIF)) ppl->flProp &= ~PROP_SKIPPIF; return fSuccess; }
/** FlushPIFData - write dirty PIF data back to PIF
* * INPUT * ppl -> property * fDiscard == TRUE to discard dirty data, FALSE to keep it * * OUTPUT * TRUE if succeeded, FALSE if not * * NOTES * We must first check the PROPLINK and see if the DONTWRITE bit has * been set, in which case we have to fail the flush. Once DONTWRITE is * set in a PROPLINK, it will never be cleared, unless the caller * specifies fDiscard == TRUE to reload the data. This is BY DESIGN (ie, * a UI compromise). How does DONTWRITE get set? By someone else * having previously (and successfully) done a flush to the same PIF; at * that point in time, we will look for all other properties that refer to * the same file, and set their DONTWRITE bit. What about PROPLINKs that * are created later? They're ok, they don't get DONTWRITE set until * the above sequence takes place during their lifetime. */
BOOL FlushPIFData(PPROPLINK ppl, BOOL fDiscard) { UINT u; BOOL fSuccess = FALSE; FunctionName(FlushPIFData);
// If nothing dirty, nothing to do
if (!(ppl->flProp & PROP_DIRTY) || (ppl->flProp & PROP_INHIBITPIF)) return TRUE; // ie, success
// If discarding, then clear PROP_DIRTY and reload the data
if (fDiscard) { ppl->flProp &= ~(PROP_DIRTY | PROP_DONTWRITE); return GetPIFData(ppl, FALSE); }
if (ppl->flProp & PROP_DONTWRITE) return fSuccess; // ie, FALSE (error)
if (!ppl->lpPIFData) return fSuccess; // ie, FALSE (error)
ppl->cLocks++;
// If we created properties without opening a file, it may have
// been because normal PIF search processing was overridden by the
// presence of a WIN.INI entry; if that entry is still there,
// then our data is not in sync with any existing file, nor is there
// any point in creating a new file as long as that entry exists. We
// need to consider prompting the user as to whether he really wants
// that WIN.INI entry, so that it's clear what the heck is going on
if (ppl->flProp & PROP_IGNOREPIF) {
HANDLE hProps;
ppl->ckbMem = GetProfileInt(apszAppType[APPTYPE_PIF]+1, ppl->szPathName+ppl->iFileName, -1); if (ppl->ckbMem != -1) goto Exit;
// The WIN.INI entry apparently went away, so let's re-attempt to
// open the properties that we should have obtained in the first
// place. Assuming success, we will copy our entire block on top of
// them (thereby flushing it), and also copy their PIF name to our
// PIF name and their PIF flags to our PIF flags, so that future
// flushes are of the more normal variety
hProps = PifMgr_OpenProperties(ppl->ofPIF.szPathName, NULL, 0, OPENPROPS_RAWIO); if (hProps) { ppl->flProp &= ~(PROP_IGNOREPIF | PROP_SKIPPIF); ppl->flProp |= ((PPROPLINK)hProps)->flProp & (PROP_IGNOREPIF | PROP_SKIPPIF); lstrcpy(ppl->ofPIF.szPathName, ((PPROPLINK)hProps)->ofPIF.szPathName); if (PifMgr_SetProperties(hProps, NULL, ppl->lpPIFData, ppl->cbPIFData, SETPROPS_RAWIO) == ppl->cbPIFData) { fSuccess++; ppl->flProp &= ~(PROP_DIRTY | PROP_TRUNCATE); } PifMgr_CloseProperties(hProps, CLOSEPROPS_NONE); } goto Exit; }
// Disable annoying critical error popups (NO MORE GOTOS PAST HERE PLEASE)
u = SetErrorMode(SEM_FAILCRITICALERRORS);
ppl->hPIF = CreateFile( ppl->ofPIF.szPathName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
// If we couldn't open the file, then the presumption is that the
// app didn't have a PIF (or did but someone but someone deleted it),
// and so we use the name we constructed during PifMgr_OpenProperties in case
// they ever opted to save new settings (which they obviously have done!)
// 28-Feb-95: If the PIF did exist at one time (meaning NOCREATPIF is
// set), then don't recreate it; somebody's trying to delete their own
// PIF, so let them. -JTP
if ((ppl->hPIF != INVALID_HANDLE_VALUE) && (GetLastError()!=ERROR_FILE_EXISTS)) {
if (!(ppl->flProp & PROP_NOCREATEPIF)) SetFilePointer( ppl->hPIF, 0, NULL, FILE_BEGIN );
// If the create succeeded, we're no longer using the default PIF
if (ppl->hPIF != INVALID_HANDLE_VALUE) {
ppl->flProp |= PROP_NOCREATEPIF;
ppl->flProp &= ~(PROP_TRUNCATE | PROP_NOPIF | PROP_DEFAULTPIF); } }
// If either the open or the create succeeded, write the PIF data out now
if (ppl->hPIF != INVALID_HANDLE_VALUE) {
PPROPLINK pplEnum; DWORD dwDummy;
WriteFile( ppl->hPIF, (LPCVOID)ppl->lpPIFData, ppl->cbPIFData, &dwDummy, NULL ); if (ppl->flProp & PROP_TRUNCATE) WriteFile(ppl->hPIF, (LPCVOID)ppl->lpPIFData, 0, &dwDummy, NULL ); CloseHandle(ppl->hPIF); ppl->hPIF = INVALID_HANDLE_VALUE; ppl->flProp &= ~(PROP_DIRTY | PROP_TRUNCATE); fSuccess++;
// Here's where we want to check for other active PROPLINKs using the
// same PIF. For each one found, set its DONTWRITE bit.
pplEnum = NULL; while (NULL != (pplEnum = (PPROPLINK)EnumProperties(pplEnum))) { if (lstrcmpi(ppl->ofPIF.szPathName, pplEnum->ofPIF.szPathName) == 0) { if (pplEnum != ppl) pplEnum->flProp |= PROP_DONTWRITE; } } }
// Re-enable annoying critical error popups
SetErrorMode(u);
Exit: ppl->cLocks--; return fSuccess; }
/** AddEnhancedData - create enhanced section(s) of PIF data
* * INPUT * ppl -> property * * OUTPUT * lpenh or NULL */
LPWENHPIF40 AddEnhancedData(PPROPLINK ppl, LPW386PIF30 lp386) { PROPPRG prg; PROPTSK tsk; PROPVID vid; PROPKBD kbd; PROPMSE mse; PROPFNT fnt; PROPWIN win; PROPENV env; void *aDataPtrs[NUM_DATA_PTRS]; LPWENHPIF40 lpenh = NULL;
FunctionName(AddEnhancedData);
// Get copies of pre-enhanced and/or default settings first,
// and do them all *before* doing the AddGroupData, because the
// functions' behavior will change once the enhanced section is added;
// in addition, zero those strucs that contain strings, since lstrcpy()
// may initialize a minimum of 1 byte, leaving garbage in the rest.
BZero(&prg, sizeof(prg)); BZero(&fnt, sizeof(fnt)); BZero(&win, sizeof(win)); BZero(&env, sizeof(env)); BZero(aDataPtrs, sizeof(aDataPtrs));
aDataPtrs[ LP386_INDEX ] = (LPVOID)lp386; GetPrgData(ppl, aDataPtrs, &prg, sizeof(prg), GETPROPS_NONE); GetTskData(ppl, aDataPtrs, &tsk, sizeof(tsk), GETPROPS_NONE); GetVidData(ppl, aDataPtrs, &vid, sizeof(vid), GETPROPS_NONE); GetKbdData(ppl, aDataPtrs, &kbd, sizeof(kbd), GETPROPS_NONE); GetMseData(ppl, aDataPtrs, &mse, sizeof(mse), GETPROPS_NONE); GetFntData(ppl, aDataPtrs, &fnt, sizeof(fnt), GETPROPS_NONE); GetWinData(ppl, aDataPtrs, &win, sizeof(win), GETPROPS_NONE); GetEnvData(ppl, aDataPtrs, &env, sizeof(env), GETPROPS_NONE);
if (AddGroupData(ppl, szWENHHDRSIG40, NULL, sizeof(WENHPIF40))) {
if (NULL != (lpenh = GetGroupData(ppl, szWENHHDRSIG40, NULL, NULL))) {
lpenh->dwEnhModeFlagsProp = prg.dwEnhModeFlags; lpenh->dwRealModeFlagsProp = prg.dwRealModeFlags; lstrcpyA(lpenh->achOtherFileProp, prg.achOtherFile); lstrcpyA(lpenh->achIconFileProp, prg.achIconFile); lpenh->wIconIndexProp = prg.wIconIndex; lpenh->tskProp = tsk; lpenh->vidProp = vid; lpenh->kbdProp = kbd; lpenh->mseProp = mse; lpenh->fntProp = fnt; lpenh->winProp = win; lpenh->envProp = env; lpenh->wInternalRevision = WENHPIF40_VERSION; } } return lpenh; }
/** AddGroupData - add NEW property group to PIF data
* * INPUT * ppl -> property * lpszGroup -> name of new group * lpGroup -> new group record (if NULL, then group data is zero-filled) * cbGroup == size of new group record * * OUTPUT * TRUE if successful, FALSE if not */
BOOL AddGroupData(PPROPLINK ppl, LPCSTR lpszGroup, LPCVOID lpGroup, int cbGroup) { INT cbOld; LPPIFEXTHDR lpph; FunctionName(AddGroupData);
if ((cbOld = ResizePIFData(ppl, cbGroup+sizeof(PIFEXTHDR))) != -1) {
lpph = (LPPIFEXTHDR)LPPIF_FIELDOFF(stdpifext);
while ((DWORD_PTR)lpph <= (DWORD_PTR)LPPIF_OFF(cbOld - sizeof(PIFEXTHDR)) && (DWORD_PTR)lpph >= (DWORD_PTR)LPPIF_FIELDOFF(stdpifext)) { if (lpph->extnxthdrfloff == LASTHDRPTR) { lpph->extnxthdrfloff = (WORD) cbOld; lpph = (LPPIFEXTHDR)LPPIF_OFF(cbOld); lstrcpynA(lpph->extsig, lpszGroup, sizeof(lpph->extsig)); lpph->extnxthdrfloff = LASTHDRPTR; lpph->extfileoffset = (INT)(cbOld + sizeof(PIFEXTHDR)); lpph->extsizebytes = (WORD) cbGroup; if (lpGroup) { hmemcpy((LPBYTE)LPPH_OFF(sizeof(PIFEXTHDR)), lpGroup, cbGroup); ppl->flProp |= PROP_DIRTY; } break; } lpph = (LPPIFEXTHDR)LPPIF_OFF(lpph->extnxthdrfloff); } ppl->cLocks--; return TRUE; } ASSERTFAIL(); return FALSE; }
/** RemoveGroupData - remove EXISTING property group from PIF data
* * INPUT * ppl -> property * lpszGroup -> name of group * * OUTPUT * TRUE if successful, FALSE if not */
BOOL RemoveGroupData(PPROPLINK ppl, LPCSTR lpszGroup) { INT cbGroup, fSuccess; LPBYTE lpGroup; WORD extnxthdrfloff; LPPIFEXTHDR lpph, lpphGroup; FunctionName(RemoveGroupData);
ppl->cLocks++;
fSuccess = FALSE; if (NULL != (lpGroup = GetGroupData(ppl, lpszGroup, &cbGroup, &lpphGroup))) {
// Removing groups is a bit tedious, so here goes....
// First, we will walk all the headers, attempting to find the
// one that points to the one we're about to remove, and point it
// to the next one, and at the same time adjust all file offsets that
// equal or exceed the offsets of either the outgoing data or its
// header.
lpph = (LPPIFEXTHDR)LPPIF_FIELDOFF(stdpifext);
while ((DWORD_PTR)lpph <= (DWORD_PTR)LPPIF_OFF(ppl->cbPIFData - sizeof(PIFEXTHDR)) && (DWORD_PTR)lpph >= (DWORD_PTR)LPPIF_FIELDOFF(stdpifext)) {
extnxthdrfloff = lpph->extnxthdrfloff;
if ((DWORD_PTR)LPPH_OFF(lpph->extfileoffset) >= (DWORD_PTR)lpGroup) lpph->extfileoffset -= (WORD) cbGroup;
if (lpphGroup) { if ((DWORD_PTR)LPPH_OFF(lpph->extfileoffset) >= (DWORD_PTR)lpphGroup) lpph->extfileoffset -= sizeof(PIFEXTHDR); if ((DWORD_PTR)LPPH_OFF(lpph->extnxthdrfloff) == (DWORD_PTR)lpphGroup) extnxthdrfloff = lpph->extnxthdrfloff = lpphGroup->extnxthdrfloff; } if (extnxthdrfloff == LASTHDRPTR) break;
if ((DWORD_PTR)LPPH_OFF(lpph->extnxthdrfloff) >= (DWORD_PTR)lpGroup) lpph->extnxthdrfloff -= (WORD) cbGroup;
if (lpphGroup) if ((DWORD_PTR)LPPH_OFF(lpph->extnxthdrfloff) >= (DWORD_PTR)lpphGroup) lpph->extnxthdrfloff -= sizeof(PIFEXTHDR);
lpph = (LPPIFEXTHDR)LPPIF_OFF(extnxthdrfloff); }
// Next, move everything up over the data, then adjust lpph as
// needed and move everything up over the header (this must be done
// in two discrete steps, because we shouldn't assume anything
// about the data's location relative to its header).
hmemcpy(lpGroup, (LPBYTE)lpGroup+cbGroup, (DWORD_PTR)LPPIF_OFF(ppl->cbPIFData) - (DWORD_PTR)((LPBYTE)lpGroup+cbGroup));
if (lpphGroup) {
if ((DWORD_PTR)lpphGroup >= (DWORD_PTR)((LPBYTE)lpGroup+cbGroup)) lpphGroup -= cbGroup;
hmemcpy(lpphGroup, lpphGroup+1, (DWORD_PTR)LPPIF_OFF(ppl->cbPIFData) - (DWORD_PTR)((LPBYTE)lpphGroup+1+cbGroup));
cbGroup += sizeof(PIFEXTHDR); } ResizePIFData(ppl, -cbGroup); ppl->flProp |= PROP_DIRTY | PROP_TRUNCATE; ppl->cLocks--; } ppl->cLocks--; return fSuccess; }
/** GetGroupData - get ptr to property group (by name)
* * INPUT * ppl -> property (assumes it is LOCKED) * lpszGroup -> property group; may be one of the following: * "WINDOWS 286 3.0" * "WINDOWS 386 3.0" * "WINDOWS PIF.400" * or any other group name that is the name of a valid PIF extension. * if NULL, then *lpcbGroup is a 0-based index of the group we are looking for * lpcbGroup -> where to return size of group data (NULL if not) * lplpph -> where to return ptr to pif extension header, if any (NULL if not) * * OUTPUT * Returns ptr to property group info, NULL if not found */
void *GetGroupData(PPROPLINK ppl, LPCSTR lpszGroup, LPINT lpcbGroup, LPPIFEXTHDR *lplpph) { BOOL fFixMe; LPPIFEXTHDR lpph; FunctionName(GetGroupData);
if (!ppl->lpPIFData) return NULL;
lpph = (LPPIFEXTHDR)LPPIF_FIELDOFF(stdpifext);
while ((DWORD_PTR)lpph <= (DWORD_PTR)LPPIF_OFF(ppl->cbPIFData-sizeof(PIFEXTHDR)) && (DWORD_PTR)lpph >= (DWORD_PTR)LPPIF_FIELDOFF(stdpifext)) {
// PIFEDIT 3.x can trash the first byte of our extended portion
// (generally with a zero), so try to recover by stuffing the first
// character of the group we're looking for into the signature;
// if the rest of the signature matches, great, if it doesn't, then
// re-zero it.
if (!lpszGroup) { // searching by index *lpcbGroup
if (!(*lpcbGroup)--) { if (lplpph) *lplpph = lpph; *lpcbGroup = lpph->extsizebytes; return lpph; } } else { if (FALSE != (fFixMe = !lpph->extsig[0])) // attempt to fix
lpph->extsig[0] = *lpszGroup; if (lstrcmpiA(lpph->extsig, lpszGroup) == 0) { if (lplpph) *lplpph = lpph; if (lpcbGroup) *lpcbGroup = lpph->extsizebytes; if (lpph->extfileoffset >= (WORD)ppl->cbPIFData) { ASSERTFAIL(); return NULL; } return (LPBYTE)LPPIF_OFF(lpph->extfileoffset); } if (fFixMe) // fix failed (this time anyway)
lpph->extsig[0] = 0; } if (lpph->extnxthdrfloff == LASTHDRPTR) break; lpph = (LPPIFEXTHDR)LPPIF_OFF(lpph->extnxthdrfloff); }
// If we didn't get anywhere, check if this is a "really old" PIF;
// ie, one without any headers; if so, then if all they were asking for
// was the old stuff, return it
if (ppl->cbPIFData == sizeof(STDPIF) && lpszGroup) { if (lstrcmpiA(szSTDHDRSIG, lpszGroup) == 0) { if (lplpph) *lplpph = NULL; if (lpcbGroup) *lpcbGroup = sizeof(STDPIF); return ppl->lpPIFData; } } return NULL; }
/** AppWizard - call the AppWizard CPL (appwiz.cpl)
*/
TCHAR c_szAPPWIZ[] = TEXT("appwiz.cpl"); CHAR c_szAppWizard[] = "AppWizard";
typedef DWORD (WINAPI *LPAPPWIZARD)(HWND hwnd, HANDLE i, UINT ui);
UINT WINAPI AppWizard(HWND hwnd, HANDLE hProps, UINT action) { DWORD err = 42; LPAPPWIZARD XAppWizard; HINSTANCE hAppWizard;
hAppWizard = LoadLibrary(c_szAPPWIZ);
if (hAppWizard) { if (NULL != (XAppWizard = (LPAPPWIZARD)GetProcAddress(hAppWizard, c_szAppWizard))) { err = XAppWizard( hwnd, hProps, action ); } FreeLibrary((HINSTANCE)hAppWizard); }
return (UINT)err; }
|