Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3479 lines
118 KiB

  1. /*
  2. * Microsoft Confidential
  3. * Copyright (C) Microsoft Corporation 1991
  4. * All Rights Reserved.
  5. *
  6. *
  7. * PIFMGR.C
  8. * Main module for PIFMGR.DLL
  9. *
  10. * History:
  11. * Created 31-Jul-1992 3:30pm by Jeff Parsons
  12. *
  13. * Exported Program Information File (PIF) Manager services:
  14. *
  15. * PifMgr_OpenProperties()
  16. * Give it the name of an DOS application (com, exe, or bat),
  17. * and it will open the PIF associated with that application
  18. * and return a "handle" to the app's "properties". Use this
  19. * handle when calling any of the other "properties" services (ie,
  20. * Get, Set, and Close).
  21. *
  22. * If no PIF exists, it will still allocate a PIF data block
  23. * in memory and initialize it, either with data from _DEFAULT.PIF
  24. * or its internal defaults. It will also construct the PIF name
  25. * it was looking for but couldn')t find and save that in its internal
  26. * PIF data structure, so that if PifMgr_SetProperties is ever called, the
  27. * data can be saved to disk.
  28. *
  29. * PifMgr_GetProperties()
  30. * Returns the specified block of data from the associated PIF.
  31. * If it is a "named" block, it must be the name of a linked
  32. * extension inside the PIF, which can be any predefined name
  33. * (eg, "WINDOWS 386 3.0") or the name of your own block. You can
  34. * create your own named data blocks using the PifMgr_SetProperties()
  35. * service. "Named" data can also be thought of as "raw" data,
  36. * because it is returned to the caller as-is -- without translation.
  37. *
  38. * The size of a named block can be determined by calling
  39. * PifMgr_GetProperties with a size of zero; no data is copied, but the size
  40. * of the requested block is returned (0 if not found).
  41. *
  42. * All named blocks can be enumerated by passing NULL for the name,
  43. * a pointer to a 16-byte buffer for the requested block name, and a
  44. * 0-based block index in the size parameter. The size returned
  45. * is the size of the block (0 if none).
  46. *
  47. * If an unnamed property block is requested (ie, the selector of
  48. * the name parameter is NULL, and the offset is a property group
  49. * ordinal), then the associated structure is returned. For example,
  50. * PifMgr_GetProperties(GROUP_TSK) returns a predefined structure (see
  51. * PROPTSK in PIF.H) containing all the tasking-related information,
  52. * in a format that is PIF-independent. This is a valuable service,
  53. * because it relieves callers from having to cope with PIFs
  54. * containing a wide variety of sections (known as PIF extensions),
  55. * only one of which is required. Think of this as "cooked" data.
  56. *
  57. * A third variation is raw read/write of the entire PIF data block,
  58. * if lpszGroup is NULL. This must be used with extreme caution, and
  59. * will only be allowed if the properties were opened with the
  60. * OPENPROPS_RAWIO flag specified.
  61. *
  62. * PifMgr_SetProperties()
  63. * This is pretty much the opposite of PifMgr_GetProperties, except that it
  64. * also takes a flags parameter that can specify that the changes
  65. * be made immediately, or deferred to PifMgr_CloseProperties.
  66. *
  67. * PifMgr_CloseProperties()
  68. * Flushes any dirty PIF data in memory, and frees the local heap
  69. * storage.
  70. *
  71. */
  72. #include "shellprv.h"
  73. #pragma hdrstop
  74. /* Global R/W DLL data
  75. */
  76. PPROPLINK g_pplHead; // pointer to first prop entry
  77. HANDLE g_offHighestPropLink; // highest offset of a prop thus far recorded
  78. TCHAR g_szNone[16]; // initialized by LibMainP,
  79. TCHAR g_szAuto[16]; // and 16 chars to allow for localization
  80. char g_szMSDOSSTSFile[] = "C:\\MSDOSSYS.STS";
  81. TCHAR g_szConfigFile[] = TEXT("C:") CONFIGFILE;
  82. TCHAR g_szAutoexecFile[] = TEXT("C:") AUTOEXECFILE;
  83. TCHAR g_szMConfigFile[] = TEXT("C:") MCONFIGFILE;
  84. TCHAR g_szMAutoexecFile[] = TEXT("C:") MAUTOEXECFILE;
  85. TCHAR g_szWConfigFile[] = TEXT("C:") WCONFIGFILE;
  86. TCHAR g_szWAutoexecFile[] = TEXT("C:") WAUTOEXECFILE;
  87. #ifdef DBCS
  88. char ImeBatchFile[] = "DOSIME\0";
  89. #endif
  90. #define NT_CONFIG_FILE "%SystemRoot%\\SYSTEM32\\CONFIG.NT"
  91. #define NT_AUTOEXEC_FILE "%SystemRoot%\\SYSTEM32\\AUTOEXEC.NT"
  92. #define LPPH_OFF(off) ((LPBYTE)lpph + off)
  93. #define LPPIF_FIELDOFF(off) ((LPBYTE)ppl->lpPIFData + FIELD_OFFSET(PIFDATA,off))
  94. #define LPPIF_OFF(off) ((LPBYTE)ppl->lpPIFData + off)
  95. //
  96. // g_szDefaultPIF can be in one of three states:
  97. //
  98. // 1. "_DEFAULT.PIF", which means that we have never needed to search
  99. // for a _default.pif yet. The next time we need to locate
  100. // _default.pif, we must perform a full search. On success,
  101. // move to state 2. On failure, move to state 3.
  102. //
  103. // 2. A fully-qualified path to _default.pif, which means that we have
  104. // searched for a _default.pif and found it in the specified
  105. // location. The next time we need to locate _default.pif, we
  106. // will look here. If found, remain in state 2, else move to
  107. // state 3.
  108. //
  109. // 3. The null string, which means that we searched for a _default.pif
  110. // and didn't find one. The next time we need to locate
  111. // _default.pif, we just fail without even looking on the disk.
  112. // (This is the common case for a clean install.)
  113. //
  114. // Note that all the cases are "sticky"; once you reach a state, you
  115. // can never move back to a previous state. This sacrifices flexibility
  116. // for performance.
  117. //
  118. // The macro fTryDefaultPif() returns nonzero if we are in cases
  119. // 1 or 2.
  120. //
  121. // The macro fDefaultPifFound() returns nonzero if we are in case 2.
  122. //
  123. // WARNING! WARNING! WARNING! WARNING!
  124. //
  125. // Evil hack relies on the fact that the three states can be
  126. // distinguished by the first character of g_szDefaultPIF, which
  127. // in turn relies on the fact that `_' cannot be the first character
  128. // of a fully-qualified path. (It is not a valid drive letter,
  129. // and it cannot start a UNC.)
  130. //
  131. //
  132. #define INIT_INIDATA 0x01
  133. #define INIT_PIFDIR 0x02
  134. CHAR fbInit = 0; // see INIT_* flags
  135. INT iPIFName = (12*sizeof(TCHAR)); // strlen(g_szPIFDir)
  136. INT iWinName = (12*sizeof(TCHAR)); // strlen(g_szPIFDir)
  137. TCHAR g_szPIFDir[MAXPATHNAME] = TEXT("\\WINDOWS\\PIF");
  138. TCHAR g_szDefaultPIF[MAXPATHNAME] = TEXT("_DEFAULT.PIF");
  139. #define fTryDefaultPif() g_szDefaultPIF[0]
  140. #define fDefaultPifFound() (g_szDefaultPIF[0] != TEXT('_'))
  141. //
  142. // szComspec is the name of the COMSPEC program, usually "COMMAND.COM"
  143. // or "CMD.EXE".
  144. //
  145. TCHAR szComspec[8+1+3+1];
  146. /* Global R/O DLL data
  147. */
  148. extern const TCHAR c_szNULL[]; // A string so nice...
  149. const TCHAR szZero[] = TEXT("0");
  150. const int acbData[] = {
  151. sizeof(PROPPRG),
  152. sizeof(PROPTSK),
  153. sizeof(PROPVID),
  154. sizeof(PROPMEM),
  155. sizeof(PROPKBD),
  156. sizeof(PROPMSE),
  157. sizeof(PROPSND),
  158. sizeof(PROPFNT),
  159. sizeof(PROPWIN),
  160. sizeof(PROPENV),
  161. sizeof(PROPNT31),
  162. sizeof(PROPNT40),
  163. };
  164. /*
  165. * The casts are used because we intentionally mis-prototyped the GetXxxData
  166. * and SetXxxData functions to receive their third argument as a LPXXX instead
  167. * of a LPVOID.
  168. */
  169. const DATAGETFN afnGetData[] = {
  170. (DATAGETFN)GetPrgData,
  171. (DATAGETFN)GetTskData,
  172. (DATAGETFN)GetVidData,
  173. (DATAGETFN)GetMemData,
  174. (DATAGETFN)GetKbdData,
  175. (DATAGETFN)GetMseData,
  176. (DATAGETFN)GetSndData,
  177. (DATAGETFN)GetFntData,
  178. (DATAGETFN)GetWinData,
  179. (DATAGETFN)GetEnvData,
  180. (DATAGETFN)GetNt31Data,
  181. (DATAGETFN)GetNt40Data,
  182. };
  183. const DATASETFN afnSetData[] = {
  184. (DATASETFN)SetPrgData,
  185. (DATASETFN)SetTskData,
  186. (DATASETFN)SetVidData,
  187. (DATASETFN)SetMemData,
  188. (DATASETFN)SetKbdData,
  189. (DATASETFN)SetMseData,
  190. (DATASETFN)SetSndData,
  191. (DATASETFN)SetFntData,
  192. (DATASETFN)SetWinData,
  193. (DATASETFN)SetEnvData,
  194. (DATASETFN)SetNt31Data,
  195. (DATASETFN)SetNt40Data,
  196. };
  197. // WIN.INI things of interest
  198. // Note: some of these NEED to be ANSI strings, and other TCHAR
  199. // strings. Please do not arbitrarily change the type casts of
  200. // these strings!!!! (RickTu)
  201. const TCHAR szMemory[] = TEXT("MEMORY");
  202. const TCHAR szComp[] = TEXT("COMPATIBLE");
  203. CHAR szSingle[] = "DOS=SINGLE\r\n";
  204. CHAR szCRLF[] = "\r\n";
  205. CHAR szEcho[] = "ECHO ";
  206. CHAR szPause[] = "\r\nPAUSE\r\n";
  207. CHAR szCall[] = "CALL ";
  208. CHAR szCD[] = "CD ";
  209. CHAR szWin[] = "WIN";
  210. // SYSTEM.INI things of interest
  211. const TCHAR szSystemINI[] = TEXT("SYSTEM.INI");
  212. const TCHAR sz386EnhSection[] = TEXT("386Enh");
  213. const TCHAR szWOAFontKey[] = TEXT("WOAFont");
  214. const TCHAR szWOADBCSFontKey[] = TEXT("WOADBCSFont");
  215. const TCHAR szNonWinSection[] = TEXT("NonWindowsApp");
  216. const TCHAR szTTInitialSizes[] = TEXT("TTInitialSizes");
  217. #ifdef CUSTOMIZABLE_HEURISTICS
  218. const TCHAR szTTHeuristics[] = TEXT("TTHeuristics");
  219. const TCHAR szTTNonAspectMin[] = TEXT("TTNonAspectMin");
  220. #endif
  221. TCHAR szTTCacheSection[2][32] = {TEXT("TTFontDimenCache"), TEXT("TTFontDimenCacheDBCS")};
  222. //
  223. // These are because they are accessed only when we need to create
  224. // a new PIF file or convert a 3.1 PIF file into a 4.0 PIF file.
  225. //
  226. const TCHAR szDOSAPPINI[] = TEXT("DOSAPP.INI");
  227. const TCHAR szDOSAPPSection[] = TEXT("DOS Applications");
  228. const TCHAR szDOSAPPDefault[] = TEXT("Default");
  229. const TCHAR szDisplay[] = TEXT("DISPLAY");
  230. const TCHAR szDefIconFile[] = ICONFILE_DEFAULT;
  231. const TCHAR szDotExe[] = TEXT(".EXE");
  232. const TCHAR szDotCom[] = TEXT(".COM");
  233. const TCHAR szDotBat[] = TEXT(".BAT");
  234. const TCHAR szDotPif[] = TEXT(".PIF");
  235. const TCHAR szDotCmd[] = TEXT(".CMD");
  236. const TCHAR * apszAppType[] = {
  237. szDotExe, szDotCom, szDotBat, szDotCmd, szDotPif
  238. };
  239. CHAR szSTDHDRSIG[] = STDHDRSIG;
  240. CHAR szW286HDRSIG30[] = W286HDRSIG30;
  241. CHAR szW386HDRSIG30[] = W386HDRSIG30;
  242. CHAR szWENHHDRSIG40[] = WENHHDRSIG40;
  243. CHAR szWNTHDRSIG31[] = WNTHDRSIG31;
  244. CHAR szWNTHDRSIG40[] = WNTHDRSIG40;
  245. CHAR szCONFIGHDRSIG40[] = CONFIGHDRSIG40;
  246. CHAR szAUTOEXECHDRSIG40[] = AUTOEXECHDRSIG40;
  247. const TCHAR szRunOnceKey[] = REGSTR_PATH_RUNONCE;
  248. const TCHAR szPIFConvert[] = TEXT("PIFConvert");
  249. const TCHAR szPIFConvertExe[] = TEXT("RUNDLL.EXE PIFMGR.DLL,ProcessStartupProperties");
  250. const TCHAR szPIFConvertKey[] = REGSTR_PATH_PIFCONVERT;
  251. const TCHAR szMSDOSMode[] = REGSTR_VAL_MSDOSMODE;
  252. const TCHAR szMSDOSModeDiscard[] = REGSTR_VAL_MSDOSMODEDISCARD;
  253. // wsprintf formatting strings
  254. const TCHAR szDotPercent03d[] = TEXT(".%03d");
  255. // miscellaneous hack-o-ramas
  256. const TCHAR szPP4[] = TEXT("PP4"); // MS Powerpoint 4.0
  257. PROPTSK tskDefault ={TSK_DEFAULT,
  258. TSKINIT_DEFAULT,
  259. TSKFGNDBOOST_DEFAULT,
  260. TSKBGNDBOOST_DEFAULT,
  261. 0,
  262. 0,
  263. TSKIDLESENS_DEFAULT,
  264. };
  265. PROPVID vidDefault ={VID_DEFAULT,
  266. VIDINIT_DEFAULT,
  267. 0,
  268. 0,
  269. 0,
  270. };
  271. PROPMEM memDefault ={MEM_DEFAULT,
  272. MEMINIT_DEFAULT,
  273. MEMLOW_DEFAULT, // ignore stdpifdata.minmem?
  274. MEMLOW_MAX, // ignore stdpifdata.maxmem?
  275. MEMEMS_DEFAULT,
  276. MEMEMS_MAX,
  277. MEMXMS_DEFAULT,
  278. MEMXMS_MAX,
  279. };
  280. PROPKBD kbdDefault ={KBD_DEFAULT,
  281. KBDINIT_DEFAULT,
  282. KBDALTDELAY_DEFAULT,
  283. KBDALTPASTEDELAY_DEFAULT,
  284. KBDPASTEDELAY_DEFAULT,
  285. KBDPASTEFULLDELAY_DEFAULT,
  286. KBDPASTETIMEOUT_DEFAULT,
  287. KBDPASTESKIP_DEFAULT,
  288. KBDPASTECRSKIP_DEFAULT,
  289. };
  290. PROPMSE mseDefault ={MSE_DEFAULT,
  291. MSEINIT_DEFAULT,
  292. };
  293. PROPENV envDefault ={ENV_DEFAULT,
  294. ENVINIT_DEFAULT,
  295. "",
  296. ENVSIZE_DEFAULT,
  297. ENVDPMI_DEFAULT,
  298. };
  299. WORD flWinDefault = WIN_DEFAULT;
  300. /*
  301. * Default face name to use for Raster fonts. Currently, this is
  302. * just a hard-coded value (ie, not maintained in any INI file).
  303. */
  304. CHAR szRasterFaceName[LF_FACESIZE] = "Terminal";
  305. /*
  306. * Default face name to use for TrueType fonts. It must be a monospace
  307. * font, and it must be a font that everyone is guaranteed to have. Currently,
  308. * this can be changed by setting TTFont in [NonWindowsApp] in SYSTEM.INI.
  309. */
  310. // now this is initialized with string resource. The 2nd element will get
  311. // the native typeface for the bilingual dos prompt
  312. CHAR szTTFaceName[2][LF_FACESIZE] = {"Lucida Console", "Courier New"};
  313. const TCHAR szAltKeyDelay [] = TEXT("AltKeyDelay");
  314. const TCHAR szAltPasteDelay [] = TEXT("AltPasteDelay");
  315. const TCHAR szKeyPasteDelay [] = TEXT("KeyPasteDelay");
  316. const TCHAR szKeyBufferDelay [] = TEXT("KeyBufferDelay");
  317. const TCHAR szKeyPasteTimeout [] = TEXT("KeyPasteTimeout");
  318. const TCHAR szKeyPasteSkipCount [] = TEXT("KeyPasteSkipCount");
  319. const TCHAR szKeyPasteCRSkipCount[] = TEXT("KeyPasteCRSkipCount");
  320. const TCHAR szMouseInDosBox [] = TEXT("MouseInDosBox");
  321. const TCHAR szDisablePositionSave[] = TEXT("DisablePositionSave");
  322. const TCHAR szDOSPromptExitInst [] = TEXT("DOSPromptExitInstruc");
  323. const TCHAR szCommandEnvSize [] = TEXT("CommandEnvSize");
  324. const TCHAR szScreenLines [] = TEXT("ScreenLines");
  325. const INIDATA aINIData[] = {
  326. {sz386EnhSection, szAltKeyDelay, &kbdDefault.msAltDelay, INIDATA_FIXEDPOINT},
  327. {sz386EnhSection, szAltPasteDelay, &kbdDefault.msAltPasteDelay, INIDATA_FIXEDPOINT},
  328. {sz386EnhSection, szKeyPasteDelay, &kbdDefault.msPasteDelay, INIDATA_FIXEDPOINT},
  329. {sz386EnhSection, szKeyBufferDelay, &kbdDefault.msPasteFullDelay,INIDATA_FIXEDPOINT},
  330. {sz386EnhSection, szKeyPasteTimeout, &kbdDefault.msPasteTimeout, INIDATA_FIXEDPOINT},
  331. {sz386EnhSection, szKeyPasteSkipCount, &kbdDefault.cPasteSkip, INIDATA_DECINT},
  332. {sz386EnhSection, szKeyPasteCRSkipCount, &kbdDefault.cPasteCRSkip, INIDATA_DECINT},
  333. {szNonWinSection, szMouseInDosBox, &mseDefault.flMse, INIDATA_BOOLEAN, MSE_WINDOWENABLE},
  334. {szNonWinSection, szDisablePositionSave, &flWinDefault, INIDATA_BOOLEAN | INIDATA_INVERT, WIN_SAVESETTINGS},
  335. #ifdef ENVINIT_INSTRUCTIONS
  336. {sz386EnhSection, szDOSPromptExitInst, &envDefault.flEnvInit, INIDATA_BOOLEAN, ENVINIT_INSTRUCTIONS},
  337. #endif
  338. {szNonWinSection, szCommandEnvSize, &envDefault.cbEnvironment, INIDATA_DECINT},
  339. {szNonWinSection, szScreenLines, &vidDefault.cScreenLines, INIDATA_DECINT},
  340. };
  341. /**************************************************************************
  342. *
  343. * OVERVIEW OF INI FILE USAGE
  344. *
  345. *
  346. * SYSTEM.INI
  347. *
  348. * [386Enh]
  349. *
  350. * WOAFont=<fon filename>
  351. *
  352. * Status: Public
  353. * Default: dosapp.fon
  354. * Purpose:
  355. *
  356. * This setting allows the user to specify which Terminal font
  357. * file should be loaded when DOS box is started.
  358. *
  359. * To change:
  360. *
  361. * Use Notepad to edit the SYSTEM.INI file.
  362. *
  363. *
  364. * [NonWindowsApp]
  365. *
  366. * DisablePositionSave=<Boolean>
  367. *
  368. * Status: Public
  369. * Default: 0 (FALSE)
  370. * Purpose:
  371. *
  372. * When FALSE, the position and font used in a non-Windows
  373. * application is saved in the application's PIF file when
  374. * you exit the application. When TRUE, the position, fonts, and
  375. * toolbar state of a non-Windows application whose settings
  376. * have not been previously saved in the DOSAPP.INI file will
  377. * not be saved.
  378. *
  379. * If enabled, the setting can be overridden for each
  380. * non-Windows application by selecting the Save Settings On
  381. * Exit check box in the Font dialog box.
  382. *
  383. * To change:
  384. *
  385. * Use Notepad to edit the SYSTEM.INI file.
  386. *
  387. * Compatibility notes:
  388. *
  389. * In Windows 3.x, the "position save" (and font) information was
  390. * saved in DOSAPP.INI, and although we will still read DOSAPP.INI
  391. * in the absence of any information in the PIF file, we only *write*
  392. * settings back to the PIF file. DOSAPP.INI should be considered
  393. * obsolete.
  394. *
  395. *
  396. * TTFont=<fontname>
  397. *
  398. * Status: ?
  399. * Default: Courier New // FEATURE -- this should be a TT OEM font
  400. * Purpose:
  401. *
  402. * This setting allows the user to specify which TrueType font
  403. * will be used in a DOS box. It must be an OEM font.
  404. *
  405. * To change:
  406. *
  407. * Use Notepad to edit the SYSTEM.INI file.
  408. *
  409. *
  410. * TTInitialSizes=<i1 i2 i3 i4 ... i16>
  411. *
  412. * Status: ?
  413. * Default: 4 5 6 7 8 9 10 11 12 14 16 18 20 22 36 72
  414. * Purpose:
  415. *
  416. * This setting allows the user to specify which font sizes
  417. * WinOldAp initially builds for the TrueType fonts in a DOS
  418. * application window.
  419. *
  420. * At most 16 font sizes can be requested.
  421. *
  422. * Note that this INI entry is consulted only the first time
  423. * Windows is restarted after changing video drivers or fonts.
  424. *
  425. * To change:
  426. *
  427. * Use Notepad to edit the SYSTEM.INI file.
  428. *
  429. *
  430. * TTHeuristics=<i1 i2 i3 i4 i5 i6 i7 i8 i9>
  431. *
  432. * Status: Public
  433. * Default: 5000 1000 0 1000 5000 1000 0 1000 1
  434. * Purpose:
  435. *
  436. * These integers control the way Windows chooses the font to
  437. * display for DOS applications running inside a window if you
  438. * have chosen "Auto" as the font size.
  439. *
  440. * The parameters are named as follows:
  441. *
  442. * i1=XOvershootInitial
  443. * i2=XOvershootScale
  444. * i3=XShortfallInitial
  445. * i4=XShortfallScale
  446. * i5=YOvershootInitial
  447. * i6=YOvershootScale
  448. * i7=YShortfallInitial
  449. * i8=YShortfallScale
  450. * i9=TrueTypePenalty
  451. *
  452. * Each penalty value may not exceed 5000.
  453. *
  454. * When Windows needs to select a font for use in a DOS
  455. * application's window, it goes through the list of font
  456. * sizes available and computes the "penalty" associated
  457. * with using that font. Windows then selects the font with
  458. * the smallest penalty.
  459. *
  460. * The horizontal penalty is computed as follows:
  461. *
  462. * Let dxActual = <actual window width>
  463. * Let dxDesired = <font width> * <characters per line>
  464. *
  465. * If dxActual = dxDesired:
  466. * xPenalty = 0
  467. * If dxActual < dxDesired:
  468. * Let Ratio = 1 - dxDesired / dxActual
  469. * xPenalty = XOvershootInitial + Ratio * XOvershootScale
  470. * If dxActual > dxDesired:
  471. * Let Ratio = 1 - dxActual / dxDesired
  472. * xPenalty = XShortfallInitial + Ratio * XShortfallScale
  473. *
  474. * The vertical penalty is computed similarly.
  475. *
  476. * Note that the Ratio is always a fraction between 0 and 1.
  477. *
  478. * The penalty associated with a font is the sum of the vertical
  479. * and horizontal penalties, plus the TrueTypePenalty if the font
  480. * is a TrueType font.
  481. *
  482. * The default value of 1 for the TrueTypePenalty means that,
  483. * all other things being equal, Windows will select a raster
  484. * font in preference to a TrueType font. You can set this
  485. * value to -1 if you wish the opposite preference.
  486. *
  487. * To change:
  488. *
  489. * Use Notepad to edit the SYSTEM.INI file.
  490. *
  491. * Internals:
  492. *
  493. * Even though floating point appears in the computations,
  494. * everything is really done in integer arithmetic.
  495. *
  496. * Pixels are NEVER MENTIONED anywhere in the penalty computations.
  497. * (All pixel values are divided by other pixel values, so that
  498. * we get a dimensionless number as a result.)
  499. * This keeps us independent of the display resolution as well
  500. * as the display aspect ratio.
  501. *
  502. * Since the stretch and shrink are taken as fractions of the
  503. * larger dimension, this keeps us from penalizing large
  504. * differences by too much. This is important because there
  505. * isn't much visible difference between being ten times too
  506. * big and being eleven times too big, but there is a big
  507. * difference between being just right and being twice as big.
  508. *
  509. * We must be careful not to let the maximum possible penalty
  510. * exceed 32767. This is done by making sure that each
  511. * dimension cannot produce a penalty of greater than 10000
  512. * (5000+5000), and that the TrueTypePenalty is at most 5000.
  513. * This makes the maximum possible penalty 25000.
  514. * This range checking is done by FontSelInit.
  515. *
  516. *
  517. * TTNonAspectMin=<x y>
  518. *
  519. * Status: Public
  520. * Default: 3 3
  521. * Purpose:
  522. *
  523. * These integers control the minimum width and height font that
  524. * Windows will attempt to create automatically in response to a
  525. * resize operation when TrueType fonts in DOS boxes are enabled
  526. * and the "Auto" font size is selected.
  527. *
  528. * These values prevent Windows from creating visually useless
  529. * fonts like 10 x 1 or 1 x 10. The default values prevent Windows
  530. * from trying to create X x Y fonts if X < 3 or Y < 3.
  531. *
  532. * TTNonAspectMin is not consulted if the font is being created at
  533. * its default aspect ratio. In other words, Windows will create,
  534. * for example, a 1 x 3 font, if 1 x 3 is the standard aspect ratio
  535. * for a 3-pixel-high font.
  536. *
  537. * To permit all aspect ratios, set the values to "0 0".
  538. *
  539. * To forbid all aspect ratios except for the standard aspect ratio,
  540. * set the values to "-1 -1".
  541. *
  542. * [TTFontDimenCache]
  543. *
  544. * dxWidthRequested dyHeightRequested=dxWidthActual dyWidthActual
  545. *
  546. * Status: Private
  547. * Default: Null
  548. * Purpose:
  549. *
  550. * The [FontDimenCache] section contains information about
  551. * TrueType font sizes that have been created. Each entry
  552. * has as the keyname the width and height that were passed
  553. * to CreateFont and has as the value the width and height of
  554. * the font that was actually created.
  555. *
  556. * Internals:
  557. *
  558. * Inspected by AddTrueTypeFontsToFontList.
  559. * Set by AddOneNewTrueTypeFontToFontList.
  560. *
  561. *
  562. **************************************************************************
  563. *
  564. * DOSAPP.INI (obsolete, supported on a read-only basis)
  565. *
  566. * [Dos Applications]
  567. *
  568. * C:\FULL\PATH\TO\EXE\COM\BAT\OR.PIF=<wFlags wFontWidth wFontHeight
  569. * wWinWidth wWinHeight length flags showCmd ptMinPositionX
  570. * ptMinPositionY ptMaxPositionX ptMaxPositionY
  571. * rcNormalLeft rcNormalTop rcNormalRight rcNormalBottom>
  572. *
  573. * Status: Private
  574. * Purpose:
  575. *
  576. * These values are used to restore a DOS application's window
  577. * to the state it was in when the DOS app last exited normally.
  578. *
  579. * The values are taken directly from the INIINFO structure, qv.
  580. *
  581. * The values of ptMinPositionX and ptMinPositionY are always -1,
  582. * since we do not try to preserve the icon position.
  583. *
  584. * If wFontHeight has the high bit set, then the font that
  585. * should be used is a TrueType font.
  586. *
  587. * If wFontWidth = 1 and wFontHeight = -1, then
  588. * Auto-font-selection is active.
  589. *
  590. * Compatibility notes:
  591. *
  592. * In Windows 3.x, the "position save" (and font) information was
  593. * saved in DOSAPP.INI, and although we will still read DOSAPP.INI
  594. * in the absence of any information in the PIF file, we only *write*
  595. * settings back to the PIF file. DOSAPP.INI should be considered
  596. * obsolete.
  597. *
  598. *
  599. **************************************************************************
  600. *
  601. * THE NEXT INI VAR IS NOT IMPLEMENTED BUT SHOULD BE
  602. *
  603. **************************************************************************
  604. *
  605. * SYSTEM.INI
  606. *
  607. * [NonWindowsApp]
  608. *
  609. * TTFontTolerance=<i>
  610. *
  611. * Status: Public
  612. * Default: 200
  613. * Purpose:
  614. *
  615. * This setting indicates how large a penalty (see TTHeuristics)
  616. * Windows should tolerate before trying to synthesize new font
  617. * sizes from TrueType fonts.
  618. *
  619. * Decreasing this value will result in a tighter fit of the
  620. * Windows-selected font to the actual window size, but at a
  621. * cost in speed and memory.
  622. *
  623. * To change:
  624. *
  625. * Use Notepad to edit the SYSTEM.INI file.
  626. *
  627. *
  628. * Internals:
  629. *
  630. * Inspected by ChooseBestFont, if implemented.
  631. *
  632. **************************************************************************/
  633. void PifMgrDLL_Init()
  634. {
  635. static BOOL fInit = FALSE;
  636. if (!fInit)
  637. {
  638. LoadString(g_hinst, IDS_PIF_NONE, g_szNone, ARRAYSIZE(g_szNone));
  639. LoadString(g_hinst, IDS_AUTONORMAL, g_szAuto, ARRAYSIZE(g_szAuto));
  640. LoadGlobalFontData();
  641. fInit = TRUE;
  642. }
  643. }
  644. void PifMgrDLL_Term()
  645. {
  646. FreeGlobalFontData();
  647. }
  648. /** GetPIFDir - Form default PIF directory name + name of given file
  649. *
  650. * INPUT
  651. * None
  652. *
  653. * OUTPUT
  654. * None
  655. */
  656. void GetPIFDir(LPTSTR pszName)
  657. {
  658. int i;
  659. static const TCHAR szBackslashPIF[] = TEXT("\\PIF");
  660. FunctionName(GetPIFDir);
  661. if (!(fbInit & INIT_PIFDIR)) {
  662. // Set up g_szPIFDir, less space for a filename, less space for \PIF
  663. i = ARRAYSIZE(g_szPIFDir)-lstrlen(pszName)-ARRAYSIZE(szBackslashPIF);
  664. if (i <= 0) // sanity check
  665. return;
  666. GetWindowsDirectory(g_szPIFDir, i);
  667. iPIFName = lstrlen(g_szPIFDir);
  668. if (StrRChr(g_szPIFDir, NULL, TEXT('\\')) == &g_szPIFDir[iPIFName-1])
  669. iPIFName--;
  670. iWinName = iPIFName;
  671. lstrcpy(g_szPIFDir+iPIFName, szBackslashPIF);
  672. iPIFName += ARRAYSIZE(szBackslashPIF)-1;
  673. i = (int)GetFileAttributes(g_szPIFDir);
  674. if (i == -1) {
  675. // It didn't exist, so try to create it (returns TRUE if success)
  676. i = CreateDirectory(g_szPIFDir, NULL);
  677. if (i)
  678. SetFileAttributes(g_szPIFDir, FILE_ATTRIBUTE_HIDDEN);
  679. }
  680. else if (i & FILE_ATTRIBUTE_DIRECTORY)
  681. i = TRUE; // directory already exists, cool!
  682. else
  683. i = FALSE; // some sort of file is in the way...
  684. if (i) {
  685. g_szPIFDir[iPIFName++] = TEXT('\\'); // append the slash we'll need
  686. // to separate future filenames (the
  687. // space after is already zero-init'ed)
  688. }
  689. else // we'll just have to use the Windows dir
  690. iPIFName -= ARRAYSIZE(szBackslashPIF)-2;
  691. fbInit |= INIT_PIFDIR;
  692. }
  693. // Now initialize g_szPIFDir with the name of the file we're processing
  694. if (pszName)
  695. lstrcpyn(g_szPIFDir+iPIFName, pszName, ARRAYSIZE(g_szPIFDir)-iPIFName);
  696. }
  697. /** GetINIData - Read WIN.INI/SYSTEM.INI/DOSAPP.INI for default settings
  698. *
  699. * INPUT
  700. * Nothing
  701. *
  702. * OUTPUT
  703. * Nothing; global defaults (re)set
  704. *
  705. * NOTES
  706. * We only do this work now if GetPIFData couldn't open a PIF file, or
  707. * could but it contained no enhanced section. And we never do it more than
  708. * once per fresh load of this DLL.
  709. */
  710. void GetINIData()
  711. {
  712. int t;
  713. const INIDATA *pid;
  714. LPCTSTR lpsz;
  715. DWORD dwRet;
  716. TCHAR szTemp[MAX_PATH];
  717. FunctionName(GetINIData);
  718. if (fbInit & INIT_INIDATA) // if already done
  719. return; // then go away
  720. for (pid=aINIData; pid-aINIData < ARRAYSIZE(aINIData); pid++) {
  721. t = *(INT UNALIGNED *)pid->pValue;
  722. if (pid->iFlags & (INIDATA_DECINT | INIDATA_BOOLEAN)) {
  723. if (pid->iFlags & INIDATA_BOOLEAN) {
  724. t &= pid->iMask;
  725. if (pid->iFlags & INIDATA_INVERT)
  726. t ^= pid->iMask;
  727. }
  728. t = GetPrivateProfileInt(pid->pszSection,
  729. pid->pszKey,
  730. t,
  731. szSystemINI);
  732. if (pid->iFlags & INIDATA_BOOLEAN) {
  733. if (t)
  734. t = pid->iMask;
  735. if (pid->iFlags & INIDATA_INVERT)
  736. t ^= pid->iMask;
  737. t |= *(INT UNALIGNED *)pid->pValue & ~pid->iMask;
  738. }
  739. *(INT UNALIGNED *)pid->pValue = t;
  740. }
  741. else
  742. if (pid->iFlags & INIDATA_FIXEDPOINT) {
  743. wsprintf(szTemp, szDotPercent03d, t);
  744. GetPrivateProfileString(pid->pszSection,
  745. pid->pszKey,
  746. szTemp,
  747. szTemp,
  748. ARRAYSIZE(szTemp),
  749. szSystemINI);
  750. *(INT UNALIGNED *)pid->pValue = StrToInt(szTemp+1);
  751. }
  752. else
  753. ASSERTFAIL();
  754. }
  755. //
  756. // Locate COMSPEC once and for all.
  757. //
  758. dwRet = GetEnvironmentVariable(TEXT("COMSPEC"), szTemp, ARRAYSIZE(szTemp));
  759. if (dwRet < ARRAYSIZE(szTemp) && dwRet > 0)
  760. {
  761. lpsz = StrRChr(szTemp, NULL, TEXT('\\'));
  762. if (lpsz) {
  763. lstrcpyn(szComspec, lpsz+1, ARRAYSIZE(szComspec));
  764. }
  765. }
  766. fbInit |= INIT_INIDATA;
  767. }
  768. /** InitProperties - initialize new property structure
  769. *
  770. * INPUT
  771. * ppl -> property
  772. * fLocked == TRUE to return data locked, FALSE unlocked
  773. *
  774. * OUTPUT
  775. * Nothing (if successful, ppl->hPIFData will become non-zero)
  776. */
  777. void InitProperties(PPROPLINK ppl, BOOL fLocked)
  778. {
  779. LPSTDPIF lpstd;
  780. LPW386PIF30 lp386 = NULL;
  781. CHAR achPathName[ARRAYSIZE(ppl->szPathName)];
  782. BYTE behavior = 0;
  783. FunctionName(InitProperties);
  784. GetINIData(); // make sure we have all the right defaults
  785. if (ResizePIFData(ppl, sizeof(STDPIF)) != -1) {
  786. // We're no longer called *only* after a fresh ZERO'd HeapAlloc
  787. // by ResizePIFData. We could be getting called because PifMgr_OpenProperties
  788. // was told to punt on an ambiguous PIF and create new settings.
  789. // Hence, we always zero-init the buffer ourselves now.
  790. BZero(ppl->lpPIFData, ppl->cbPIFData);
  791. lpstd = (LPSTDPIF)ppl->lpPIFData;
  792. lpstd->id = 0x78;
  793. PifMgr_WCtoMBPath( ppl->szPathName, achPathName, ARRAYSIZE(achPathName) );
  794. lstrcpyncharA(lpstd->appname, achPathName+ppl->iFileName, ARRAYSIZE(lpstd->appname), '.');
  795. CharToOemA(lpstd->appname, lpstd->appname);
  796. // NOTE: When 3.x Setup creates PIF files, it sets maxmem to 640;
  797. // that's typically what memDefault.wMaxLow will be too....
  798. lpstd->minmem = memDefault.wMinLow;
  799. lpstd->maxmem = (WORD) GetProfileInt(apszAppType[APPTYPE_PIF]+1, szMemory, memDefault.wMaxLow);
  800. lstrcpynA(lpstd->startfile, achPathName, ARRAYSIZE(lpstd->startfile));
  801. CharToOemA(lpstd->startfile, lpstd->startfile);
  802. //
  803. // New for 4.0: fDestroy (close on exit) is disabled by default
  804. // for most apps, but is enabled by default for COMSPEC.
  805. //
  806. lpstd->MSflags = 0;
  807. if (!lstrcmpi(ppl->szPathName+ppl->iFileName, szComspec)) {
  808. lpstd->MSflags = fDestroy;
  809. }
  810. // Initialize various goofy non-zero stuff just to make it
  811. // look like a backward-compatible PIF file -- not that we use
  812. // or particularly care about any of it
  813. // NOTE: When 3.x Setup creates PIF files, it sets screen to 0x7F
  814. lpstd->cPages = 1;
  815. lpstd->highVector = 0xFF;
  816. lpstd->rows = 25;
  817. lpstd->cols = 80;
  818. lpstd->sysmem = 0x0007;
  819. // fFullScreen is no longer default, so only if an explicit
  820. // COMPATIBLE=FALSE exists in the PIF section of WIN.INI will
  821. // we set fScreen in behavior and fFullScreen in PfW386Flags
  822. // Similarly, fDestroy is no longer default, but we'll go
  823. // back to the old way if the switch tells us to.
  824. if (!GetProfileInt(apszAppType[APPTYPE_PIF]+1, szComp, TRUE)) {
  825. lpstd->behavior = behavior = fScreen;
  826. lpstd->MSflags = fDestroy;
  827. }
  828. if (ppl->ckbMem != -1 && ppl->ckbMem != 1)
  829. lpstd->minmem = lpstd->maxmem = (WORD) ppl->ckbMem;
  830. if (AddGroupData(ppl, szW386HDRSIG30, NULL, sizeof(W386PIF30))) {
  831. if (NULL != (lp386 = GetGroupData(ppl, szW386HDRSIG30, NULL, NULL))) {
  832. lp386->PfW386minmem = lpstd->minmem;
  833. lp386->PfW386maxmem = lpstd->maxmem;
  834. lp386->PfFPriority = TSKFGND_OLD_DEFAULT;
  835. lp386->PfBPriority = TSKBGND_OLD_DEFAULT;
  836. lp386->PfMinEMMK = memDefault.wMinEMS;
  837. lp386->PfMaxEMMK = memDefault.wMaxEMS;
  838. lp386->PfMinXmsK = memDefault.wMinXMS;
  839. lp386->PfMaxXmsK = memDefault.wMaxXMS;
  840. lp386->PfW386Flags = fBackground + fPollingDetect + fINT16Paste;
  841. if (behavior & fScreen)
  842. lp386->PfW386Flags |= fFullScreen;
  843. lp386->PfW386Flags2 = fVidTxtEmulate + fVidNoTrpTxt + fVidNoTrpLRGrfx + fVidNoTrpHRGrfx + fVidTextMd;
  844. }
  845. }
  846. VERIFYTRUE(AddEnhancedData(ppl, lp386));
  847. if (AddGroupData(ppl, szWNTHDRSIG31, NULL, sizeof(WNTPIF31))) {
  848. LPWNTPIF31 lpnt31;
  849. if (NULL != (lpnt31 = GetGroupData(ppl, szWNTHDRSIG31, NULL, NULL))) {
  850. lstrcpyA( lpnt31->nt31Prop.achConfigFile, NT_CONFIG_FILE );
  851. lstrcpyA( lpnt31->nt31Prop.achAutoexecFile, NT_AUTOEXEC_FILE );
  852. }
  853. }
  854. VERIFYTRUE(AddGroupData(ppl, szWNTHDRSIG40, NULL, sizeof(WNTPIF40)));
  855. // Can't be dirty anymore, 'cause we just set everything to defaults
  856. ppl->flProp &= ~PROP_DIRTY;
  857. if (!fLocked)
  858. ppl->cLocks--;
  859. }
  860. else
  861. ASSERTFAIL();
  862. }
  863. /** OpenPIFFile - Wrapper around CreateFile for opening PIF files
  864. *
  865. * The wrapper handles the following things:
  866. *
  867. * Passing the proper access and sharing flags to CreateFile.
  868. * Setting pof->nErrCode = 0 on success.
  869. * Converting ERROR_PATH_NOT_FOUND to ERROR_FILE_NOT_FOUND.
  870. *
  871. * INPUT
  872. *
  873. * pszFile -> name of file to attempt to open
  874. * pof -> PIFOFSTRUCT to fill in
  875. *
  876. * OUTPUT
  877. *
  878. * Same return code as CreateFile.
  879. *
  880. */
  881. HANDLE OpenPIFFile(LPCTSTR pszFile, LPPIFOFSTRUCT pof)
  882. {
  883. HANDLE hf;
  884. TCHAR pszFullFile[ MAX_PATH ];
  885. LPTSTR pszTheFile;
  886. DWORD dwRet;
  887. //
  888. // CreateFile does not search the path, so do that first, then
  889. // give CreateFile a fully qualified file name to open...
  890. //
  891. dwRet = SearchPath( NULL,
  892. pszFile,
  893. NULL,
  894. ARRAYSIZE(pszFullFile),
  895. pszFullFile,
  896. &pszTheFile
  897. );
  898. if ((dwRet==0) || (dwRet > ARRAYSIZE(pszFullFile)))
  899. {
  900. pszTheFile = (LPTSTR)pszFile;
  901. }
  902. else
  903. {
  904. pszTheFile = pszFullFile;
  905. }
  906. hf = CreateFile( pszTheFile,
  907. GENERIC_READ,
  908. FILE_SHARE_READ,
  909. NULL,
  910. OPEN_EXISTING,
  911. FILE_ATTRIBUTE_NORMAL,
  912. NULL );
  913. if (hf == INVALID_HANDLE_VALUE)
  914. {
  915. pof->nErrCode = GetLastError();
  916. if (pof->nErrCode == ERROR_PATH_NOT_FOUND)
  917. pof->nErrCode = ERROR_FILE_NOT_FOUND;
  918. }
  919. else
  920. {
  921. LPTSTR lpDummy;
  922. //
  923. // NOTE: Special hack for creating shortcuts. If the PIF file
  924. // that we find is 0 bytes long, pretend we did not find one at all.
  925. // This is because appwiz renames a 0 length file from "New shortcut.lnk"
  926. // to "appname.pif" and we end up finding it. We'll ignore this file.
  927. //
  928. if (SetFilePointer( hf, 0, NULL, FILE_END) == 0)
  929. {
  930. CloseHandle( hf );
  931. hf = INVALID_HANDLE_VALUE;
  932. pof->nErrCode = ERROR_FILE_NOT_FOUND;
  933. }
  934. else
  935. {
  936. LPCTSTR pszNewFile;
  937. TCHAR szTemp[ ARRAYSIZE(pof->szPathName) ];
  938. SetFilePointer( hf, 0, NULL, FILE_BEGIN );
  939. pof->nErrCode = ERROR_SUCCESS;
  940. // In some cases, people pass in two pointers to the same
  941. // buffer. This will hose GetFullPathName, so if they
  942. // are the same, then make a copy before calling GetFullPathName.
  943. if (pszTheFile==pof->szPathName) {
  944. FillMemory( szTemp, sizeof(szTemp), 0 );
  945. lstrcpy( szTemp, pszTheFile );
  946. pszNewFile = szTemp;
  947. }
  948. else
  949. {
  950. pszNewFile = pszTheFile;
  951. }
  952. GetFullPathName( pszNewFile, ARRAYSIZE(pof->szPathName),
  953. pof->szPathName, &lpDummy );
  954. }
  955. }
  956. return hf;
  957. }
  958. /** PifMgr_OpenProperties - return handle to property info for application
  959. *
  960. * INPUT
  961. * lpszApp -> name of application
  962. * lpszPIF -> name of PIF file to use/create
  963. * hInf = Inf handle, 0 if none, -1 to inhibit Inf processing
  964. * flOpt = OPENPROPS_RAWIO to allow raw file updates; otherwise, 0
  965. *
  966. * OUTPUT
  967. * handle to properties, FALSE if could not be opened, or out of memory
  968. *
  969. * REMARKS
  970. * This should not be thought of as a function that opens a file somewhere
  971. * on the disk (although that's usually the effect), but rather as an
  972. * property structure allocator that is optionally initialized by disk data
  973. * (currently, the file does not even remain open after this call). So the
  974. * main reason for failure in this function will be either a low memory
  975. * condition *or* inability to open a specific PIF file.
  976. *
  977. * The rules for PIF file searching are as follows:
  978. *
  979. * If not a .PIF file:
  980. * Search in current directory.
  981. * Endif.
  982. *
  983. * If path components were specified:
  984. * Search in specified directory.
  985. * Endif.
  986. *
  987. * Search in PIF directory.
  988. * Search the path.
  989. *
  990. * Note that this differs from the Windows 3.1 PIF search algorithm, which
  991. * was...
  992. *
  993. * Search current directory.
  994. * Search the path.
  995. * Search in application directory.
  996. *
  997. * This was a really bogus search order. Fortunately, it seems that
  998. * very few people relied on it.
  999. *
  1000. * Things to watch out for when dorking the PIF file search order:
  1001. *
  1002. * Make sure editing PIF properties from the shell works. (I.e.,
  1003. * if a full path to a PIF is given, then use it; don't search.)
  1004. *
  1005. * Extra special thing to watch out for when dorking the PIF file
  1006. * search order:
  1007. *
  1008. * MS Delta execs its child process as follows:
  1009. *
  1010. * CreatePif("C:\DELTA\DELTABAT.PIF");
  1011. * SetCurrentDirectory("C:\RANDOM\PLACE");
  1012. * WinExec("C:\TMP\DELTABAT.BAT", SW_HIDE);
  1013. *
  1014. * It expects the PIF search to pick up C:\DELTA\DELTABAT.PIF
  1015. * from the path, even though the WinExec supplied a full path.
  1016. *
  1017. */
  1018. HANDLE WINAPI PifMgr_OpenProperties(LPCTSTR lpszApp, LPCTSTR lpszPIF, UINT hInf, UINT flOpt)
  1019. {
  1020. PPROPLINK ppl;
  1021. LPTSTR pszExt;
  1022. BOOL fError = FALSE;
  1023. BOOL fFixedDisk = FALSE;
  1024. BOOL fSearchInf = FALSE;
  1025. BOOL fExplicitPIF = FALSE;
  1026. PROPPRG prg;
  1027. PROPNT40 nt40;
  1028. LPTSTR pszName, pszFullName;
  1029. #ifdef DBCS
  1030. PROPENV env;
  1031. #endif
  1032. FunctionName(PifMgr_OpenProperties);
  1033. // Allocate new prop
  1034. if (!(ppl = (PPROPLINK)LocalAlloc(LPTR, sizeof(PROPLINK))))
  1035. return 0;
  1036. if (!(pszFullName = (LPTSTR)LocalAlloc(LPTR, MAXPATHNAME*sizeof(TCHAR)))) {
  1037. EVAL(LocalFree(ppl) == NULL);
  1038. return 0;
  1039. }
  1040. if ((HANDLE)ppl > g_offHighestPropLink) {
  1041. g_offHighestPropLink = (HANDLE)ppl;
  1042. }
  1043. // Initialize the new prop
  1044. ppl->ppl = ppl;
  1045. ppl->ckbMem = -1;
  1046. ppl->iSig = PROP_SIG;
  1047. ppl->hPIF = INVALID_HANDLE_VALUE;
  1048. if (flOpt & OPENPROPS_RAWIO)
  1049. ppl->flProp |= PROP_RAWIO;
  1050. #if (PRGINIT_INHIBITPIF != PROP_INHIBITPIF)
  1051. #error PRGINIT_INIHIBITPIF and PROP_INHIBITPIF out of sync!
  1052. #endif
  1053. ppl->flProp |= (flOpt & PROP_INHIBITPIF);
  1054. // Link into the global list
  1055. if (NULL != (ppl->pplNext = g_pplHead))
  1056. g_pplHead->pplPrev = ppl;
  1057. g_pplHead = ppl;
  1058. // Copy app name to both temp and perm buffers, and record location
  1059. // of base filename, and extension if any, within the buffer
  1060. lstrcpyn(pszFullName, lpszApp, MAXPATHNAME-4);
  1061. lstrcpyn(ppl->szPathName, pszFullName, ARRAYSIZE(ppl->szPathName));
  1062. if (NULL != (pszName = StrRChr(pszFullName, NULL, TEXT('\\'))) ||
  1063. NULL != (pszName = StrRChr(pszFullName, NULL, TEXT(':'))))
  1064. pszName++;
  1065. else
  1066. pszName = pszFullName;
  1067. if (!(pszExt = StrRChr(pszName, NULL, TEXT('.'))))
  1068. pszExt = pszFullName + lstrlen(pszFullName);
  1069. ppl->iFileName = (UINT) (pszName - pszFullName);
  1070. ppl->iFileExt = (UINT) (pszExt - pszFullName);
  1071. // Check the application's file extension
  1072. if (!*pszExt) {
  1073. lstrcat(pszFullName, apszAppType[APPTYPE_PIF]);
  1074. }
  1075. else if (!lstrcmpi(pszExt, apszAppType[APPTYPE_EXE]) ||
  1076. !lstrcmpi(pszExt, apszAppType[APPTYPE_COM]) ||
  1077. !lstrcmpi(pszExt, apszAppType[APPTYPE_BAT])) {
  1078. // !lstrcmpi(pszExt, apszAppType[APPTYPE_CMD])) {
  1079. lstrcpy(pszExt, apszAppType[APPTYPE_PIF]);
  1080. }
  1081. else if (!lstrcmpi(pszExt, apszAppType[APPTYPE_PIF]))
  1082. fExplicitPIF = TRUE;
  1083. else {
  1084. // Let's disallow random file extensions, since WinOldAp never
  1085. // allowed them either
  1086. goto Error;
  1087. }
  1088. // INFONLY means the caller just wants to search the INF, so ignore
  1089. // any WIN.INI garbage and any PIFs laying around. We still look for
  1090. // _DEFAULT.PIF, since that code takes care of other important
  1091. // initialization that needs to happen when no PIF was found at all.
  1092. if (flOpt & OPENPROPS_INFONLY)
  1093. goto FindDefault;
  1094. // Backward compatibility requires that if the app is not a PIF,
  1095. // then we must check the PIF section of WIN.INI for an entry matching
  1096. // the base name of the app. If the entry exists, then we have to skip
  1097. // the PIF search, and pass the value of the entry to InitProperties,
  1098. // which it uses to establish default memory requirements
  1099. //
  1100. // Also note that if IGNOREPIF is set, then ofPIF.szPathName is nothing
  1101. // more than the name of the app that was given to PifMgr_OpenProperties; this
  1102. // may give us the opportunity to do something more intelligent later...
  1103. if (!fExplicitPIF) {
  1104. ppl->ckbMem = GetProfileInt(apszAppType[APPTYPE_PIF]+1, ppl->szPathName+ppl->iFileName, -1);
  1105. if (ppl->ckbMem != -1) {
  1106. ppl->flProp |= PROP_IGNOREPIF | PROP_SKIPPIF;
  1107. lstrcpyn(ppl->ofPIF.szPathName, lpszApp, ARRAYSIZE(ppl->ofPIF.szPathName));
  1108. goto IgnorePIF; // entry exists, skip PIF file search
  1109. }
  1110. }
  1111. //
  1112. // Initialize default error return code. Once we get a successful
  1113. // open, it will be set to zero.
  1114. //
  1115. ppl->flProp |= PROP_NOCREATEPIF;
  1116. ppl->ofPIF.nErrCode = ERROR_FILE_NOT_FOUND;
  1117. //
  1118. // We must search in the current directory if not given a path to a PIF.
  1119. // We need to prefix `.\' to the filename so that OpenFile will not do
  1120. // a path search.
  1121. //
  1122. if (!fExplicitPIF || pszName == pszFullName) {
  1123. //
  1124. // This relies on a feature of OpenFile, that it copies the input
  1125. // buffer to a private buffer before stomping the output buffer,
  1126. // thus permitting precisely the stunt we are pulling here, namely,
  1127. // passing an input buffer equal to the output buffer.
  1128. //
  1129. *(LPDWORD)(ppl->ofPIF.szPathName) = 0x005C002E; /*dot backslash prefix */
  1130. lstrcpyn( &ppl->ofPIF.szPathName[2],
  1131. pszName,
  1132. ARRAYSIZE(ppl->ofPIF.szPathName) - 2);
  1133. ppl->hPIF = OpenPIFFile(ppl->ofPIF.szPathName, &ppl->ofPIF);
  1134. }
  1135. //
  1136. // If we were given a path component, then look in that directory.
  1137. // (The fact that we have a backslash or drive letter will suppress
  1138. // the path search.)
  1139. //
  1140. if (pszName != pszFullName && ppl->ofPIF.nErrCode == ERROR_FILE_NOT_FOUND) {
  1141. ppl->hPIF = OpenPIFFile(pszFullName, &ppl->ofPIF);
  1142. // If we didn't find a PIF there, we'd probably still like to create
  1143. // one there if the media is a fixed disk. Network shares, CD-ROM
  1144. // drives, and floppies are not good targets for PIF files in general.
  1145. //
  1146. // So, if the media is a fixed disk, set the fFixedDisk flag so that
  1147. // we'll leave pszFullName alone.
  1148. if (ppl->hPIF == INVALID_HANDLE_VALUE && pszFullName[1] == TEXT(':')) {
  1149. TCHAR szTemp[4];
  1150. lstrcpyn( szTemp, pszFullName, 4 );
  1151. szTemp[3] = (TCHAR)0;
  1152. if (GetDriveType(szTemp) == DRIVE_FIXED)
  1153. fFixedDisk++;
  1154. }
  1155. }
  1156. // PERF: replace this PIF dir search with a registry search -JTP
  1157. //
  1158. // Failing that, let's look in the PIF directory. Again, since we're
  1159. // supplying a full pathname, OpenFile won't try to search the PATH again.
  1160. if (ppl->ofPIF.nErrCode == ERROR_FILE_NOT_FOUND) {
  1161. GetPIFDir(pszName);
  1162. ppl->hPIF = OpenPIFFile(g_szPIFDir, &ppl->ofPIF);
  1163. if (ppl->hPIF != INVALID_HANDLE_VALUE)
  1164. ppl->flProp |= PROP_PIFDIR;
  1165. }
  1166. // If we're still in trouble, our last chance is to do a path
  1167. // search. This is an unconditional search, thanks to the
  1168. // wonders of MS-Delta.
  1169. if (ppl->ofPIF.nErrCode == ERROR_FILE_NOT_FOUND) {
  1170. ppl->hPIF = OpenPIFFile(pszName, &ppl->ofPIF);
  1171. }
  1172. if (ppl->hPIF == INVALID_HANDLE_VALUE) {
  1173. if (ppl->ofPIF.nErrCode != ERROR_FILE_NOT_FOUND || fExplicitPIF) {
  1174. // Hmmm, file *may* exist, but it cannot be opened; if it's a
  1175. // strange error, or we were specifically told to open that file,
  1176. // then return error
  1177. goto Error;
  1178. }
  1179. FindDefault:
  1180. fSearchInf = TRUE;
  1181. ppl->flProp &= ~PROP_NOCREATEPIF;
  1182. // Any files we find now are NOT really what we wanted, so save
  1183. // the name we'd like to use in the future, in case we need to save
  1184. // updated properties later.
  1185. //
  1186. // We must save the name now because we might stomp g_szPIFDir while
  1187. // searching for the _default.pif. Furthermore, we must save it in
  1188. // the buffer we HeapAlloc'ed (pszFullName) temporarily, because
  1189. // the following calls to OpenPIFFile can still stomp on szPathName
  1190. // in our OpenFile structure (ofPIF.szPathName).
  1191. GetPIFDir(pszName);
  1192. if (!fFixedDisk) // save desired name in
  1193. lstrcpy(pszFullName, g_szPIFDir); // temp buffer (pszFullName)
  1194. //
  1195. // Try to locate the _default.pif.
  1196. //
  1197. if (fTryDefaultPif()) {
  1198. if (!fDefaultPifFound()) { // Must search for it
  1199. // First try PIFDir
  1200. lstrcpy(g_szPIFDir+iPIFName, g_szDefaultPIF);
  1201. ppl->hPIF = OpenPIFFile(g_szPIFDir, &ppl->ofPIF);
  1202. if (ppl->ofPIF.nErrCode == ERROR_FILE_NOT_FOUND) { // try PATH
  1203. ppl->hPIF = OpenPIFFile(g_szDefaultPIF, &ppl->ofPIF);
  1204. }
  1205. } else { // Look in cached path
  1206. // We've already found it once, so just open it
  1207. ppl->hPIF = OpenPIFFile(g_szDefaultPIF, &ppl->ofPIF);
  1208. }
  1209. }
  1210. if (ppl->hPIF != INVALID_HANDLE_VALUE) {
  1211. ppl->flProp |= PROP_DEFAULTPIF;
  1212. // Save the fully-qualified pathname of the default PIF file,
  1213. // so that subsequent OpenFile() calls will be faster (note that
  1214. // we don't specify OF_SEARCH on that particular call)
  1215. lstrcpy(g_szDefaultPIF, ppl->ofPIF.szPathName);
  1216. }
  1217. else {
  1218. // Not only could we not open any sort of PIF, we also need to
  1219. // tell GetPIFData to not bother trying to open the file itself
  1220. // (since it is unlikely someone created one in this short time)
  1221. ppl->flProp |= PROP_NOPIF | PROP_SKIPPIF;
  1222. if (ppl->ofPIF.nErrCode == ERROR_FILE_NOT_FOUND)
  1223. g_szDefaultPIF[0] = 0; // Invalidate cache.
  1224. }
  1225. // NOW we can set ppl->ofPIF.szPathName to the filename we REALLY
  1226. // wanted, since we're done with all the calls to OpenPIFFile.
  1227. lstrcpy(ppl->ofPIF.szPathName, pszFullName);
  1228. }
  1229. // Initialize the properties by PIF if we have one, by hand if not
  1230. IgnorePIF:
  1231. // We don't need to check the return code from GetPIFData() here,
  1232. // because we validate hPIFData below anyway. Please also note that
  1233. // this GetPIFData call uses the handle we supplied (if any), and closes
  1234. // it for us when it's done. Furthermore, if we didn't supply a handle,
  1235. // then we should have set PROP_SKIPPIF, so that GetPIFData won't try to
  1236. // open anything (since we just tried!)
  1237. GetPIFData(ppl, FALSE);
  1238. // Now that the original file from which we obtained settings (if any) is
  1239. // closed, we need to see if the caller wants us to create a new PIF file
  1240. // using a specific name. If so, force it to be created now.
  1241. if (lpszPIF) {
  1242. lstrcpy(ppl->ofPIF.szPathName, lpszPIF);
  1243. ppl->flProp |= PROP_DIRTY;
  1244. ppl->flProp &= ~PROP_NOCREATEPIF;
  1245. fError = !FlushPIFData(ppl, FALSE);
  1246. }
  1247. // Apply INF data to the PIF data we just retrieved, as appropriate,
  1248. // as long as it's an app file and not a PIF file (and if, in the case of
  1249. // creating a specific PIF, we were actually able to create one).
  1250. if (!fError && !fExplicitPIF && (hInf != -1)) {
  1251. if (PifMgr_GetProperties(ppl, MAKELP(0,GROUP_PRG),
  1252. &prg, sizeof(prg), GETPROPS_NONE)) {
  1253. // In the PRGINIT_AMBIGUOUSPIF case, GetAppsInfData must
  1254. // again look for a matching entry; however, if the entry it
  1255. // finds is the same as what we've already got (based on Other
  1256. // File), then it will leave the PIF data alone (ie, it doesn't
  1257. // reinitialize it, it doesn't call AppWiz to silently
  1258. // reconfigure it, etc).
  1259. if (fSearchInf || (prg.flPrgInit & PRGINIT_AMBIGUOUSPIF)) {
  1260. if (PifMgr_GetProperties(ppl, MAKELP(0,GROUP_NT40),
  1261. &nt40, sizeof(nt40), GETPROPS_NONE)) {
  1262. if (!GetAppsInfData(ppl, &prg, &nt40, (HINF)IntToPtr( hInf ), lpszApp, fFixedDisk, flOpt)) {
  1263. // When GetAppsInfData fails and the PIF is ambiguous, then
  1264. // we need to restart the PIF search process at the point where
  1265. // it searches for _DEFAULT.PIF, so that the ambiguous PIF is
  1266. // effectively ignored now.
  1267. // Also, we avoid the ugly possibility of getting to this
  1268. // point again and infinitely jumping back FindDefault, by
  1269. // only jumping if fSearchInf was FALSE. FindDefault sets
  1270. // it to TRUE.
  1271. if (!fSearchInf && (prg.flPrgInit & PRGINIT_AMBIGUOUSPIF)) {
  1272. goto FindDefault;
  1273. }
  1274. #ifdef DBCS
  1275. if (GetSystemDefaultLangID() == 0x0411) {
  1276. ZeroMemory(&env, sizeof(env));
  1277. lstrcpyA(env.achBatchFile, ImeBatchFile);
  1278. PifMgr_SetProperties(ppl, MAKELP(0,GROUP_ENV),
  1279. &env, sizeof(env), SETPROPS_NONE);
  1280. }
  1281. #endif
  1282. }
  1283. }
  1284. }
  1285. }
  1286. }
  1287. Error:
  1288. LocalFree(pszFullName);
  1289. if (fError || !ppl->lpPIFData) {
  1290. PifMgr_CloseProperties(ppl, 0);
  1291. return 0;
  1292. }
  1293. // We should never leave PIFMGR with outstanding locks
  1294. ASSERTTRUE(!ppl->cLocks);
  1295. return ppl;
  1296. }
  1297. /** PifMgr_GetProperties - get property info by name
  1298. *
  1299. * INPUT
  1300. * hProps = handle to properties
  1301. * lpszGroup -> property group; may be one of the following:
  1302. * "WINDOWS 286 3.0"
  1303. * "WINDOWS 386 3.0"
  1304. * "WINDOWS VMM 4.0"
  1305. * "WINDOWS NT 3.1"
  1306. * "WINDOWS NT 4.0"
  1307. * or any other group name that is the name of a valid PIF extension;
  1308. * if NULL, then cbProps is a 0-based index of a named group, and lpProps
  1309. * must point to a 16-byte buffer to receive the name of the group (this
  1310. * enables the caller to enumerate the names of all the named groups)
  1311. * lpProps -> property group record to receive the data
  1312. * cbProps = size of property group record to get; if cbProps is zero
  1313. * and a named group is requested, lpProps is ignored, no data is copied,
  1314. * and the size of the group record is returned (this enables the caller
  1315. * to determine the size of a named group)
  1316. * flOpt = GETPROPS_RAWIO to perform raw file read (lpszGroup ignored)
  1317. *
  1318. * Alternatively, if the high word (selector) of lpszGroup is 0, the low
  1319. * word must be a group ordinal (eg, GROUP_PRG, GROUP_TSK, etc)
  1320. *
  1321. * OUTPUT
  1322. * If the group is not found, or an error occurs, 0 is returned.
  1323. * Otherwise, the size of the group info transferred in bytes is returned.
  1324. */
  1325. int WINAPI PifMgr_GetProperties(HANDLE hProps, LPCSTR lpszGroup, void *lpProps, int cbProps, UINT flOpt)
  1326. {
  1327. int cb, i;
  1328. void *lp;
  1329. LPW386PIF30 lp386;
  1330. LPWENHPIF40 lpenh;
  1331. LPWNTPIF40 lpnt40;
  1332. LPWNTPIF31 lpnt31;
  1333. PPROPLINK ppl;
  1334. FunctionName(PifMgr_GetProperties);
  1335. cb = 0;
  1336. if (!(ppl = ValidPropHandle(hProps)))
  1337. return cb;
  1338. // We should never enter PIFMGR with outstanding locks (we also call
  1339. // here from *inside* PIFMGR, but none of those cases should require a
  1340. // lock either)
  1341. ASSERTTRUE(!ppl->cLocks);
  1342. ppl->cLocks++;
  1343. if (flOpt & GETPROPS_RAWIO) {
  1344. if (ppl->flProp & PROP_RAWIO) {
  1345. cb = min(ppl->cbPIFData, cbProps);
  1346. hmemcpy(lpProps, ppl->lpPIFData, cb);
  1347. }
  1348. ppl->cLocks--;
  1349. return cb;
  1350. }
  1351. if (!lpszGroup) {
  1352. if (lpProps) {
  1353. lp = GetGroupData(ppl, NULL, &cbProps, NULL);
  1354. if (lp) {
  1355. cb = cbProps;
  1356. hmemcpy(lpProps, lp, PIFEXTSIGSIZE);
  1357. }
  1358. }
  1359. }
  1360. else if (IS_INTRESOURCE(lpszGroup) && lpProps) {
  1361. // Special case: if GROUP_ICON, then do a nested call to
  1362. // PifMgr_GetProperties to get GROUP_PRG data, then feed it to load
  1363. // LoadPIFIcon, and finally return the hIcon, if any, to the user.
  1364. if (LOWORD((DWORD_PTR) lpszGroup) == GROUP_ICON) {
  1365. PPROPPRG pprg;
  1366. PPROPNT40 pnt40 = (void *)LocalAlloc(LPTR, sizeof(PROPNT40));
  1367. if ( pnt40 ) {
  1368. pprg = (void *)LocalAlloc(LPTR, sizeof(PROPPRG));
  1369. if (pprg) {
  1370. if ( PifMgr_GetProperties(ppl, MAKELP(0,GROUP_PRG), pprg, sizeof(PROPPRG), GETPROPS_NONE)
  1371. && PifMgr_GetProperties(ppl, MAKELP(0,GROUP_NT40), pnt40, sizeof(PROPNT40), GETPROPS_NONE) ) {
  1372. *(HICON *)lpProps = LoadPIFIcon(pprg, pnt40);
  1373. cb = 2;
  1374. }
  1375. EVAL(LocalFree(pprg) == NULL);
  1376. }
  1377. EVAL(LocalFree(pnt40) == NULL);
  1378. }
  1379. }
  1380. else {
  1381. lp386 = GetGroupData(ppl, szW386HDRSIG30, NULL, NULL);
  1382. lpenh = GetGroupData(ppl, szWENHHDRSIG40, NULL, NULL);
  1383. lpnt40 = GetGroupData(ppl, szWNTHDRSIG40, NULL, NULL);
  1384. lpnt31 = GetGroupData(ppl, szWNTHDRSIG31, NULL, NULL);
  1385. //
  1386. // Fix anything from down-level PIF files. Since this
  1387. // is the first revision of the WENHPIF40 format, we
  1388. // don't have anything to worry about (yet).
  1389. //
  1390. // Don't muck with PIF files from the future!
  1391. //
  1392. if (lpenh && lpenh->wInternalRevision != WENHPIF40_VERSION) {
  1393. lpenh->wInternalRevision = WENHPIF40_VERSION;
  1394. ppl->flProp |= PROP_DIRTY;
  1395. //
  1396. // Old (pre-M7) PIFs did not zero-initialize the reserved
  1397. // fields of PIF files, so zero them out now.
  1398. //
  1399. lpenh->tskProp.wReserved1 = 0;
  1400. lpenh->tskProp.wReserved2 = 0;
  1401. lpenh->tskProp.wReserved3 = 0;
  1402. lpenh->tskProp.wReserved4 = 0;
  1403. lpenh->vidProp.wReserved1 = 0;
  1404. lpenh->vidProp.wReserved2 = 0;
  1405. lpenh->vidProp.wReserved3 = 0;
  1406. lpenh->envProp.wMaxDPMI = 0;
  1407. // Turn off bits that have been deleted during the development
  1408. // cycle.
  1409. lpenh->envProp.flEnv = 0;
  1410. lpenh->envProp.flEnvInit = 0;
  1411. if (lp386)
  1412. lp386->PfW386Flags &= ~0x00400000;
  1413. }
  1414. // End of "Remove this after M8"
  1415. // Zero the input buffer first, so that the Get* functions
  1416. // need not initialize every byte to obtain consistent results
  1417. BZero(lpProps, cbProps);
  1418. // The GetData functions CANNOT rely on either lp386 or lpenh
  1419. i = LOWORD((DWORD_PTR) lpszGroup)-1;
  1420. if (i >= 0 && i < ARRAYSIZE(afnGetData) && cbProps >= acbData[i]) {
  1421. void *aDataPtrs[NUM_DATA_PTRS];
  1422. aDataPtrs[ LP386_INDEX ] = (LPVOID)lp386;
  1423. aDataPtrs[ LPENH_INDEX ] = (LPVOID)lpenh;
  1424. aDataPtrs[ LPNT40_INDEX ] = (LPVOID)lpnt40;
  1425. aDataPtrs[ LPNT31_INDEX ] = (LPVOID)lpnt31;
  1426. cb = (afnGetData[i])(ppl, aDataPtrs, lpProps, cbProps, flOpt );
  1427. }
  1428. }
  1429. }
  1430. else if (NULL != (lp = GetGroupData(ppl, lpszGroup, &cb, NULL))) {
  1431. if (lpProps && cbProps != 0) {
  1432. cb = min(cb, cbProps);
  1433. hmemcpy(lpProps, lp, cb);
  1434. }
  1435. }
  1436. ppl->cLocks--;
  1437. #ifdef EXTENDED_DATA_SUPPORT
  1438. // Note that for GETPROPS_EXTENDED, both the normal and extended
  1439. // sections are returned, and that the return code reflects the success
  1440. // or failure of reading the normal portion only. We return both because
  1441. // that's the most convenient thing to do for the caller.
  1442. if (flOpt & GETPROPS_EXTENDED) {
  1443. if (ppl->hVM) {
  1444. WORD wGroup = EXT_GROUP_QUERY;
  1445. if (!HIWORD(lpszGroup) && LOWORD(lpszGroup) <= MAX_GROUP)
  1446. wGroup |= LOWORD(lpszGroup);
  1447. GetSetExtendedData(ppl->hVM, wGroup, lpszGroup, lpProps);
  1448. }
  1449. }
  1450. #endif
  1451. // We should never leave PIFMGR with outstanding locks (we also call
  1452. // here from *inside* PIFMGR, but none of those cases should require a
  1453. // lock either)
  1454. ASSERTTRUE(!ppl->cLocks);
  1455. return cb;
  1456. }
  1457. /** PifMgr_SetProperties - set property info by name
  1458. *
  1459. * INPUT
  1460. * hProps = handle to properties
  1461. * lpszGroup -> property group; may be one of the following:
  1462. * "WINDOWS 286 3.0"
  1463. * "WINDOWS 386 3.0"
  1464. * "WINDOWS PIF.400"
  1465. * or any other group name that is the name of a valid PIF extension
  1466. * lpProps -> property group record to copy the data from
  1467. * cbProps = size of property group record to set; if cbProps is
  1468. * zero and lpszGroup is a group name, the group will be removed
  1469. * flOpt = SETPROPS_RAWIO to perform raw file write (lpszGroup ignored)
  1470. * SETPROPS_CACHE to cache changes until properties are closed
  1471. *
  1472. * Alternatively, if the high word (selector) of lpszGroup is 0, the low
  1473. * word must be a group ordinal (eg, GROUP_PRG, GROUP_TSK, etc)
  1474. *
  1475. * OUTPUT
  1476. * If the group is not found, or an error occurs, 0 is returned.
  1477. * Otherwise, the size of the group info transferred in bytes is returned.
  1478. */
  1479. int WINAPI PifMgr_SetProperties(HANDLE hProps, LPCSTR lpszGroup, void *lpProps, int cbProps, UINT flOpt)
  1480. {
  1481. void *p = NULL;
  1482. void *lp = NULL;
  1483. LPW386PIF30 lp386;
  1484. LPWENHPIF40 lpenh;
  1485. LPWNTPIF40 lpnt40;
  1486. LPWNTPIF31 lpnt31;
  1487. int i, cb = 0;
  1488. PPROPLINK ppl;
  1489. FunctionName(PifMgr_SetProperties);
  1490. // Can't set a NULL name (nor set-by-index)--causes squirlly behavior in RemoveGroupData
  1491. if (!lpProps || !lpszGroup)
  1492. return 0;
  1493. ppl = ValidPropHandle(hProps);
  1494. if (!ppl)
  1495. return 0;
  1496. // We should never enter PIFMGR with outstanding locks (we also call
  1497. // here from *inside* PIFMGR, but none of those cases should require a
  1498. // lock either)
  1499. ASSERTTRUE(!ppl->cLocks);
  1500. if (flOpt & SETPROPS_RAWIO) {
  1501. if (ppl->flProp & PROP_RAWIO) {
  1502. ppl->cLocks++;
  1503. cb = min(ppl->cbPIFData, cbProps);
  1504. if (IsBufferDifferent(ppl->lpPIFData, lpProps, cb)) {
  1505. hmemcpy(ppl->lpPIFData, lpProps, cb);
  1506. ppl->flProp |= PROP_DIRTY;
  1507. }
  1508. if (cb < ppl->cbPIFData)
  1509. ppl->flProp |= PROP_DIRTY | PROP_TRUNCATE;
  1510. ppl->cbPIFData = cb;
  1511. ppl->cLocks--;
  1512. }
  1513. return cb;
  1514. }
  1515. #ifdef EXTENDED_DATA_SUPPORT
  1516. // Note that, unlike GETPROPS_EXTENDED, SETPROPS_EXTENDED only updates
  1517. // the extended section, and that the return code reflects the existence
  1518. // of a VM only. This is because there's a performance hit associated
  1519. // with setting the normal portion, and because the caller generally only
  1520. // wants to set one or the other.
  1521. if (flOpt & SETPROPS_EXTENDED) {
  1522. if (ppl->hVM) {
  1523. WORD wGroup = EXT_GROUP_UPDATE;
  1524. cb = cbProps;
  1525. if (!HIWORD(lpszGroup) && LOWORD(lpszGroup) <= MAX_GROUP)
  1526. wGroup |= LOWORD(lpszGroup);
  1527. GetSetExtendedData(ppl->hVM, wGroup, lpszGroup, lpProps);
  1528. }
  1529. return cb;
  1530. }
  1531. #endif
  1532. // For named groups, if the group does NOT exist, or DOES but is
  1533. // a different size, then we have to remove the old data, if any, and
  1534. // then add the new.
  1535. if (!IS_INTRESOURCE(lpszGroup)) {
  1536. cb = PifMgr_GetProperties(hProps, lpszGroup, NULL, 0, GETPROPS_NONE);
  1537. if (cb == 0 || cb != cbProps) {
  1538. if (cb) {
  1539. RemoveGroupData(ppl, lpszGroup);
  1540. cb = 0;
  1541. }
  1542. if (cbProps) {
  1543. if (AddGroupData(ppl, lpszGroup, lpProps, cbProps))
  1544. cb = cbProps;
  1545. }
  1546. goto done;
  1547. }
  1548. }
  1549. if (cbProps) {
  1550. if (!lpszGroup)
  1551. return cb;
  1552. p = (void *)LocalAlloc(LPTR, cbProps);
  1553. if (!p)
  1554. return cb;
  1555. }
  1556. cb = PifMgr_GetProperties(hProps, lpszGroup, p, cbProps, GETPROPS_NONE);
  1557. // If the group to set DOES exist, and if the data given is
  1558. // different, copy into the appropriate group(s) in the PIF data
  1559. if (cb != 0) {
  1560. cbProps = min(cb, cbProps);
  1561. if (IsBufferDifferent(p, lpProps, cbProps)) {
  1562. cb = 0;
  1563. ppl->cLocks++;
  1564. i = LOWORD((DWORD_PTR) lpszGroup)-1;
  1565. if (!IS_INTRESOURCE(lpszGroup)) {
  1566. lp = GetGroupData(ppl, lpszGroup, NULL, NULL);
  1567. if (lp) {
  1568. cb = cbProps;
  1569. hmemcpy(lp, lpProps, cbProps);
  1570. ppl->flProp |= PROP_DIRTY;
  1571. }
  1572. }
  1573. else if (i >= 0 && i < ARRAYSIZE(afnSetData) && cbProps >= acbData[i]) {
  1574. // Insure that both 386 and enhanced sections of PIF
  1575. // file are present. There are some exceptions: all
  1576. // groups from GROUP_MSE on up do not use the 386 section,
  1577. // and GROUP_MEM does not need the enh section....
  1578. lp386 = GetGroupData(ppl, szW386HDRSIG30, NULL, NULL);
  1579. if (i < GROUP_MSE-1 && !lp386) {
  1580. if (AddGroupData(ppl, szW386HDRSIG30, NULL, sizeof(W386PIF30))) {
  1581. lp386 = GetGroupData(ppl, szW386HDRSIG30, NULL, NULL);
  1582. if (!lp386) {
  1583. ASSERTFAIL();
  1584. cbProps = 0; // indicate error
  1585. }
  1586. }
  1587. }
  1588. if (cbProps) {
  1589. lpenh = GetGroupData(ppl, szWENHHDRSIG40, NULL, NULL);
  1590. if (i != GROUP_MEM-1 && !lpenh) {
  1591. if (!(lpenh = AddEnhancedData(ppl, lp386))) {
  1592. ASSERTFAIL();
  1593. cbProps = 0; // indicate error
  1594. }
  1595. }
  1596. lpnt40 = GetGroupData(ppl, szWNTHDRSIG40, NULL, NULL);
  1597. if (!lpnt40)
  1598. {
  1599. if (AddGroupData(ppl, szWNTHDRSIG40, NULL, sizeof(WNTPIF40)))
  1600. {
  1601. lpnt40 = GetGroupData(ppl, szWNTHDRSIG40, NULL, NULL);
  1602. }
  1603. }
  1604. ASSERT(lpnt40);
  1605. lpnt31 = GetGroupData(ppl, szWNTHDRSIG31, NULL, NULL);
  1606. if (!lpnt31)
  1607. {
  1608. if (AddGroupData(ppl, szWNTHDRSIG31, NULL, sizeof(WNTPIF31)))
  1609. {
  1610. if (NULL != (lpnt31 = GetGroupData(ppl, szWNTHDRSIG31, NULL, NULL))) {
  1611. lstrcpyA( lpnt31->nt31Prop.achConfigFile, NT_CONFIG_FILE );
  1612. lstrcpyA( lpnt31->nt31Prop.achAutoexecFile, NT_AUTOEXEC_FILE );
  1613. }
  1614. }
  1615. }
  1616. ASSERT(lpnt31);
  1617. }
  1618. if (cbProps)
  1619. {
  1620. void *aDataPtrs[NUM_DATA_PTRS];
  1621. //
  1622. // We need to re-establish the pointers because any of
  1623. // the AddGroupData's could have moved the block (via
  1624. // a HeapReAlloc call), so do that now...
  1625. //
  1626. lp386 = GetGroupData( ppl, szW386HDRSIG30, NULL, NULL );
  1627. lpenh = GetGroupData( ppl, szWENHHDRSIG40, NULL, NULL );
  1628. lpnt40 = GetGroupData( ppl, szWNTHDRSIG40, NULL, NULL );
  1629. lpnt31 = GetGroupData( ppl, szWNTHDRSIG31, NULL, NULL );
  1630. aDataPtrs[ LP386_INDEX ] = (LPVOID)lp386;
  1631. aDataPtrs[ LPENH_INDEX ] = (LPVOID)lpenh;
  1632. aDataPtrs[ LPNT40_INDEX ] = (LPVOID)lpnt40;
  1633. aDataPtrs[ LPNT31_INDEX ] = (LPVOID)lpnt31;
  1634. cb = (afnSetData[i])(ppl, aDataPtrs, lpProps, cbProps, flOpt );
  1635. }
  1636. }
  1637. ppl->cLocks--;
  1638. }
  1639. }
  1640. EVAL(LocalFree(p) == NULL);
  1641. done:
  1642. if (!(flOpt & SETPROPS_CACHE))
  1643. if (!FlushPIFData(ppl, FALSE))
  1644. cb = 0;
  1645. // We should never leave PIFMGR with outstanding locks (we also call
  1646. // here from *inside* PIFMGR, but none of those cases should require a
  1647. // lock either)
  1648. ASSERTTRUE(!ppl->cLocks);
  1649. return cb;
  1650. }
  1651. /** FlushProperties - flush (or discard) any cached property info
  1652. *
  1653. * INPUT
  1654. * hProps = handle to properties
  1655. * flOpt = FLUSHPROPS_DISCARD to abandon cached PIF data, otherwise flush it
  1656. *
  1657. * OUTPUT
  1658. * TRUE if successful, FALSE otherwise
  1659. */
  1660. int WINAPI FlushProperties(HANDLE hProps, UINT flOpt)
  1661. {
  1662. PPROPLINK ppl;
  1663. FunctionName(FlushProperties);
  1664. if (!(ppl = ValidPropHandle(hProps)))
  1665. return FALSE;
  1666. return FlushPIFData(ppl, (flOpt & FLUSHPROPS_DISCARD));
  1667. }
  1668. /** EnumProperties - enumerate open properties
  1669. *
  1670. * INPUT
  1671. * hProps = handle to previous properties (NULL to start)
  1672. *
  1673. * OUTPUT
  1674. * next property handle, 0 if none
  1675. */
  1676. HANDLE WINAPI EnumProperties(HANDLE hProps)
  1677. {
  1678. PPROPLINK ppl;
  1679. FunctionName(EnumProperties);
  1680. if (!hProps)
  1681. return g_pplHead;
  1682. if (!(ppl = ValidPropHandle(hProps)))
  1683. return NULL;
  1684. return ppl->pplNext;
  1685. }
  1686. /** AssociateProperties - associate data with property info
  1687. *
  1688. * INPUT
  1689. * hProps = handle to properties
  1690. * iAssociate = association index (eg, HVM_ASSOCIATION)
  1691. * lData = new associated data
  1692. *
  1693. * OUTPUT
  1694. * previously associated data for the index, 0 if none (or error)
  1695. *
  1696. * NOTES
  1697. * If iAssociate is a negative association index, then the current
  1698. * associated value is returned and not modified (ie, lData is ignored)
  1699. */
  1700. LONG_PTR WINAPI AssociateProperties(HANDLE hProps, int iAssociate, LONG_PTR lData)
  1701. {
  1702. LONG_PTR l;
  1703. int iIndex;
  1704. PPROPLINK ppl;
  1705. FunctionName(AssociateProperties);
  1706. if (!(ppl = ValidPropHandle(hProps)))
  1707. return FALSE;
  1708. iIndex = iAssociate;
  1709. if (iIndex < 0)
  1710. iIndex *= -1;
  1711. switch(iIndex) {
  1712. case HVM_ASSOCIATION:
  1713. l = ppl->hVM;
  1714. break;
  1715. case HWND_ASSOCIATION:
  1716. l = (LONG_PTR)ppl->hwndTty;
  1717. break;
  1718. case LPARGS_ASSOCIATION:
  1719. l = (LONG_PTR)ppl->lpArgs;
  1720. break;
  1721. default:
  1722. return FALSE;
  1723. }
  1724. switch(iAssociate) {
  1725. case HVM_ASSOCIATION:
  1726. ppl->hVM = (DWORD) lData;
  1727. break;
  1728. case HWND_ASSOCIATION:
  1729. ppl->hwndTty = (HWND)lData;
  1730. break;
  1731. case LPARGS_ASSOCIATION:
  1732. ppl->lpArgs = (LPTSTR)lData;
  1733. break;
  1734. }
  1735. return l;
  1736. }
  1737. void SetBootDrive(LPTSTR pszFile)
  1738. {
  1739. TCHAR szPath[10];
  1740. DWORD cbPath = sizeof(szPath);
  1741. szPath[0] = 0;
  1742. SHGetValue(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Setup"), TEXT("BootDir"), NULL, szPath, &cbPath);
  1743. pszFile[0] = szPath[0] ? szPath[0] : TEXT('C');
  1744. }
  1745. #define RENAME_DELETENEW 0x0001
  1746. #define RENAME_CREATEOLD 0x0002
  1747. #define RENAME_DELETEOLD 0x0004
  1748. /** RenameStartupFiles - rename startup files (for real-mode only)
  1749. *
  1750. * INPUT
  1751. * pszOld -> old filename (eg, C:CONFIG.SYS)
  1752. * pszNew -> new filename (eg, C:CONFIG.WOS)
  1753. * flRename == see RENAME_* above
  1754. *
  1755. * OUTPUT
  1756. * None
  1757. */
  1758. void RenameStartupFiles(LPTSTR pszOld, LPTSTR pszNew, int flRename)
  1759. {
  1760. SetBootDrive(pszOld);
  1761. SetBootDrive(pszNew);
  1762. // For RENAME_DELETENEW: make sure there's no way an existing new filename
  1763. // could block the rename process, not even a R/H/S file!
  1764. if (flRename & RENAME_DELETENEW) {
  1765. SetFileAttributes(pszNew, 0);
  1766. DeleteFile(pszNew);
  1767. }
  1768. // For RENAME_CREATEOLD: if the old filename doesn't exist, then we
  1769. // want to create a hidden 0-length copy of the new filename, as a way
  1770. // of telling ourselves later that the old file didn't exist. Then,
  1771. // when it's later (see RENAME_DELETEOLD), if the file we're to rename
  1772. // is a hidden 0-length file, just delete it.
  1773. if (flRename & RENAME_CREATEOLD) {
  1774. int i;
  1775. HANDLE hFile;
  1776. i = (int)GetFileAttributes(pszOld);
  1777. if (i == -1) {
  1778. hFile = CreateFile( pszNew,
  1779. GENERIC_READ | GENERIC_WRITE,
  1780. FILE_SHARE_READ,
  1781. NULL,
  1782. CREATE_ALWAYS,
  1783. FILE_ATTRIBUTE_HIDDEN,
  1784. NULL
  1785. );
  1786. if (hFile != INVALID_HANDLE_VALUE)
  1787. CloseHandle(hFile);
  1788. return;
  1789. }
  1790. }
  1791. // For RENAME_DELETEOLD: if the old file is a hidden 0-length
  1792. // dummy file that we presumably created ourselves, then simply delete it.
  1793. if (flRename & RENAME_DELETEOLD) {
  1794. WIN32_FIND_DATA dFind;
  1795. HANDLE hFind = FindFirstFile(pszOld, &dFind);
  1796. if (hFind != INVALID_HFINDFILE) {
  1797. FindClose(hFind);
  1798. if ((dFind.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) && dFind.nFileSizeLow == 0) {
  1799. DeleteFile(pszOld);
  1800. return;
  1801. }
  1802. }
  1803. }
  1804. MoveFile(pszOld, pszNew);
  1805. }
  1806. /** CheckForceReboot - return TRUE if exit to real-mode has been disabled
  1807. *
  1808. * This function largely copied from win\core\user\inexit.c -JTP
  1809. *
  1810. * The key being examined is:
  1811. *
  1812. * HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\Shutdown
  1813. *
  1814. * The value-name is:
  1815. *
  1816. * ForceReboot
  1817. *
  1818. * The value to force a reboot is:
  1819. *
  1820. * "1"
  1821. *
  1822. * INPUT
  1823. * None
  1824. *
  1825. * OUTPUT
  1826. * TRUE if reboot must be forced, FALSE if not
  1827. */
  1828. BOOL CheckForceReboot()
  1829. {
  1830. // Check to see if we have to force a reboot of the system. One reason is
  1831. // so that the double space utilities can tell us the system must be reboot
  1832. // since we can't exit to real mode double space...
  1833. HKEY hkey;
  1834. static TCHAR szShutdown[] = REGSTR_PATH_SHUTDOWN;
  1835. BOOL fForce=FALSE;
  1836. TCHAR sz[12];
  1837. DWORD dwType;
  1838. if (!RegOpenKey(HKEY_LOCAL_MACHINE, szShutdown, &hkey))
  1839. {
  1840. LONG cb = sizeof(sz);
  1841. static TCHAR szForceReboot[] = REGSTR_VAL_FORCEREBOOT;
  1842. if (!SHQueryValueEx(hkey, szForceReboot, NULL, &dwType, (LPBYTE)sz, &cb))
  1843. {
  1844. if (*sz==TEXT('1'))
  1845. fForce = TRUE;
  1846. }
  1847. RegCloseKey(hkey);
  1848. }
  1849. return(fForce);
  1850. }
  1851. /** CreateStartupProperties - create startup files (for real-mode only)
  1852. *
  1853. * INPUT
  1854. * hProps = handle to properties
  1855. * flOpt = CREATEPROPS_NONE (all bits reserved)
  1856. *
  1857. * OUTPUT
  1858. * TRUE (1) if any files were created, FALSE (0) if there was an error
  1859. * creating 1 or more of the necessary files, and -1 there was no error and
  1860. * no files needed to be created.
  1861. */
  1862. int WINAPI CreateStartupProperties(HANDLE hProps, UINT flOpt)
  1863. {
  1864. BOOL fForceReboot;
  1865. int fSuccess = FALSE;
  1866. PPROPLINK ppl;
  1867. FunctionName(CreateStartupProperties);
  1868. if (NULL != (ppl = ValidPropHandle(hProps))) {
  1869. InitRealModeFlag(ppl);
  1870. if (ppl->flProp & PROP_REALMODE) {
  1871. // WriteAdvPrgData only returns the exact value TRUE (1) if it
  1872. // actually created a file. If there was no need to create
  1873. // a particular file, and therefore no error, it returns -1.
  1874. fForceReboot = CheckForceReboot();
  1875. fSuccess = WriteAdvPrgData(ppl, MAX_CONFIG_SIZE, szCONFIGHDRSIG40, g_szMConfigFile, g_szConfigFile, fForceReboot, fForceReboot);
  1876. fSuccess &= WriteAdvPrgData(ppl, MAX_AUTOEXEC_SIZE, szAUTOEXECHDRSIG40, g_szMAutoexecFile, g_szAutoexecFile, fSuccess, fForceReboot);
  1877. }
  1878. }
  1879. return fSuccess;
  1880. }
  1881. /** DoLargeEnvironmentSubstA - do large environment substitution
  1882. *
  1883. * NOTE: the below comment is no longer true. DoEnvironmentSubst
  1884. * calls ExpandEnvironmentStrings which does not have this restriction!
  1885. *
  1886. * This is a wrapper around shell's DoEnvironmentSubst that can handle
  1887. * larger buffers. Although not well-documented, DoEnvironmentSubst will
  1888. * fail if the input buffer simply contains more than 256 chars, hence
  1889. * it is not entirely suitable for our purposes.
  1890. *
  1891. * The basic idea is: find CR/LF-terminated lines, copy them to szTmp
  1892. * with a null terminator instead of the CR/LF, do environment substitution
  1893. * on szTmp, move the data in psz up by the number of bytes szTmp grew (if
  1894. * any), and then copy the expanded string back into psz. Repeat until no
  1895. * more lines.
  1896. */
  1897. int DoLargeEnvironmentSubstA(LPSTR psz, int cchsz, BOOL fStripWIN)
  1898. {
  1899. int cch, cchNew, cchDiff;
  1900. DWORD dwResult;
  1901. CHAR szTmp[256];
  1902. CHAR *pszCRLF, *pszCRLFNext;
  1903. pszCRLF = psz;
  1904. while (NULL != (pszCRLFNext = (PBYTE)(UINT_PTR)(ULONG_PTR)_fstrstr(pszCRLF, szCRLF))) {
  1905. cch = (int)(pszCRLFNext - pszCRLF);
  1906. pszCRLFNext += ARRAYSIZE(szCRLF)-1;
  1907. if (cch >= ARRAYSIZE(szTmp))
  1908. goto Next;
  1909. hmemcpy(szTmp, pszCRLF, cch);
  1910. szTmp[cch] = '\0';
  1911. // fStripWIN means: strip WIN wherever you find it and do NOTHING else
  1912. if (fStripWIN) {
  1913. if (lstrcmpiA(szTmp, szWin) == 0) {
  1914. cchNew = 0;
  1915. goto Strip;
  1916. }
  1917. goto Next;
  1918. }
  1919. cchNew = cch;
  1920. dwResult = DoEnvironmentSubstA(szTmp, ARRAYSIZE(szTmp));
  1921. if (HIWORD(dwResult))
  1922. cchNew = LOWORD(dwResult);
  1923. Strip:
  1924. cchDiff = cchNew - cch; // cchDiff == # chars needed
  1925. if (cchDiff) {
  1926. if (cchDiff > cchsz - (lstrlenA(psz)+1))
  1927. goto Next; // not enough room left
  1928. cch = lstrlenA(pszCRLFNext)+1;// cch == # chars required to copy
  1929. hmemcpy(pszCRLFNext+(cchDiff*sizeof(CHAR)), pszCRLFNext, cch*sizeof(CHAR));
  1930. pszCRLFNext += cchDiff;
  1931. }
  1932. hmemcpy(pszCRLF, szTmp, cchNew*sizeof(CHAR));
  1933. hmemcpy(pszCRLF+cchNew, szCRLF, (ARRAYSIZE(szCRLF)-1)*sizeof(CHAR));
  1934. Next:
  1935. pszCRLF = pszCRLFNext;
  1936. }
  1937. return lstrlenA(psz);
  1938. }
  1939. void ReadAdvPrgData(PPROPLINK ppl, int cbMax, LPCSTR lpszName, LPTSTR pszFile, UINT flOpt)
  1940. {
  1941. HANDLE hFile;
  1942. int i;
  1943. DWORD cbData = 0, cb;
  1944. CHAR szTmp[256];
  1945. PBYTE pb, pbLast;
  1946. PBYTE pbData = NULL, pbDataTmp, pbDataOrig = NULL;
  1947. SetBootDrive(pszFile);
  1948. if (flOpt & DELETEPROPS_DISCARD) {
  1949. DeleteFile(pszFile);
  1950. return;
  1951. }
  1952. hFile = CreateFile( pszFile,
  1953. GENERIC_READ,
  1954. FILE_SHARE_READ,
  1955. NULL,
  1956. OPEN_EXISTING,
  1957. FILE_ATTRIBUTE_NORMAL,
  1958. NULL );
  1959. if (hFile != INVALID_HANDLE_VALUE) {
  1960. pbData = (PBYTE)LocalAlloc(LPTR, cbMax);
  1961. if (pbData) {
  1962. pbDataOrig = pbData;
  1963. if (ReadFile( hFile, (PVOID)pbData, (DWORD)cbMax-1, &cbData, NULL))
  1964. {
  1965. // Guess what, this routine isn't so general purpose after all:
  1966. // in the autoexec case, we must find the signature that precedes
  1967. // the command-line that we added to the file. For expediency,
  1968. // we simply delete it and everything after it.
  1969. if (pszFile == g_szMConfigFile) {
  1970. // If "DOS=SINGLE" is present, skip over it
  1971. if (pbData == (PBYTE)(UINT_PTR)(ULONG_PTR)_fstrstr(pbData, szSingle)) {
  1972. cb = lstrlenA(szSingle);
  1973. pbData += cb;
  1974. cbData -= cb;
  1975. }
  1976. }
  1977. if (pszFile == g_szMAutoexecFile) {
  1978. // If "@echo off" is present, skip over it
  1979. if (LoadStringA(g_hinst, IDS_AUTOEXECTOP, szTmp, ARRAYSIZE(szTmp))) {
  1980. if (pbData == (PBYTE)(UINT_PTR)(ULONG_PTR)_fstrstr(pbData, szTmp)) {
  1981. cb = lstrlenA(szTmp);
  1982. pbData += cb;
  1983. cbData -= cb;
  1984. }
  1985. }
  1986. // Try to find our signature at the end of the file now
  1987. if (LoadStringA(g_hinst, IDS_AUTOEXECBOTTOM, szTmp, ARRAYSIZE(szTmp))) {
  1988. pb = pbData;
  1989. pbLast = NULL;
  1990. while (NULL != (pb = (PBYTE)(UINT_PTR)(ULONG_PTR)_fstrstr(pb, szTmp))) {
  1991. pbLast = pb++;
  1992. }
  1993. // If there was a (last) occurrence of the signature
  1994. // in the file, then it's starting point is where we cut
  1995. // off the data we're about to rewrite.
  1996. if (pbLast)
  1997. cbData = (DWORD) (pbLast - pbData);
  1998. }
  1999. }
  2000. }
  2001. }
  2002. CloseHandle(hFile);
  2003. DeleteFile(pszFile);
  2004. }
  2005. // Before we simply blast the data back out to the PIF, we check to
  2006. // see if the data we're about to write is the same as what's already
  2007. // there after environment string substitution. If so, we leave the
  2008. // data alone, so that environment strings are left in their pre-expanded
  2009. // form whenever possible (and with a minimum of work on our part).
  2010. i = -1;
  2011. if (NULL != (pbDataTmp = (PBYTE)LocalAlloc(LPTR, cbMax))) {
  2012. if (PifMgr_GetProperties(ppl, lpszName, pbDataTmp, cbMax-1, GETPROPS_NONE)) {
  2013. DoLargeEnvironmentSubstA(pbDataTmp, cbMax, FALSE);
  2014. i = lstrcmpA(pbDataTmp, pbData);
  2015. }
  2016. EVAL(LocalFree(pbDataTmp) == NULL);
  2017. }
  2018. // Note that if the file no longer exists, or does but is zero bytes
  2019. // long, this call effectively removes the data from the PIF. Otherwise,
  2020. // the PIF gets refreshed with the contents of the startup file, which
  2021. // we deleted immediately after pulling its contents out.
  2022. if (i != 0)
  2023. PifMgr_SetProperties(ppl, lpszName, pbData, cbData, SETPROPS_NONE);
  2024. if (pbDataOrig)
  2025. EVAL(LocalFree(pbDataOrig) == NULL);
  2026. }
  2027. BOOL WriteAdvPrgData(PPROPLINK ppl, int cbMax, LPCSTR lpszName, LPTSTR pszFile, LPTSTR pszOrigFile, BOOL fCreateAnyway, BOOL fForceReboot)
  2028. {
  2029. HANDLE hFile;
  2030. int cbData, cb, i, j;
  2031. PBYTE pbData;
  2032. PROPPRG prg;
  2033. PROPENV env;
  2034. DWORD dwBytes;
  2035. BOOL fPause = FALSE;
  2036. BOOL fSuccess = FALSE;
  2037. CHAR szTmp[MAXPATHNAME];
  2038. cbData = PifMgr_GetProperties(ppl, lpszName, NULL, 0, GETPROPS_NONE);
  2039. if (!cbData) { // if there's no autoexec data
  2040. if (fCreateAnyway != 1) // and no previous file was created
  2041. return -1; // then there's no need to create this file
  2042. }
  2043. // If fCreateAnyway is -1, then we know are being called for AUTOEXEC
  2044. // *and* that no file was created for CONFIG. Since we are committed to
  2045. // creating an AUTOEXEC now, we therefore *force* the CONFIG file to
  2046. // be created as well. That way, the user won't end up with the default
  2047. // CONFIG (he may have just not have needed/cared about device drivers, but
  2048. // still wanted to run in real-mode).
  2049. if (fCreateAnyway == -1) {
  2050. if (!WriteAdvPrgData(ppl, MAX_CONFIG_SIZE, szCONFIGHDRSIG40, g_szMConfigFile, g_szConfigFile, TRUE, fForceReboot))
  2051. return FALSE;
  2052. }
  2053. pbData = (PBYTE)LocalAlloc(LPTR, cbMax);
  2054. if (pbData) {
  2055. if (cbData) {
  2056. if (cbData >= cbMax)
  2057. cbData = cbMax-1;
  2058. PifMgr_GetProperties(ppl, lpszName, pbData, cbData, GETPROPS_NONE);
  2059. } else if (fForceReboot) {
  2060. SetBootDrive(pszOrigFile);
  2061. hFile = CreateFile( pszOrigFile,
  2062. GENERIC_READ | GENERIC_WRITE,
  2063. FILE_SHARE_READ,
  2064. NULL,
  2065. OPEN_EXISTING,
  2066. FILE_ATTRIBUTE_NORMAL,
  2067. NULL
  2068. );
  2069. if (hFile != INVALID_HANDLE_VALUE) {
  2070. cbData = (int)SetFilePointer( hFile, 0, NULL, FILE_END );
  2071. if (cbData) {
  2072. if (cbData >= cbMax)
  2073. cbData = cbMax-1;
  2074. SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
  2075. if (ReadFile( hFile, pbData, cbData, &dwBytes, NULL)) {
  2076. if (dwBytes != (UINT)cbData) {
  2077. cbData = 0;
  2078. } else {
  2079. cbData = (int)dwBytes;
  2080. }
  2081. }
  2082. }
  2083. CloseHandle(hFile);
  2084. }
  2085. }
  2086. }
  2087. else
  2088. cbData = 0;
  2089. SetBootDrive( pszFile );
  2090. hFile = CreateFile( pszFile,
  2091. GENERIC_READ | GENERIC_WRITE,
  2092. FILE_SHARE_READ,
  2093. NULL,
  2094. CREATE_ALWAYS,
  2095. FILE_ATTRIBUTE_NORMAL,
  2096. NULL );
  2097. if (hFile!=INVALID_HANDLE_VALUE) {
  2098. // Do environment string substitution on the buffer before writing
  2099. // it out. Treat it like the huge null-terminated string that it is.
  2100. if (pbData)
  2101. cbData = DoLargeEnvironmentSubstA(pbData, cbMax, fForceReboot);
  2102. // Do the simple thing: ALWAYS write out the "@echo off" line;
  2103. // if someone really wants to see the batch file execute, let them
  2104. // put an explicit "echo on" in their autoexec edit control; since it
  2105. // comes after ours, it will override as appropriate.
  2106. //
  2107. // This rule also simplifies conversion on the way back: if the
  2108. // first line is "@echo off", we delete it.
  2109. if (pszFile == g_szMConfigFile) {
  2110. WriteFile( hFile, szSingle, lstrlenA(szSingle), &dwBytes, NULL );
  2111. }
  2112. if (pszFile == g_szMAutoexecFile) {
  2113. szTmp[0] = '\0';
  2114. LoadStringA(g_hinst, IDS_AUTOEXECTOP, szTmp, ARRAYSIZE(szTmp));
  2115. WriteFile( hFile, szTmp, lstrlenA(szTmp), &dwBytes, NULL );
  2116. }
  2117. if (WriteFile(hFile, pbData, cbData, &dwBytes, NULL) && (dwBytes==(DWORD)cbData))
  2118. fSuccess++; // return TRUE to indicate file created
  2119. // Make sure the user-defined gob-o-goo is CR/LF-terminated;
  2120. // if not, spit one out.
  2121. if (cbData != 0)
  2122. if (cbData < 2 || lstrcmpA(pbData+cbData-(ARRAYSIZE(szCRLF)-1), szCRLF) != 0)
  2123. WriteFile( hFile, szCRLF, sizeof(szCRLF)-1, &dwBytes, NULL);
  2124. // Guess what, this routine isn't so general purpose after all:
  2125. // in the autoexec case, we must append the command-line to
  2126. // the file, followed by C:\WINDOWS\WIN.COM /W to restore everything
  2127. if (pszFile == g_szMAutoexecFile) {
  2128. if (PifMgr_GetProperties(ppl, MAKELP(0,GROUP_PRG),
  2129. &prg, sizeof(prg), GETPROPS_NONE)) {
  2130. szTmp[0] = '\0';
  2131. LoadStringA(g_hinst, IDS_AUTOEXECBOTTOM, szTmp, ARRAYSIZE(szTmp));
  2132. WriteFile(hFile, szTmp, lstrlenA(szTmp), &dwBytes, NULL);
  2133. // If the program is on drive A, then it is time emit:
  2134. //
  2135. // ECHO Please insert the diskette for Mickey's ABCs in drive A now
  2136. // PAUSE
  2137. //
  2138. // since WinOldAp will have asked the user to remove it.
  2139. if ((ppl->szPathName[0] == TEXT('a') ||
  2140. ppl->szPathName[0] == TEXT('A')) && ppl->szPathName[1] == TEXT(':')) {
  2141. CHAR szTmpFmt[128];
  2142. fPause++;
  2143. WriteFile(hFile, szEcho, sizeof(szEcho)-1, &dwBytes, NULL);
  2144. szTmpFmt[0] = '\0';
  2145. LoadStringA(g_hinst, IDS_DISKINSERT, szTmpFmt, ARRAYSIZE(szTmpFmt));
  2146. // The DISKINSERT string should have one %s for the title...
  2147. wsprintfA(szTmp, szTmpFmt, prg.achTitle);
  2148. WriteFile(hFile, szTmp, lstrlenA(szTmp), &dwBytes, NULL);
  2149. WriteFile(hFile, szPause, sizeof(szPause)-1, &dwBytes, NULL);
  2150. }
  2151. if (PifMgr_GetProperties(ppl, MAKELP(0,GROUP_ENV),
  2152. &env, sizeof(env), GETPROPS_NONE)) {
  2153. if (env.achBatchFile[0]) {
  2154. WriteFile(hFile, szCall, sizeof(szCall)-1, &dwBytes, NULL);
  2155. // env.achBatchFile could be a long filename and/or
  2156. // quoted (since it could be followed by args). Take
  2157. // care of it now.
  2158. i = lstrunquotefnameA(szTmp, env.achBatchFile, ARRAYSIZE(szTmp), TRUE);
  2159. j = lstrskipfnameA(env.achBatchFile);
  2160. if (env.achBatchFile[j])
  2161. lstrcpynA(szTmp+i, env.achBatchFile+j, ARRAYSIZE(szTmp)-i);
  2162. WriteFile(hFile, szTmp, lstrlenA(szTmp), &dwBytes, NULL);
  2163. WriteFile(hFile, szCRLF, sizeof(szCRLF)-1, &dwBytes, NULL);
  2164. }
  2165. }
  2166. #ifdef DBCS
  2167. // else if (!GetProperties(ppl, lpszName, NULL, 0, GETPROPS_NONE))
  2168. // _lwrite(hFile, szDOSIME, lstrlen(szDOSIME));
  2169. #endif
  2170. if (!prg.achWorkDir[0]) {
  2171. // If there's no hard-coded working directory,
  2172. // create one. It's ok to party on prg.achWorkDir because
  2173. // we're not going to call PifMgr_SetProperties and inadvertently
  2174. // change it.
  2175. InitWorkDir(ppl, &prg, NULL);
  2176. }
  2177. if (prg.achWorkDir[0]) {
  2178. cb = lstrunquotefnameA(szTmp, prg.achWorkDir, ARRAYSIZE(szTmp), TRUE);
  2179. if (szTmp[1] == ':') {
  2180. WriteFile(hFile, szTmp, 2, &dwBytes, NULL);
  2181. WriteFile(hFile, szCRLF, ARRAYSIZE(szCRLF)-1, &dwBytes, NULL);
  2182. }
  2183. WriteFile(hFile, szCD, sizeof(szCD)-1, &dwBytes, NULL);
  2184. WriteFile(hFile, szTmp, cb, &dwBytes, NULL);
  2185. WriteFile(hFile, szCRLF, sizeof(szCRLF)-1, &dwBytes, NULL);
  2186. }
  2187. // We now always CALL the cmd-line, in case it's a batch file.
  2188. // If it isn't, no biggie, because command.com is smart enough
  2189. // to ignore it.
  2190. WriteFile(hFile, szCall, sizeof(szCall)-1, &dwBytes, NULL);
  2191. // If the properties we have are ones *we* created in the
  2192. // PIF dir, and it looks like the application name passed to
  2193. // PifMgr_OpenProperties is a "qualified" pathname, then we'll use
  2194. // that instead of the path stored in the PIF, since the app
  2195. // might have moved since *we* created the PIF.
  2196. if ((ppl->flProp & PROP_PIFDIR) &&
  2197. (ppl->szPathName[0] == TEXT('\\') ||
  2198. ppl->szPathName[0] && ppl->szPathName[1] == TEXT(':'))) {
  2199. // We're making this call not because ppl->szPathName
  2200. // is quoted (it shouldn't be), but because it could still
  2201. // be a long filename, so translate it.
  2202. CHAR achPathName[ ARRAYSIZE(ppl->szPathName) ];
  2203. PifMgr_WCtoMBPath( ppl->szPathName, achPathName, ARRAYSIZE(achPathName) );
  2204. i = lstrunquotefnameA(szTmp, achPathName, ARRAYSIZE(szTmp), -1);
  2205. }
  2206. else {
  2207. // As for the thing stored in the PIF, now *that* could
  2208. // both long and quoted. Take care of it now.
  2209. i = lstrunquotefnameA(szTmp, prg.achCmdLine, ARRAYSIZE(szTmp), TRUE);
  2210. }
  2211. // NOTE: There is an obscure case where the preceding call
  2212. // to lstrunquotefname can fail, returning i == 0, even though
  2213. // WinOldAp already verified the app exists. That case is when
  2214. // the app name is NOT fully-qualified, so it exists somewhere
  2215. // on the PATH, but NOT in the current directory, and the app
  2216. // name is an LFN. Because lstrunquotefname must call INT 21h,
  2217. // function 7160h, subfunction 1 (NAMTRN_DO83QUERY), IFSMGR must
  2218. // find the equivalent 8.3 name for the LFN app name, but all
  2219. // IFSMGR has to work with is the current directory, and that's
  2220. // not where the app is, so the INT 21h fails, returning error
  2221. // #3 (ERROR_PATH_NOT_FOUND).
  2222. //
  2223. // To solve, we could do another PATH search for the app file,
  2224. // via OpenFileEx; if found, it would return the fully-qualifed
  2225. // long-file-name of the app, in the OEM char set, which we
  2226. // could then perform NameTrans on, to get full 8.3. Yuck! -JP
  2227. // If there are any arguments in the PIF, find them
  2228. // and append them, giving PREFERENCE to any args that WINOLDAP
  2229. // specifically associated with this instance of the app.
  2230. if (ppl->lpArgs) { // any associated args?
  2231. j = lstrlen(ppl->lpArgs);
  2232. if (j) {
  2233. szTmp[i++] = ' ';
  2234. if (ppl->lpArgs[j-1] == TEXT('\r'))
  2235. j--;
  2236. {
  2237. CHAR achArgs[ ARRAYSIZE(ppl->lpArgs) ];
  2238. WideCharToMultiByte( CP_ACP, 0,
  2239. ppl->lpArgs, -1,
  2240. achArgs, ARRAYSIZE(achArgs),
  2241. NULL, NULL );
  2242. lstrcpynA(szTmp+i, achArgs, min(sizeof(szTmp)-i,(unsigned)j+1));
  2243. }
  2244. }
  2245. } else {
  2246. j = lstrskipfnameA(prg.achCmdLine);
  2247. if (prg.achCmdLine[j])
  2248. lstrcpynA(szTmp+i, prg.achCmdLine+j, ARRAYSIZE(szTmp)-i);
  2249. }
  2250. WriteFile(hFile, szTmp, lstrlenA(szTmp), &dwBytes, NULL);
  2251. WriteFile(hFile, szCRLF, sizeof(szCRLF)-1, &dwBytes, NULL);
  2252. // If we paused above to ask for a disk, we should pause
  2253. // again to ask them to remove the disk. We won't actually
  2254. // emit a PAUSE though unless close-on-exit is set, since
  2255. // WIN.COM should already pause for them when the cursor isn't
  2256. // at 0,0.
  2257. if (fPause) {
  2258. WriteFile(hFile, szEcho, sizeof(szEcho)-1, &dwBytes, NULL);
  2259. szTmp[0] = TEXT('\0');
  2260. LoadStringA(g_hinst, IDS_DISKREMOVE, szTmp, ARRAYSIZE(szTmp));
  2261. WriteFile(hFile, szTmp, lstrlenA(szTmp), &dwBytes, NULL);
  2262. if (prg.flPrg & PRG_CLOSEONEXIT)
  2263. WriteFile(hFile, szPause, sizeof(szPause)-1, &dwBytes, NULL);
  2264. else
  2265. WriteFile(hFile, szCRLF, sizeof(szCRLF)-1, &dwBytes, NULL);
  2266. }
  2267. }
  2268. }
  2269. CloseHandle(hFile);
  2270. }
  2271. if (pbData)
  2272. EVAL(LocalFree(pbData) == NULL);
  2273. return fSuccess;
  2274. }
  2275. /** PifMgr_CloseProperties - close property info for application
  2276. *
  2277. * INPUT
  2278. * hProps = handle to properties
  2279. * flOpt = CLOSEPROPS_DISCARD to abandon cached PIF data, otherwise save it
  2280. *
  2281. * OUTPUT
  2282. * NULL if successful, otherwise hProps is returned as given
  2283. */
  2284. HANDLE WINAPI PifMgr_CloseProperties(HANDLE hProps, UINT flOpt)
  2285. {
  2286. PPROPLINK ppl;
  2287. FunctionName(PifMgr_CloseProperties);
  2288. if (!(ppl = ValidPropHandle(hProps)))
  2289. return hProps;
  2290. // When discarding on a close, set the SKIPPIF flag, so that the
  2291. // flush code won't say "oh, not only should I throw away my current
  2292. // set of data, but I should read in clean data" -- new data is no use
  2293. // since the caller is closing.
  2294. if (flOpt & CLOSEPROPS_DISCARD)
  2295. ppl->flProp |= PROP_SKIPPIF;
  2296. if (ppl->flProp & PROP_DIRTY) { // this redundant check added
  2297. // to avoid making FlushPIFData PRELOAD -JTP
  2298. // Note that we avoid calling FlushPIFData if INHIBITPIF is set,
  2299. // since FlushPIFData will just return a fake TRUE result anyway.
  2300. // But we don't want to be fooled, we want to make sure the block
  2301. // gets unlocked now.
  2302. if ((ppl->flProp & PROP_INHIBITPIF) || !FlushPIFData(ppl, (flOpt & CLOSEPROPS_DISCARD))) {
  2303. // If FlushPIFData failed, then if we still have an outstanding
  2304. // dirty lock, force the data to become unlocked, by clearing the
  2305. // dirty flag in the middle of a pair otherwise pointless lock/unlock
  2306. // calls (because that's the nice, clean way to do it!)
  2307. if (ppl->flProp & PROP_DIRTYLOCK) {
  2308. ppl->cLocks++;
  2309. ppl->flProp &= ~PROP_DIRTY;
  2310. ppl->cLocks--;
  2311. }
  2312. }
  2313. }
  2314. if (ppl->lpPIFData) {
  2315. LocalFree(ppl->lpPIFData);
  2316. ppl->lpPIFData = NULL;
  2317. }
  2318. if (ppl->hPIF != INVALID_HANDLE_VALUE)
  2319. CloseHandle(ppl->hPIF);
  2320. // Unlink from the global list
  2321. if (ppl->pplPrev)
  2322. ppl->pplPrev->pplNext = ppl->pplNext;
  2323. else
  2324. g_pplHead = ppl->pplNext;
  2325. if (ppl->pplNext)
  2326. ppl->pplNext->pplPrev = ppl->pplPrev;
  2327. LocalFree(ppl);
  2328. return NULL;
  2329. }
  2330. /** ValidPropHandle - verify handle
  2331. *
  2332. * INPUT
  2333. * hProps = handle to properties
  2334. *
  2335. * OUTPUT
  2336. * pointer to prop, NULL otherwise
  2337. */
  2338. PPROPLINK ValidPropHandle(HANDLE hProps)
  2339. {
  2340. FunctionName(ValidPropHandle);
  2341. if (!hProps ||
  2342. (HANDLE)hProps > g_offHighestPropLink ||
  2343. ((PPROPLINK)hProps)->iSig != PROP_SIG) {
  2344. ASSERTFAIL();
  2345. return NULL;
  2346. }
  2347. return (PPROPLINK)hProps;
  2348. }
  2349. /** ResizePIFData - verify handle and resize PIF data
  2350. *
  2351. * INPUT
  2352. * ppl -> property
  2353. * cbResize = bytes to resize PIF data by
  2354. *
  2355. * OUTPUT
  2356. * previous size of PIF data if successful, -1 if not
  2357. *
  2358. * on success, the PIF data is returned LOCKED, so successful
  2359. * ResizePIFData calls should be matched with UnlockPIFData calls.
  2360. */
  2361. int ResizePIFData(PPROPLINK ppl, INT cbResize)
  2362. {
  2363. INT cbOld, cbNew;
  2364. void *lpNew;
  2365. BOOL fInitStdHdr = FALSE;
  2366. FunctionName(ResizePIFData);
  2367. ASSERTTRUE(cbResize != 0);
  2368. // Cope with empty or old PIF files
  2369. cbOld = ppl->cbPIFData;
  2370. cbNew = ppl->cbPIFData + cbResize;
  2371. if ((cbNew < cbOld) == (cbResize > 0))
  2372. return -1; // underflow/overflow
  2373. if (!ppl->lpPIFData && cbOld == 0) {
  2374. if (cbNew >= sizeof(STDPIF) + sizeof(PIFEXTHDR))
  2375. fInitStdHdr = TRUE;
  2376. lpNew = LocalAlloc(LPTR, cbNew);
  2377. }
  2378. else
  2379. {
  2380. if (cbOld == sizeof(STDPIF))
  2381. {
  2382. fInitStdHdr = TRUE;
  2383. cbOld += sizeof(PIFEXTHDR);
  2384. cbNew += sizeof(PIFEXTHDR);
  2385. }
  2386. lpNew = LocalReAlloc( ppl->lpPIFData, cbNew, LMEM_MOVEABLE|LMEM_ZEROINIT);
  2387. }
  2388. if (lpNew) {
  2389. ppl->cbPIFData = cbNew;
  2390. ppl->lpPIFData = (LPPIFDATA)lpNew;
  2391. ppl->cLocks++;
  2392. if (fInitStdHdr) {
  2393. lstrcpyA(ppl->lpPIFData->stdpifext.extsig, szSTDHDRSIG);
  2394. ppl->lpPIFData->stdpifext.extnxthdrfloff = LASTHDRPTR;
  2395. ppl->lpPIFData->stdpifext.extfileoffset = 0x0000;
  2396. ppl->lpPIFData->stdpifext.extsizebytes = sizeof(STDPIF);
  2397. }
  2398. return cbOld;
  2399. }
  2400. return -1;
  2401. }
  2402. /** GetPIFData - read PIF data back from PIF
  2403. *
  2404. * INPUT
  2405. * ppl -> property
  2406. * fLocked == TRUE to return data locked, FALSE unlocked
  2407. *
  2408. * OUTPUT
  2409. * TRUE if succeeded, FALSE if not
  2410. */
  2411. BOOL GetPIFData(PPROPLINK ppl, BOOL fLocked)
  2412. {
  2413. DWORD dwOff;
  2414. LPTSTR pszOpen;
  2415. BOOL fSuccess = FALSE;
  2416. FunctionName(GetPIFData);
  2417. // Since we're going to (re)load the property data now, reset
  2418. // the current size, so that ResizePIFData will resize it from zero
  2419. ppl->cbPIFData = 0;
  2420. // If SKIPPIF is set (eg, by PifMgr_OpenProperties), then don't
  2421. // try to open anything (since PifMgr_OpenProperties already tried!),
  2422. if (ppl->hPIF == INVALID_HANDLE_VALUE && !(ppl->flProp & PROP_SKIPPIF)) {
  2423. pszOpen = g_szDefaultPIF;
  2424. if (!(ppl->flProp & PROP_DEFAULTPIF))
  2425. pszOpen = ppl->ofPIF.szPathName;
  2426. ppl->hPIF = CreateFile( pszOpen,
  2427. GENERIC_READ | GENERIC_WRITE,
  2428. FILE_SHARE_READ | FILE_SHARE_WRITE,
  2429. NULL,
  2430. OPEN_EXISTING,
  2431. FILE_ATTRIBUTE_NORMAL,
  2432. NULL );
  2433. }
  2434. if (ppl->hPIF == INVALID_HANDLE_VALUE) {
  2435. // The following warning is disabled because the presence of
  2436. // the dialog box got WINOLDAP stuck in an infinite message loop -JTP
  2437. InitProperties(ppl, fLocked);
  2438. goto Exit;
  2439. }
  2440. dwOff = SetFilePointer(ppl->hPIF, 0, NULL, FILE_END);
  2441. if (dwOff >= sizeof(STDPIF)) {
  2442. ppl->flProp |= PROP_REGEN;
  2443. if (ResizePIFData(ppl, dwOff) != -1) {
  2444. SetFilePointer(ppl->hPIF, 0, NULL, FILE_BEGIN);
  2445. if (ReadFile( ppl->hPIF, ppl->lpPIFData,
  2446. ppl->cbPIFData, &ppl->cbPIFData, NULL ))
  2447. {
  2448. // Can't be dirty anymore, 'cause we just read the PIF back in
  2449. ppl->flProp &= ~PROP_DIRTY;
  2450. if (ppl->flProp & PROP_DEFAULTPIF) {
  2451. WideCharToMultiByte( CP_ACP, 0,
  2452. ppl->szPathName+ppl->iFileName,
  2453. -1,
  2454. ppl->lpPIFData->stdpifdata.appname,
  2455. ARRAYSIZE(ppl->lpPIFData->stdpifdata.appname),
  2456. NULL, NULL
  2457. );
  2458. PifMgr_WCtoMBPath( ppl->szPathName,
  2459. ppl->lpPIFData->stdpifdata.startfile,
  2460. ARRAYSIZE(ppl->lpPIFData->stdpifdata.startfile)
  2461. );
  2462. // I don't think this is generally worth dirtying the
  2463. // property info for, because otherwise every app that used
  2464. // _DEFAULT.PIF initially would get its own PIF file created
  2465. // later; PIF file creation should only take place when
  2466. // substantive changes have been made
  2467. // ppl->flProp |= PROP_DIRTY;
  2468. }
  2469. // If we're not dealing with an enhanced PIF, then we
  2470. // go to the various INI files to retrieve DOS app defaults
  2471. if (!GetGroupData(ppl, szWENHHDRSIG40, NULL, NULL)) {
  2472. GetINIData();
  2473. }
  2474. // If we're not dealing with a new NT/UNICODE PIF, then
  2475. // we add a new section so it's ALWAYS there when we're
  2476. // UNICODE enabled.
  2477. if (!GetGroupData(ppl, szWNTHDRSIG40, NULL, NULL)) {
  2478. VERIFYTRUE(AddGroupData(ppl, szWNTHDRSIG40, NULL, sizeof(WNTPIF40)));
  2479. }
  2480. // If we're not dealing with a NT PIF, then
  2481. // we add the NT sections so it's ALWAYS there when we're
  2482. // running on NT.
  2483. if (!GetGroupData(ppl, szWNTHDRSIG31, NULL, NULL)) {
  2484. LPWNTPIF31 lpnt31;
  2485. VERIFYTRUE(AddGroupData(ppl, szWNTHDRSIG31, NULL, sizeof(WNTPIF31)));
  2486. if (NULL != (lpnt31 = GetGroupData(ppl, szWNTHDRSIG31, NULL, NULL))) {
  2487. lstrcpyA( lpnt31->nt31Prop.achConfigFile, NT_CONFIG_FILE );
  2488. lstrcpyA( lpnt31->nt31Prop.achAutoexecFile, NT_AUTOEXEC_FILE );
  2489. }
  2490. }
  2491. if (!fLocked)
  2492. ppl->cLocks--; // UnlockPIFData(ppl);
  2493. fSuccess++;
  2494. }
  2495. }
  2496. else
  2497. ASSERTFAIL();
  2498. ppl->flProp &= ~PROP_REGEN;
  2499. }
  2500. CloseHandle(ppl->hPIF);
  2501. ppl->hPIF = INVALID_HANDLE_VALUE;
  2502. // As long as IGNOREPIF isn't set, clear SKIPPIF, because even if we
  2503. // already knew the PIF didn't exist on *this* call, one may be created
  2504. // (by someone else) by the next time we're called
  2505. Exit:
  2506. if (!(ppl->flProp & PROP_IGNOREPIF))
  2507. ppl->flProp &= ~PROP_SKIPPIF;
  2508. return fSuccess;
  2509. }
  2510. /** FlushPIFData - write dirty PIF data back to PIF
  2511. *
  2512. * INPUT
  2513. * ppl -> property
  2514. * fDiscard == TRUE to discard dirty data, FALSE to keep it
  2515. *
  2516. * OUTPUT
  2517. * TRUE if succeeded, FALSE if not
  2518. *
  2519. * NOTES
  2520. * We must first check the PROPLINK and see if the DONTWRITE bit has
  2521. * been set, in which case we have to fail the flush. Once DONTWRITE is
  2522. * set in a PROPLINK, it will never be cleared, unless the caller
  2523. * specifies fDiscard == TRUE to reload the data. This is BY DESIGN (ie,
  2524. * a UI compromise). How does DONTWRITE get set? By someone else
  2525. * having previously (and successfully) done a flush to the same PIF; at
  2526. * that point in time, we will look for all other properties that refer to
  2527. * the same file, and set their DONTWRITE bit. What about PROPLINKs that
  2528. * are created later? They're ok, they don't get DONTWRITE set until
  2529. * the above sequence takes place during their lifetime.
  2530. */
  2531. BOOL FlushPIFData(PPROPLINK ppl, BOOL fDiscard)
  2532. {
  2533. UINT u;
  2534. BOOL fSuccess = FALSE;
  2535. FunctionName(FlushPIFData);
  2536. // If nothing dirty, nothing to do
  2537. if (!(ppl->flProp & PROP_DIRTY) || (ppl->flProp & PROP_INHIBITPIF))
  2538. return TRUE; // ie, success
  2539. // If discarding, then clear PROP_DIRTY and reload the data
  2540. if (fDiscard) {
  2541. ppl->flProp &= ~(PROP_DIRTY | PROP_DONTWRITE);
  2542. return GetPIFData(ppl, FALSE);
  2543. }
  2544. if (ppl->flProp & PROP_DONTWRITE)
  2545. return fSuccess; // ie, FALSE (error)
  2546. if (!ppl->lpPIFData)
  2547. return fSuccess; // ie, FALSE (error)
  2548. ppl->cLocks++;
  2549. // If we created properties without opening a file, it may have
  2550. // been because normal PIF search processing was overridden by the
  2551. // presence of a WIN.INI entry; if that entry is still there,
  2552. // then our data is not in sync with any existing file, nor is there
  2553. // any point in creating a new file as long as that entry exists. We
  2554. // need to consider prompting the user as to whether he really wants
  2555. // that WIN.INI entry, so that it's clear what the heck is going on
  2556. if (ppl->flProp & PROP_IGNOREPIF) {
  2557. HANDLE hProps;
  2558. ppl->ckbMem = GetProfileInt(apszAppType[APPTYPE_PIF]+1, ppl->szPathName+ppl->iFileName, -1);
  2559. if (ppl->ckbMem != -1)
  2560. goto Exit;
  2561. // The WIN.INI entry apparently went away, so let's re-attempt to
  2562. // open the properties that we should have obtained in the first
  2563. // place. Assuming success, we will copy our entire block on top of
  2564. // them (thereby flushing it), and also copy their PIF name to our
  2565. // PIF name and their PIF flags to our PIF flags, so that future
  2566. // flushes are of the more normal variety
  2567. hProps = PifMgr_OpenProperties(ppl->ofPIF.szPathName, NULL, 0, OPENPROPS_RAWIO);
  2568. if (hProps) {
  2569. ppl->flProp &= ~(PROP_IGNOREPIF | PROP_SKIPPIF);
  2570. ppl->flProp |= ((PPROPLINK)hProps)->flProp & (PROP_IGNOREPIF | PROP_SKIPPIF);
  2571. lstrcpy(ppl->ofPIF.szPathName, ((PPROPLINK)hProps)->ofPIF.szPathName);
  2572. if (PifMgr_SetProperties(hProps, NULL, ppl->lpPIFData, ppl->cbPIFData, SETPROPS_RAWIO) == ppl->cbPIFData) {
  2573. fSuccess++;
  2574. ppl->flProp &= ~(PROP_DIRTY | PROP_TRUNCATE);
  2575. }
  2576. PifMgr_CloseProperties(hProps, CLOSEPROPS_NONE);
  2577. }
  2578. goto Exit;
  2579. }
  2580. // Disable annoying critical error popups (NO MORE GOTOS PAST HERE PLEASE)
  2581. u = SetErrorMode(SEM_FAILCRITICALERRORS);
  2582. ppl->hPIF = CreateFile( ppl->ofPIF.szPathName,
  2583. GENERIC_READ | GENERIC_WRITE,
  2584. FILE_SHARE_READ | FILE_SHARE_WRITE,
  2585. NULL,
  2586. OPEN_ALWAYS,
  2587. FILE_ATTRIBUTE_NORMAL,
  2588. NULL );
  2589. // If we couldn't open the file, then the presumption is that the
  2590. // app didn't have a PIF (or did but someone but someone deleted it),
  2591. // and so we use the name we constructed during PifMgr_OpenProperties in case
  2592. // they ever opted to save new settings (which they obviously have done!)
  2593. // 28-Feb-95: If the PIF did exist at one time (meaning NOCREATPIF is
  2594. // set), then don't recreate it; somebody's trying to delete their own
  2595. // PIF, so let them. -JTP
  2596. if ((ppl->hPIF != INVALID_HANDLE_VALUE) && (GetLastError()!=ERROR_FILE_EXISTS)) {
  2597. if (!(ppl->flProp & PROP_NOCREATEPIF))
  2598. SetFilePointer( ppl->hPIF, 0, NULL, FILE_BEGIN );
  2599. // If the create succeeded, we're no longer using the default PIF
  2600. if (ppl->hPIF != INVALID_HANDLE_VALUE) {
  2601. ppl->flProp |= PROP_NOCREATEPIF;
  2602. ppl->flProp &= ~(PROP_TRUNCATE | PROP_NOPIF | PROP_DEFAULTPIF);
  2603. }
  2604. }
  2605. // If either the open or the create succeeded, write the PIF data out now
  2606. if (ppl->hPIF != INVALID_HANDLE_VALUE) {
  2607. PPROPLINK pplEnum;
  2608. DWORD dwDummy;
  2609. WriteFile( ppl->hPIF, (LPCVOID)ppl->lpPIFData,
  2610. ppl->cbPIFData, &dwDummy, NULL );
  2611. if (ppl->flProp & PROP_TRUNCATE)
  2612. WriteFile(ppl->hPIF, (LPCVOID)ppl->lpPIFData, 0, &dwDummy, NULL );
  2613. CloseHandle(ppl->hPIF);
  2614. ppl->hPIF = INVALID_HANDLE_VALUE;
  2615. ppl->flProp &= ~(PROP_DIRTY | PROP_TRUNCATE);
  2616. fSuccess++;
  2617. // Here's where we want to check for other active PROPLINKs using the
  2618. // same PIF. For each one found, set its DONTWRITE bit.
  2619. pplEnum = NULL;
  2620. while (NULL != (pplEnum = (PPROPLINK)EnumProperties(pplEnum))) {
  2621. if (lstrcmpi(ppl->ofPIF.szPathName, pplEnum->ofPIF.szPathName) == 0) {
  2622. if (pplEnum != ppl)
  2623. pplEnum->flProp |= PROP_DONTWRITE;
  2624. }
  2625. }
  2626. }
  2627. // Re-enable annoying critical error popups
  2628. SetErrorMode(u);
  2629. Exit:
  2630. ppl->cLocks--;
  2631. return fSuccess;
  2632. }
  2633. /** AddEnhancedData - create enhanced section(s) of PIF data
  2634. *
  2635. * INPUT
  2636. * ppl -> property
  2637. *
  2638. * OUTPUT
  2639. * lpenh or NULL
  2640. */
  2641. LPWENHPIF40 AddEnhancedData(PPROPLINK ppl, LPW386PIF30 lp386)
  2642. {
  2643. PROPPRG prg;
  2644. PROPTSK tsk;
  2645. PROPVID vid;
  2646. PROPKBD kbd;
  2647. PROPMSE mse;
  2648. PROPFNT fnt;
  2649. PROPWIN win;
  2650. PROPENV env;
  2651. void *aDataPtrs[NUM_DATA_PTRS];
  2652. LPWENHPIF40 lpenh = NULL;
  2653. FunctionName(AddEnhancedData);
  2654. // Get copies of pre-enhanced and/or default settings first,
  2655. // and do them all *before* doing the AddGroupData, because the
  2656. // functions' behavior will change once the enhanced section is added;
  2657. // in addition, zero those strucs that contain strings, since lstrcpy()
  2658. // may initialize a minimum of 1 byte, leaving garbage in the rest.
  2659. BZero(&prg, sizeof(prg));
  2660. BZero(&fnt, sizeof(fnt));
  2661. BZero(&win, sizeof(win));
  2662. BZero(&env, sizeof(env));
  2663. BZero(aDataPtrs, sizeof(aDataPtrs));
  2664. aDataPtrs[ LP386_INDEX ] = (LPVOID)lp386;
  2665. GetPrgData(ppl, aDataPtrs, &prg, sizeof(prg), GETPROPS_NONE);
  2666. GetTskData(ppl, aDataPtrs, &tsk, sizeof(tsk), GETPROPS_NONE);
  2667. GetVidData(ppl, aDataPtrs, &vid, sizeof(vid), GETPROPS_NONE);
  2668. GetKbdData(ppl, aDataPtrs, &kbd, sizeof(kbd), GETPROPS_NONE);
  2669. GetMseData(ppl, aDataPtrs, &mse, sizeof(mse), GETPROPS_NONE);
  2670. GetFntData(ppl, aDataPtrs, &fnt, sizeof(fnt), GETPROPS_NONE);
  2671. GetWinData(ppl, aDataPtrs, &win, sizeof(win), GETPROPS_NONE);
  2672. GetEnvData(ppl, aDataPtrs, &env, sizeof(env), GETPROPS_NONE);
  2673. if (AddGroupData(ppl, szWENHHDRSIG40, NULL, sizeof(WENHPIF40))) {
  2674. if (NULL != (lpenh = GetGroupData(ppl, szWENHHDRSIG40, NULL, NULL))) {
  2675. lpenh->dwEnhModeFlagsProp = prg.dwEnhModeFlags;
  2676. lpenh->dwRealModeFlagsProp = prg.dwRealModeFlags;
  2677. lstrcpyA(lpenh->achOtherFileProp, prg.achOtherFile);
  2678. lstrcpyA(lpenh->achIconFileProp, prg.achIconFile);
  2679. lpenh->wIconIndexProp = prg.wIconIndex;
  2680. lpenh->tskProp = tsk;
  2681. lpenh->vidProp = vid;
  2682. lpenh->kbdProp = kbd;
  2683. lpenh->mseProp = mse;
  2684. lpenh->fntProp = fnt;
  2685. lpenh->winProp = win;
  2686. lpenh->envProp = env;
  2687. lpenh->wInternalRevision = WENHPIF40_VERSION;
  2688. }
  2689. }
  2690. return lpenh;
  2691. }
  2692. /** AddGroupData - add NEW property group to PIF data
  2693. *
  2694. * INPUT
  2695. * ppl -> property
  2696. * lpszGroup -> name of new group
  2697. * lpGroup -> new group record (if NULL, then group data is zero-filled)
  2698. * cbGroup == size of new group record
  2699. *
  2700. * OUTPUT
  2701. * TRUE if successful, FALSE if not
  2702. */
  2703. BOOL AddGroupData(PPROPLINK ppl, LPCSTR lpszGroup, LPCVOID lpGroup, int cbGroup)
  2704. {
  2705. INT cbOld;
  2706. LPPIFEXTHDR lpph;
  2707. FunctionName(AddGroupData);
  2708. if ((cbOld = ResizePIFData(ppl, cbGroup+sizeof(PIFEXTHDR))) != -1) {
  2709. lpph = (LPPIFEXTHDR)LPPIF_FIELDOFF(stdpifext);
  2710. while ((DWORD_PTR)lpph <= (DWORD_PTR)LPPIF_OFF(cbOld - sizeof(PIFEXTHDR)) &&
  2711. (DWORD_PTR)lpph >= (DWORD_PTR)LPPIF_FIELDOFF(stdpifext)) {
  2712. if (lpph->extnxthdrfloff == LASTHDRPTR) {
  2713. lpph->extnxthdrfloff = (WORD) cbOld;
  2714. lpph = (LPPIFEXTHDR)LPPIF_OFF(cbOld);
  2715. lstrcpynA(lpph->extsig, lpszGroup, sizeof(lpph->extsig));
  2716. lpph->extnxthdrfloff = LASTHDRPTR;
  2717. lpph->extfileoffset = (INT)(cbOld + sizeof(PIFEXTHDR));
  2718. lpph->extsizebytes = (WORD) cbGroup;
  2719. if (lpGroup) {
  2720. hmemcpy((LPBYTE)LPPH_OFF(sizeof(PIFEXTHDR)), lpGroup, cbGroup);
  2721. ppl->flProp |= PROP_DIRTY;
  2722. }
  2723. break;
  2724. }
  2725. lpph = (LPPIFEXTHDR)LPPIF_OFF(lpph->extnxthdrfloff);
  2726. }
  2727. ppl->cLocks--;
  2728. return TRUE;
  2729. }
  2730. ASSERTFAIL();
  2731. return FALSE;
  2732. }
  2733. /** RemoveGroupData - remove EXISTING property group from PIF data
  2734. *
  2735. * INPUT
  2736. * ppl -> property
  2737. * lpszGroup -> name of group
  2738. *
  2739. * OUTPUT
  2740. * TRUE if successful, FALSE if not
  2741. */
  2742. BOOL RemoveGroupData(PPROPLINK ppl, LPCSTR lpszGroup)
  2743. {
  2744. INT cbGroup, fSuccess;
  2745. LPBYTE lpGroup;
  2746. WORD extnxthdrfloff;
  2747. LPPIFEXTHDR lpph, lpphGroup;
  2748. FunctionName(RemoveGroupData);
  2749. ppl->cLocks++;
  2750. fSuccess = FALSE;
  2751. if (NULL != (lpGroup = GetGroupData(ppl, lpszGroup, &cbGroup, &lpphGroup))) {
  2752. // Removing groups is a bit tedious, so here goes....
  2753. // First, we will walk all the headers, attempting to find the
  2754. // one that points to the one we're about to remove, and point it
  2755. // to the next one, and at the same time adjust all file offsets that
  2756. // equal or exceed the offsets of either the outgoing data or its
  2757. // header.
  2758. lpph = (LPPIFEXTHDR)LPPIF_FIELDOFF(stdpifext);
  2759. while ((DWORD_PTR)lpph <= (DWORD_PTR)LPPIF_OFF(ppl->cbPIFData - sizeof(PIFEXTHDR)) &&
  2760. (DWORD_PTR)lpph >= (DWORD_PTR)LPPIF_FIELDOFF(stdpifext)) {
  2761. extnxthdrfloff = lpph->extnxthdrfloff;
  2762. if ((DWORD_PTR)LPPH_OFF(lpph->extfileoffset) >= (DWORD_PTR)lpGroup)
  2763. lpph->extfileoffset -= (WORD) cbGroup;
  2764. if (lpphGroup) {
  2765. if ((DWORD_PTR)LPPH_OFF(lpph->extfileoffset) >= (DWORD_PTR)lpphGroup)
  2766. lpph->extfileoffset -= sizeof(PIFEXTHDR);
  2767. if ((DWORD_PTR)LPPH_OFF(lpph->extnxthdrfloff) == (DWORD_PTR)lpphGroup)
  2768. extnxthdrfloff = lpph->extnxthdrfloff = lpphGroup->extnxthdrfloff;
  2769. }
  2770. if (extnxthdrfloff == LASTHDRPTR)
  2771. break;
  2772. if ((DWORD_PTR)LPPH_OFF(lpph->extnxthdrfloff) >= (DWORD_PTR)lpGroup)
  2773. lpph->extnxthdrfloff -= (WORD) cbGroup;
  2774. if (lpphGroup)
  2775. if ((DWORD_PTR)LPPH_OFF(lpph->extnxthdrfloff) >= (DWORD_PTR)lpphGroup)
  2776. lpph->extnxthdrfloff -= sizeof(PIFEXTHDR);
  2777. lpph = (LPPIFEXTHDR)LPPIF_OFF(extnxthdrfloff);
  2778. }
  2779. // Next, move everything up over the data, then adjust lpph as
  2780. // needed and move everything up over the header (this must be done
  2781. // in two discrete steps, because we shouldn't assume anything
  2782. // about the data's location relative to its header).
  2783. hmemcpy(lpGroup, (LPBYTE)lpGroup+cbGroup,
  2784. (DWORD_PTR)LPPIF_OFF(ppl->cbPIFData) - (DWORD_PTR)((LPBYTE)lpGroup+cbGroup));
  2785. if (lpphGroup) {
  2786. if ((DWORD_PTR)lpphGroup >= (DWORD_PTR)((LPBYTE)lpGroup+cbGroup))
  2787. lpphGroup -= cbGroup;
  2788. hmemcpy(lpphGroup, lpphGroup+1,
  2789. (DWORD_PTR)LPPIF_OFF(ppl->cbPIFData) - (DWORD_PTR)((LPBYTE)lpphGroup+1+cbGroup));
  2790. cbGroup += sizeof(PIFEXTHDR);
  2791. }
  2792. ResizePIFData(ppl, -cbGroup);
  2793. ppl->flProp |= PROP_DIRTY | PROP_TRUNCATE;
  2794. ppl->cLocks--;
  2795. }
  2796. ppl->cLocks--;
  2797. return fSuccess;
  2798. }
  2799. /** GetGroupData - get ptr to property group (by name)
  2800. *
  2801. * INPUT
  2802. * ppl -> property (assumes it is LOCKED)
  2803. * lpszGroup -> property group; may be one of the following:
  2804. * "WINDOWS 286 3.0"
  2805. * "WINDOWS 386 3.0"
  2806. * "WINDOWS PIF.400"
  2807. * or any other group name that is the name of a valid PIF extension.
  2808. * if NULL, then *lpcbGroup is a 0-based index of the group we are looking for
  2809. * lpcbGroup -> where to return size of group data (NULL if not)
  2810. * lplpph -> where to return ptr to pif extension header, if any (NULL if not)
  2811. *
  2812. * OUTPUT
  2813. * Returns ptr to property group info, NULL if not found
  2814. */
  2815. void *GetGroupData(PPROPLINK ppl, LPCSTR lpszGroup,
  2816. LPINT lpcbGroup, LPPIFEXTHDR *lplpph)
  2817. {
  2818. BOOL fFixMe;
  2819. LPPIFEXTHDR lpph;
  2820. FunctionName(GetGroupData);
  2821. if (!ppl->lpPIFData)
  2822. return NULL;
  2823. lpph = (LPPIFEXTHDR)LPPIF_FIELDOFF(stdpifext);
  2824. while ((DWORD_PTR)lpph <= (DWORD_PTR)LPPIF_OFF(ppl->cbPIFData-sizeof(PIFEXTHDR)) &&
  2825. (DWORD_PTR)lpph >= (DWORD_PTR)LPPIF_FIELDOFF(stdpifext)) {
  2826. // PIFEDIT 3.x can trash the first byte of our extended portion
  2827. // (generally with a zero), so try to recover by stuffing the first
  2828. // character of the group we're looking for into the signature;
  2829. // if the rest of the signature matches, great, if it doesn't, then
  2830. // re-zero it.
  2831. if (!lpszGroup) {
  2832. // searching by index *lpcbGroup
  2833. if (!(*lpcbGroup)--) {
  2834. if (lplpph)
  2835. *lplpph = lpph;
  2836. *lpcbGroup = lpph->extsizebytes;
  2837. return lpph;
  2838. }
  2839. }
  2840. else {
  2841. if (FALSE != (fFixMe = !lpph->extsig[0])) // attempt to fix
  2842. lpph->extsig[0] = *lpszGroup;
  2843. if (lstrcmpiA(lpph->extsig, lpszGroup) == 0) {
  2844. if (lplpph)
  2845. *lplpph = lpph;
  2846. if (lpcbGroup)
  2847. *lpcbGroup = lpph->extsizebytes;
  2848. if (lpph->extfileoffset >= (WORD)ppl->cbPIFData) {
  2849. ASSERTFAIL();
  2850. return NULL;
  2851. }
  2852. return (LPBYTE)LPPIF_OFF(lpph->extfileoffset);
  2853. }
  2854. if (fFixMe) // fix failed (this time anyway)
  2855. lpph->extsig[0] = 0;
  2856. }
  2857. if (lpph->extnxthdrfloff == LASTHDRPTR)
  2858. break;
  2859. lpph = (LPPIFEXTHDR)LPPIF_OFF(lpph->extnxthdrfloff);
  2860. }
  2861. // If we didn't get anywhere, check if this is a "really old" PIF;
  2862. // ie, one without any headers; if so, then if all they were asking for
  2863. // was the old stuff, return it
  2864. if (ppl->cbPIFData == sizeof(STDPIF) && lpszGroup) {
  2865. if (lstrcmpiA(szSTDHDRSIG, lpszGroup) == 0) {
  2866. if (lplpph)
  2867. *lplpph = NULL;
  2868. if (lpcbGroup)
  2869. *lpcbGroup = sizeof(STDPIF);
  2870. return ppl->lpPIFData;
  2871. }
  2872. }
  2873. return NULL;
  2874. }
  2875. /** AppWizard - call the AppWizard CPL (appwiz.cpl)
  2876. */
  2877. TCHAR c_szAPPWIZ[] = TEXT("appwiz.cpl");
  2878. CHAR c_szAppWizard[] = "AppWizard";
  2879. typedef DWORD (WINAPI *LPAPPWIZARD)(HWND hwnd, HANDLE i, UINT ui);
  2880. UINT WINAPI AppWizard(HWND hwnd, HANDLE hProps, UINT action)
  2881. {
  2882. DWORD err = 42;
  2883. LPAPPWIZARD XAppWizard;
  2884. HINSTANCE hAppWizard;
  2885. hAppWizard = LoadLibrary(c_szAPPWIZ);
  2886. if (hAppWizard)
  2887. {
  2888. if (NULL != (XAppWizard = (LPAPPWIZARD)GetProcAddress(hAppWizard, c_szAppWizard)))
  2889. {
  2890. err = XAppWizard( hwnd, hProps, action );
  2891. }
  2892. FreeLibrary((HINSTANCE)hAppWizard);
  2893. }
  2894. return (UINT)err;
  2895. }