3480 lines
118 KiB
C
3480 lines
118 KiB
C
/*
|
|
* 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;
|
|
}
|