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.

2247 lines
81 KiB

  1. /****************************************************************************\
  2. APPINST.C / OPK Wizard (OPKWIZ.EXE)
  3. Microsoft Confidential
  4. Copyright (c) Microsoft Corporation 1998
  5. All rights reserved
  6. Source file for the OPK Wizard that contains the external and internal
  7. functions used by the "application preinstallation" wizard page.
  8. 06/99 - Jason Cohen (JCOHEN)
  9. Updated this source file for the OPK Wizard as part of the
  10. Millennium rewrite.
  11. 09/2000 - Stephen Lodwick (STELO)
  12. Ported OPK Wizard to Whistler
  13. \****************************************************************************/
  14. //
  15. // Include File(s):
  16. //
  17. #include "pch.h"
  18. #include "wizard.h"
  19. #include "resource.h"
  20. #include "appinst.h"
  21. #include <setupapi.h>
  22. //
  23. // Internal Defined Value(s):
  24. //
  25. #define INI_SEC_RESERVEDNAMES _T("AppNames")
  26. #define INF_KEY_PREINSTALL _T("\"%s\",\"\"\"%s\"\" %s\"")
  27. #define INF_KEY_PREINSTALL_ADV _T("\"%s\",%s,%s")
  28. #define INI_KEY_SIG _T("Signature")
  29. #define INI_VAL_SIG _T("$CHICAGO$")
  30. #define STR_INI_SEC_ADVAPP _T("AppPre%s%2.2d")
  31. #define STR_INI_SEC_ADVAPP_STAGE _T(".%s")
  32. #define APP_ADD 0
  33. #define APP_DELETE 1
  34. #define APP_MOVE_DOWN 2
  35. //
  36. // Internal Type Definition(s):
  37. //
  38. typedef struct _RADIOBUTTONS
  39. {
  40. int iButtonId;
  41. INSTALLTECH itSectionType;
  42. }
  43. RADIOBUTTONS, *PRADIOBUTTONS, *LPRADIOBUTTONS;
  44. //
  45. // Internal Constant Global(s):
  46. //
  47. // Resource ID for column header string.
  48. //
  49. const UINT g_cuHeader[] =
  50. {
  51. IDS_PREINSTALL_NAME,
  52. IDS_PREINSTALL_COMMAND
  53. };
  54. // Column format flags.
  55. //
  56. const UINT g_cuFormat[] =
  57. {
  58. LVCFMT_LEFT,
  59. LVCFMT_LEFT
  60. };
  61. // Column width in percent (the last one is
  62. // zero so that it uses the rest of the space).
  63. //
  64. const UINT g_cuWidth[] =
  65. {
  66. 50,
  67. 50
  68. };
  69. const INSTALLTECHS g_cSectionTypes[] =
  70. {
  71. { installtechApp, INI_VAL_WBOM_APP },
  72. { installtechMSI, INI_VAL_WBOM_MSI },
  73. { installtechINF, INI_VAL_WBOM_INF },
  74. };
  75. const INSTALLTYPES g_cInstallTypes[] =
  76. {
  77. { installtypeStandard, INI_VAL_WBOM_STANDARD },
  78. { installtypeStage, INI_VAL_WBOM_STAGE },
  79. { installtypeAttach, INI_VAL_WBOM_ATTACH },
  80. { installtypeDetach, INI_VAL_WBOM_DETACH },
  81. };
  82. const RADIOBUTTONS g_crbChecked[] =
  83. {
  84. { IDC_APP_TYPE_GEN, installtechApp },
  85. { IDC_APP_TYPE_MSI, installtechMSI },
  86. { IDC_APP_TYPE_INF, installtechINF },
  87. };
  88. //
  89. // Internal Golbal Variable(s):
  90. //
  91. LPAPPENTRY g_lpAppHead = NULL;
  92. HMENU g_hMenu;
  93. HANDLE g_hArrowUp = NULL;
  94. HANDLE g_hArrowDn = NULL;
  95. //
  96. // Other Internal Defined Value(s):
  97. //
  98. #define NUM_COLUMNS ARRAYSIZE(g_cuHeader)
  99. //
  100. // Internal Function Prototype(s):
  101. //
  102. BOOL CALLBACK OneAppDlgProc(HWND, UINT, WPARAM, LPARAM);
  103. static BOOL OnInit(HWND, HWND, LPARAM);
  104. static void OnCommand(HWND, INT, HWND, UINT);
  105. static LRESULT OnListViewNotify(HWND, UINT, WPARAM, NMLVDISPINFO*);
  106. static BOOL SaveData(HWND);
  107. static BOOL SaveOneApp(HWND, LPAPPENTRY);
  108. static LPAPPENTRY ManageAppList(LPLPAPPENTRY, LPAPPENTRY, DWORD);
  109. static void AddAppToListView(HWND, LPAPPENTRY);
  110. static BOOL RefreshAppList(HWND, LPAPPENTRY);
  111. static BOOL AdvancedView(HWND hwnd, BOOL bChange);
  112. static void CleanupSections(LPTSTR lpSection, BOOL bStage);
  113. static void StrCpyDbl(LPTSTR lpDst, LPTSTR lpSrc);
  114. static BOOL FindUncPath(LPTSTR lpPath, DWORD cbPath);
  115. static void EnableControls(HWND hwnd, UINT uId);
  116. static BOOL AppInternal(LPTSTR lpszAppName);
  117. //
  118. // External Function(s):
  119. //
  120. LPAPPENTRY OpenAppList(LPTSTR lpIniFile)
  121. {
  122. LPAPPENTRY lpAppHead = NULL;
  123. HINF hInf;
  124. DWORD dwErr;
  125. HRESULT hrPrintf;
  126. // Open up the INF we need to look through to build our app list.
  127. //
  128. if ( (hInf = SetupOpenInfFile(lpIniFile, NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, &dwErr)) != INVALID_HANDLE_VALUE )
  129. {
  130. INFCONTEXT InfContext;
  131. BOOL bRet,
  132. bBadApp;
  133. APPENTRY app;
  134. TCHAR szIniVal[MAX_PATH];
  135. //
  136. // Now we look for and add our user added apps by searching thru
  137. // the INI_SEC_WBOM_PREINSTALL first.
  138. //
  139. // Lines in this section are of the form:
  140. // AppName=AppPath,n
  141. //
  142. // The fields start at index one, as index zero is for the key,
  143. // which we don't have.
  144. //
  145. // Loop thru each line in the INI_SEC_WBOM_PREINSTALL INF section.
  146. //
  147. for ( bRet = SetupFindFirstLine(hInf, INI_SEC_WBOM_PREINSTALL, NULL, &InfContext);
  148. bRet;
  149. bRet = SetupFindNextLine(&InfContext, &InfContext) )
  150. {
  151. // Clear the app structure.
  152. //
  153. ZeroMemory(&app, sizeof(APPENTRY));
  154. bBadApp = FALSE;
  155. // Get the AppName.
  156. //
  157. SetupGetStringField(&InfContext, 1, app.szDisplayName, AS(app.szDisplayName), NULL);
  158. // Get Command line path.
  159. //
  160. SetupGetStringField(&InfContext, 2, app.szSourcePath, AS(app.szSourcePath), NULL);
  161. // Check to see if this is an internal app.
  162. //
  163. if ( app.szDisplayName[0] &&
  164. AppInternal(app.szDisplayName) )
  165. {
  166. SETBIT(app.dwFlags, APP_FLAG_INTERNAL, TRUE);
  167. }
  168. // By default this is not an advanced section type, so this is set to
  169. // undefined.
  170. //
  171. app.itSectionType = installtechUndefined;
  172. // Get advanced section type if there is one
  173. //
  174. szIniVal[0] = NULLCHR;
  175. SetupGetStringField(&InfContext, 3, szIniVal, AS(szIniVal), NULL);
  176. if ( szIniVal[0] )
  177. {
  178. DWORD dwIndex;
  179. // Lets make sure this has a valid section type.
  180. //
  181. for ( dwIndex = 0; ( dwIndex < AS(g_cSectionTypes) ) && ( installtechUndefined == app.itSectionType ); dwIndex++ )
  182. {
  183. if ( lstrcmpi(szIniVal, g_cSectionTypes[dwIndex].lpszDescription) == 0 )
  184. app.itSectionType = g_cSectionTypes[dwIndex].InstallTech;
  185. }
  186. // Not a vallid app entry if it is still undefined at this point.
  187. //
  188. if ( installtechUndefined != app.itSectionType )
  189. {
  190. INSTALLTYPE itInstallType = installtypeStandard;
  191. LPTSTR lpEnd;
  192. // Since this is an advanced section type, the command line
  193. // is actually the section name.
  194. //
  195. lstrcpyn(app.szSectionName, app.szSourcePath, AS(app.szSectionName));
  196. app.szSourcePath[0] = NULLCHR;
  197. // Now get the install type parameter.
  198. //
  199. szIniVal[0] = NULLCHR;
  200. GetPrivateProfileString(app.szSectionName, INI_KEY_WBOM_INSTALLTYPE, NULLSTR, szIniVal, AS(szIniVal), lpIniFile);
  201. // Test to see if we are staging this app.
  202. //
  203. for ( dwIndex = 0; ( dwIndex < AS(g_cInstallTypes) ) && ( installtypeStandard == itInstallType ); dwIndex++ )
  204. {
  205. if ( lstrcmpi(szIniVal, g_cInstallTypes[dwIndex].lpszDescription) == 0 )
  206. itInstallType = g_cInstallTypes[dwIndex].InstallType;
  207. }
  208. //
  209. // Here we read in all the settings that are either in the staged
  210. // or the standard section.
  211. //
  212. // Get the source path.
  213. //
  214. GetPrivateProfileString(app.szSectionName, INI_KEY_WBOM_SOURCEPATH, NULLSTR, app.szSourcePath, AS(app.szSourcePath), lpIniFile);
  215. // Get the settings specific to the type of install.
  216. //
  217. switch ( itInstallType )
  218. {
  219. case installtypeStandard:
  220. break;
  221. case installtypeStage:
  222. // Set this bit so we know it is a staged install.
  223. //
  224. SETBIT(app.dwFlags, APP_FLAG_STAGE, TRUE);
  225. // Need check to see if we need to chop off the staging part of
  226. // the section name.
  227. //
  228. lpEnd = (app.szSectionName + lstrlen(app.szSectionName)) - (lstrlen(szIniVal) + 1);
  229. if ( ( lstrlen(app.szSectionName) > lstrlen(szIniVal) ) &&
  230. ( _T('.') == *lpEnd ) &&
  231. ( lstrcmpi(CharNext(lpEnd), szIniVal) == 0 ) )
  232. {
  233. *lpEnd = NULLCHR;
  234. }
  235. break;
  236. case installtypeAttach:
  237. case installtypeDetach:
  238. // If it is attach or detach, then we didn't write the setting and
  239. // the user must have manually edited the file. Right now we can't
  240. // handle this so it will just not show up in our list.
  241. //
  242. // TODO: We probably can be a little smarter about this.
  243. //
  244. bBadApp = TRUE;
  245. break;
  246. }
  247. // Now get the rest of the settings if we are to continue.
  248. //
  249. if ( !bBadApp )
  250. {
  251. // If were are staging, look in the attach section for all
  252. // of the following settings.
  253. //
  254. if ( GETBIT(app.dwFlags, APP_FLAG_STAGE) )
  255. {
  256. // Need the end of the section for staged installs.
  257. //
  258. lpEnd = app.szSectionName + lstrlen(app.szSectionName);
  259. hrPrintf=StringCchPrintf(lpEnd, (MAX_SECTIONNAME-lstrlen(app.szSectionName)), STR_INI_SEC_ADVAPP_STAGE, INI_VAL_WBOM_ATTACH);
  260. }
  261. else
  262. lpEnd = NULL;
  263. // Get the setup file.
  264. //
  265. GetPrivateProfileString(app.szSectionName, INI_KEY_WBOM_SETUPFILE, NULLSTR, app.szSetupFile, AS(app.szSetupFile), lpIniFile);
  266. // Get target path (only really used for staged installs).
  267. //
  268. GetPrivateProfileString(app.szSectionName, INI_KEY_WBOM_TARGETPATH, NULLSTR, app.szStagePath, AS(app.szStagePath), lpIniFile);
  269. // Also get the command line arguments.
  270. //
  271. GetPrivateProfileString(app.szSectionName, INI_KEY_WBOM_CMDLINE, NULLSTR, app.szCommandLine, AS(app.szCommandLine), lpIniFile);
  272. // Get the INF key if it is there.
  273. //
  274. GetPrivateProfileString(app.szSectionName, INI_KEY_WBOM_SECTIONNAME, NULLSTR, app.szInfSectionName, AS(app.szInfSectionName), lpIniFile);
  275. // Get the reboot key.
  276. //
  277. szIniVal[0] = NULLCHR;
  278. GetPrivateProfileString(app.szSectionName, INI_KEY_WBOM_REBOOT, NULLSTR, szIniVal, AS(szIniVal), lpIniFile);
  279. if ( szIniVal[0] && ( LSTRCMPI( szIniVal, WBOM_YES) == 0 ) )
  280. SETBIT(app.dwFlags, APP_FLAG_REBOOT, TRUE);
  281. // Make sure the section name goes back to the proper format.
  282. //
  283. if ( lpEnd )
  284. *lpEnd = NULLCHR;
  285. }
  286. }
  287. else
  288. bBadApp = TRUE;
  289. }
  290. else
  291. {
  292. DWORD dwArgs;
  293. LPTSTR *lpArg,
  294. lpFilePart;
  295. // ISSUE-2002/02/27-stelo,swamip - Should initialize pointers before passing to GetLineArgs
  296. //
  297. // Need to split the command line into the setup file and command
  298. // arguments. Call GetLineArgs() to convert the line buffer into
  299. // a list of arguments.
  300. //
  301. if ( (dwArgs = GetLineArgs(app.szSourcePath, &lpArg, &lpFilePart)) > 0 )
  302. {
  303. // We need a pointer to a string with just the args.
  304. //
  305. if ( ( dwArgs > 1 ) && lpFilePart )
  306. lstrcpyn(app.szCommandLine, lpFilePart, AS(app.szCommandLine));
  307. // Use the first arg for our file name.
  308. //
  309. lstrcpyn(app.szSourcePath, lpArg[0], AS(app.szSourcePath));
  310. // Now free the string and list of points returned.
  311. //
  312. FREE(*lpArg);
  313. FREE(lpArg);
  314. }
  315. // Now need to split the file name from the source path. If it fails,
  316. // oh well... no setup file. That is bad but what can you do.
  317. //
  318. if ( GetFullPathName(app.szSourcePath, AS(app.szSetupFile), app.szSetupFile, &lpFilePart) &&
  319. app.szSetupFile[0] &&
  320. lpFilePart )
  321. {
  322. DWORD dwPathLen = lstrlen(app.szSourcePath) - lstrlen(lpFilePart);
  323. lstrcpyn(app.szSetupFile, app.szSourcePath + dwPathLen, AS(app.szSetupFile));
  324. app.szSourcePath[dwPathLen] = NULLCHR;
  325. }
  326. }
  327. // Only add it to the list if it is a valid app entry.
  328. //
  329. if ( !bBadApp )
  330. ManageAppList(&lpAppHead, &app, APP_ADD);
  331. }
  332. // We are done, so close the INF file.
  333. //
  334. SetupCloseInfFile(hInf);
  335. }
  336. // Return the head pointer of the list we allocated with the apps
  337. // read in... or NULL if there were no apps.
  338. //
  339. return lpAppHead;
  340. }
  341. void CloseAppList(LPAPPENTRY lpAppHead)
  342. {
  343. ManageAppList(&lpAppHead, NULL, 0);
  344. }
  345. BOOL SaveAppList(LPAPPENTRY lpAppHead, LPTSTR lpszIniFile, LPTSTR lpszAltIniFile)
  346. {
  347. LPAPPENTRY lpApp;
  348. LPTSTR lpSection,
  349. lpIndex;
  350. TCHAR szKey[MAX_STRING],
  351. szData[MAX_STRING];
  352. UINT uIndex = 1;
  353. HRESULT hrPrintf;
  354. // Dynamically allocate our section buffer since it is quite large.
  355. //
  356. if ( (lpSection = MALLOC(MAX_SECTION * sizeof(TCHAR))) == NULL )
  357. {
  358. return FALSE;
  359. }
  360. // Delete the current INF_SEC_RUNONCE and INI_SEC_WBOM_PREINSTALL.
  361. // sections. Passing NULL for the key name will do the trick.
  362. //
  363. WritePrivateProfileString(INI_SEC_WBOM_PREINSTALL, NULL, NULL, lpszIniFile);
  364. WritePrivateProfileString(INI_SEC_WBOM_PREINSTALL, NULL, NULL, lpszAltIniFile);
  365. // Loop through all the items so we can write them back out to the INF file.
  366. //
  367. lpIndex = lpSection;
  368. for ( lpApp = lpAppHead; lpApp; lpApp = lpApp->lpNext, uIndex++ )
  369. {
  370. TCHAR szDisplayName[MAX_DISPLAYNAME * 2],
  371. szCommandLine[MAX_CMDLINE * 2],
  372. szSetupPathFile[MAX_PATH];
  373. LPTSTR lpTest;
  374. // We have to double any quotes in the string so that
  375. // when we read them back they look the same way we
  376. // want them to.
  377. //
  378. StrCpyDbl(szDisplayName, lpApp->szDisplayName);
  379. StrCpyDbl(szCommandLine, lpApp->szCommandLine);
  380. // We also have to make sure the source path doesn't have a trailing backslash,
  381. // otherwise the stupid setupapi apis think that the next line is supposed to be
  382. // added onto the source path line.
  383. //
  384. if ( ( lpTest = CharPrev(lpApp->szSourcePath, lpApp->szSourcePath + lstrlen(lpApp->szSourcePath)) ) &&
  385. ( _T('\\') == *lpTest ) )
  386. {
  387. // Get rid of the trailing backslash.
  388. //
  389. *lpTest = NULLCHR;
  390. }
  391. switch ( lpApp->itSectionType )
  392. {
  393. case installtechUndefined:
  394. // Check if there is a section name.
  395. //
  396. if ( lpApp->szSectionName[0] )
  397. {
  398. // There is, so this must have been an advanced
  399. // type install and they changed it to a standard one.
  400. // Need to remove an sections that might be around.
  401. //
  402. CleanupSections(lpApp->szSectionName, TRUE);
  403. CleanupSections(lpApp->szSectionName, FALSE);
  404. lpApp->szSectionName[0] = NULLCHR;
  405. }
  406. // Now write the one line entry to oem runonce.
  407. //
  408. lstrcpyn(szSetupPathFile, lpApp->szSourcePath, AS(szSetupPathFile));
  409. AddPathN(szSetupPathFile, lpApp->szSetupFile,AS(szSetupPathFile));
  410. hrPrintf=StringCchPrintf(lpIndex, (MAX_SECTION-(lpIndex-lpSection)),
  411. INF_KEY_PREINSTALL, szDisplayName, szSetupPathFile, szCommandLine);
  412. lpIndex+= lstrlen(lpIndex)+1;
  413. break;
  414. case installtechApp:
  415. case installtechMSI:
  416. case installtechINF:
  417. {
  418. LPCTSTR lpSectionType;
  419. DWORD dwIndex;
  420. // Lets find the string to use for this section type.
  421. //
  422. lpSectionType = NULL;
  423. for ( dwIndex = 0; ( dwIndex < AS(g_cSectionTypes) ) && ( NULL == lpSectionType ); dwIndex++ )
  424. {
  425. if ( lpApp->itSectionType == g_cSectionTypes[dwIndex].InstallTech )
  426. lpSectionType = g_cSectionTypes[dwIndex].lpszDescription;
  427. }
  428. // We can only save this app if we have a section type.
  429. //
  430. if ( lpSectionType )
  431. {
  432. LPTSTR lpEnd = NULL;
  433. // Make sure we have a section name to write to.
  434. //
  435. if ( NULLCHR == lpApp->szSectionName[0] )
  436. {
  437. BOOL bFound = FALSE;
  438. DWORD dwCount = 1;
  439. TCHAR szBuffer[256];
  440. // Try to find a section name that isn't used.
  441. //
  442. do
  443. {
  444. // First check if the section exists.
  445. //
  446. hrPrintf=StringCchPrintf(lpApp->szSectionName, AS(lpApp->szSectionName), STR_INI_SEC_ADVAPP, lpSectionType, dwCount++);
  447. if ( GetPrivateProfileSection(lpApp->szSectionName, szBuffer, AS(szBuffer), lpszIniFile) == 0 )
  448. {
  449. // Section doesn't exist, so make sure that the
  450. // section + .stage also doesn't exist.
  451. //
  452. lpEnd = lpApp->szSectionName + lstrlen(lpApp->szSectionName);
  453. hrPrintf=StringCchPrintf(lpEnd, (MAX_SECTIONNAME-lstrlen(lpApp->szSectionName)), STR_INI_SEC_ADVAPP_STAGE, INI_VAL_WBOM_STAGE);
  454. bFound = ( GetPrivateProfileSection(lpApp->szSectionName, szBuffer, AS(szBuffer), lpszIniFile) == 0 );
  455. *lpEnd = NULLCHR;
  456. lpEnd = NULL;
  457. }
  458. }
  459. while ( !bFound && ( dwCount < 100 ) );
  460. }
  461. else
  462. {
  463. // We do this because the user might have changed
  464. // this app to a staged or not staged after it was
  465. // already saved to the winbom. This cleans up any
  466. // sections we don't want laying around.
  467. //
  468. CleanupSections(lpApp->szSectionName, GETBIT(lpApp->dwFlags, APP_FLAG_STAGE));
  469. }
  470. // If we are staging, we have three sections to write out first.
  471. //
  472. if ( GETBIT(lpApp->dwFlags, APP_FLAG_STAGE) )
  473. {
  474. // First save off the end pointer.
  475. //
  476. lpEnd = lpApp->szSectionName + lstrlen(lpApp->szSectionName);
  477. //
  478. // Lets write out the attach section first.
  479. //
  480. // Create the attach section name first.
  481. //
  482. hrPrintf=StringCchPrintf(lpEnd, (MAX_SECTIONNAME-lstrlen(lpApp->szSectionName)), STR_INI_SEC_ADVAPP_STAGE, INI_VAL_WBOM_ATTACH);
  483. // First write out the type of install (attach in this case).
  484. //
  485. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_INSTALLTYPE, INI_VAL_WBOM_ATTACH, lpszIniFile);
  486. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_INSTALLTYPE, INI_VAL_WBOM_ATTACH, lpszAltIniFile);
  487. // Always write out the target path to the attach section.
  488. //
  489. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_TARGETPATH, lpApp->szStagePath, lpszIniFile);
  490. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_TARGETPATH, lpApp->szStagePath, lpszAltIniFile);
  491. }
  492. else
  493. {
  494. // Must first write out the type of install (for standard we just remove the keys).
  495. //
  496. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_INSTALLTYPE, NULL, lpszIniFile);
  497. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_INSTALLTYPE, NULL, lpszAltIniFile);
  498. // Always write out the source path for standard.
  499. //
  500. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_SOURCEPATH, lpApp->szSourcePath, lpszIniFile);
  501. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_SOURCEPATH, lpApp->szSourcePath, lpszAltIniFile);
  502. }
  503. //
  504. // These are the settings writen the same for both attach and standard.
  505. //
  506. // Always write out the setup program.
  507. //
  508. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_SETUPFILE, lpApp->szSetupFile, lpszIniFile);
  509. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_SETUPFILE, lpApp->szSetupFile, lpszAltIniFile);
  510. // Always write out the command line arguments.
  511. //
  512. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_CMDLINE, lpApp->szCommandLine, lpszIniFile);
  513. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_CMDLINE, lpApp->szCommandLine, lpszAltIniFile);
  514. // Write out the reboot key if specified (remove if not).
  515. // This is only done in attach, we don't give this option
  516. // in the sections so don't touch it.
  517. //
  518. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_REBOOT, GETBIT(lpApp->dwFlags, APP_FLAG_REBOOT) ? WBOM_YES : NULL, lpszIniFile);
  519. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_REBOOT, GETBIT(lpApp->dwFlags, APP_FLAG_REBOOT) ? WBOM_YES : NULL, lpszAltIniFile);
  520. // Write out the inf section name key if specified this is
  521. // an INF install (remove if not).
  522. // This is only done in attach, we don't wrtie this option
  523. // in the sections so don't touch it.
  524. //
  525. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_SECTIONNAME, ( installtechINF == lpApp->itSectionType ) ? lpApp->szInfSectionName : NULL, lpszIniFile);
  526. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_SECTIONNAME, ( installtechINF == lpApp->itSectionType ) ? lpApp->szInfSectionName : NULL, lpszAltIniFile);
  527. // If we are staging, we still have two more sections to write out.
  528. //
  529. if ( GETBIT(lpApp->dwFlags, APP_FLAG_STAGE) )
  530. {
  531. //
  532. // Here is where we write out the detach section.
  533. //
  534. // Create the detach section next.
  535. //
  536. hrPrintf=StringCchPrintf(lpEnd, (MAX_SECTIONNAME-lstrlen(lpApp->szSectionName)), STR_INI_SEC_ADVAPP_STAGE, INI_VAL_WBOM_DETACH);
  537. // First write out the type of install (detach in this case).
  538. //
  539. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_INSTALLTYPE, INI_VAL_WBOM_DETACH, lpszIniFile);
  540. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_INSTALLTYPE, INI_VAL_WBOM_DETACH, lpszAltIniFile);
  541. // Always write out the target path to the detach section.
  542. //
  543. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_TARGETPATH, lpApp->szStagePath, lpszIniFile);
  544. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_TARGETPATH, lpApp->szStagePath, lpszAltIniFile);
  545. //
  546. // Here is where we write out the stage section.
  547. //
  548. // Create the stage section next.
  549. //
  550. hrPrintf=StringCchPrintf(lpEnd, (MAX_SECTIONNAME-lstrlen(lpApp->szSectionName)), STR_INI_SEC_ADVAPP_STAGE, INI_VAL_WBOM_STAGE);
  551. // First write out the type of install (stage in this case).
  552. //
  553. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_INSTALLTYPE, INI_VAL_WBOM_STAGE, lpszIniFile);
  554. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_INSTALLTYPE, INI_VAL_WBOM_STAGE, lpszAltIniFile);
  555. // Always write out the target path to the stage section.
  556. //
  557. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_TARGETPATH, lpApp->szStagePath, lpszIniFile);
  558. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_TARGETPATH, lpApp->szStagePath, lpszAltIniFile);
  559. // Always write out the source path to the stage section.
  560. //
  561. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_SOURCEPATH, lpApp->szSourcePath, lpszIniFile);
  562. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_SOURCEPATH, lpApp->szSourcePath, lpszAltIniFile);
  563. // Only for MSI do we write out the setup file to the stage section.
  564. //
  565. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_SETUPFILE, ( installtechMSI == lpApp->itSectionType ) ? lpApp->szSetupFile : NULL, lpszIniFile);
  566. WritePrivateProfileString(lpApp->szSectionName, INI_KEY_WBOM_SETUPFILE, ( installtechMSI == lpApp->itSectionType ) ? lpApp->szSetupFile : NULL, lpszAltIniFile);
  567. }
  568. // Write out the line now that we know we have a section name
  569. // we are going to use first.
  570. //
  571. hrPrintf=StringCchPrintf(lpIndex, (MAX_SECTION-(lpIndex-lpSection)),
  572. INF_KEY_PREINSTALL_ADV, szDisplayName, lpApp->szSectionName, lpSectionType);
  573. lpIndex+= lstrlen(lpIndex)+1;
  574. // Make sure the section name goes back to the proper format.
  575. //
  576. if ( lpEnd )
  577. *lpEnd = NULLCHR;
  578. }
  579. break;
  580. }
  581. }
  582. // Skip ahead so we don't overwrite the null terminator
  583. //
  584. }
  585. // Double null terminate the section end, write the section, and then free the buffer.
  586. //
  587. *lpIndex = NULLCHR;
  588. WritePrivateProfileSection(INI_SEC_WBOM_PREINSTALL, lpSection, lpszIniFile);
  589. WritePrivateProfileSection(INI_SEC_WBOM_PREINSTALL, lpSection, lpszAltIniFile);
  590. FREE(lpSection);
  591. return TRUE;
  592. }
  593. BOOL InsertApp(LPAPPENTRY * lplpAppHead, LPAPPENTRY lpApp)
  594. {
  595. return ManageAppList(lplpAppHead, lpApp, APP_ADD) ? TRUE : FALSE;
  596. }
  597. BOOL RemoveApp(LPAPPENTRY * lplpAppHead, LPAPPENTRY lpApp)
  598. {
  599. ManageAppList(lplpAppHead, lpApp, APP_DELETE);
  600. return TRUE;
  601. }
  602. LRESULT CALLBACK AppInstallDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  603. {
  604. switch (uMsg)
  605. {
  606. HANDLE_MSG(hwnd, WM_INITDIALOG, OnInit);
  607. HANDLE_MSG(hwnd, WM_COMMAND, OnCommand);
  608. case WM_NOTIFY:
  609. switch ( wParam )
  610. {
  611. case IDC_APPLIST:
  612. // Notify message from the list view control.
  613. //
  614. OnListViewNotify(hwnd, uMsg, wParam, (NMLVDISPINFO*) lParam);
  615. break;
  616. default:
  617. switch ( ((NMHDR FAR *) lParam)->code )
  618. {
  619. case PSN_KILLACTIVE:
  620. case PSN_RESET:
  621. case PSN_WIZBACK:
  622. case PSN_WIZFINISH:
  623. break;
  624. case PSN_WIZNEXT:
  625. if ( !SaveData(hwnd) )
  626. WIZ_FAIL(hwnd);
  627. break;
  628. case PSN_QUERYCANCEL:
  629. WIZ_CANCEL(hwnd);
  630. break;
  631. case PSN_HELP:
  632. WIZ_HELP();
  633. break;
  634. case PSN_SETACTIVE:
  635. g_App.dwCurrentHelp = IDH_APPINSTALL;
  636. WIZ_BUTTONS(hwnd, PSWIZB_BACK | PSWIZB_NEXT);
  637. // Press next if the user is in auto mode
  638. //
  639. WIZ_NEXTONAUTO(hwnd, PSBTN_NEXT);
  640. break;
  641. default:
  642. return FALSE;
  643. }
  644. }
  645. break;
  646. case WM_DESTROY:
  647. // Free the memory for the app list.
  648. //
  649. CloseAppList(g_lpAppHead);
  650. g_lpAppHead = NULL;
  651. // Destroy the menu we loaded on INIT_DIALOG.
  652. //
  653. DestroyMenu(g_hMenu);
  654. if(g_hArrowUp)
  655. DestroyIcon(g_hArrowUp);
  656. if(g_hArrowDn)
  657. DestroyIcon(g_hArrowDn);
  658. return FALSE;
  659. default:
  660. return FALSE;
  661. }
  662. return TRUE;
  663. }
  664. //
  665. // Internal Function(s):
  666. //
  667. BOOL CALLBACK OneAppDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  668. {
  669. switch ( uMsg )
  670. {
  671. case WM_INITDIALOG:
  672. {
  673. LPAPPENTRY lpApp = (LPAPPENTRY) lParam;
  674. DWORD dwIndex;
  675. BOOL bFound;
  676. TCHAR szFullPath[MAX_PATH];
  677. // Save our APPENTRY pointer.
  678. //
  679. SetWindowLongPtr(hwnd, DWLP_USER, lParam);
  680. // We have to have an app struct.
  681. //
  682. if ( NULL == lpApp )
  683. return FALSE;
  684. // Limit the text which can be entered.
  685. //
  686. SendDlgItemMessage(hwnd, IDC_APP_NAME , EM_LIMITTEXT, AS(lpApp->szDisplayName) - 1, 0L);
  687. SendDlgItemMessage(hwnd, IDC_APP_PATH , EM_LIMITTEXT, AS(lpApp->szSetupFile) - 1, 0L);
  688. SendDlgItemMessage(hwnd, IDC_APP_ARGS , EM_LIMITTEXT, AS(lpApp->szCommandLine) - 1, 0L);
  689. SendDlgItemMessage(hwnd, IDC_APP_INF_SECTION , EM_LIMITTEXT, AS(lpApp->szInfSectionName) - 1, 0L);
  690. SendDlgItemMessage(hwnd, IDC_APP_STAGEPATH , EM_LIMITTEXT, AS(lpApp->szStagePath) - 1, 0L);
  691. // Make the full path with the setup file and source path.
  692. //
  693. lstrcpyn(szFullPath, lpApp->szSourcePath, AS(szFullPath));
  694. AddPathN(szFullPath, lpApp->szSetupFile,AS(szFullPath));
  695. // Set the edit and check box controls.
  696. //
  697. SetWindowText(GetDlgItem(hwnd, IDC_APP_NAME), lpApp->szDisplayName);
  698. SetWindowText(GetDlgItem(hwnd, IDC_APP_PATH), szFullPath);
  699. SetWindowText(GetDlgItem(hwnd, IDC_APP_ARGS), lpApp->szCommandLine);
  700. SetWindowText(GetDlgItem(hwnd, IDC_APP_INF_SECTION), lpApp->szInfSectionName);
  701. SetWindowText(GetDlgItem(hwnd, IDC_APP_STAGEPATH), lpApp->szStagePath);
  702. // Start with the standard view if this isn't an advanced install.
  703. //
  704. if ( installtechUndefined == lpApp->itSectionType )
  705. AdvancedView(hwnd, TRUE);
  706. // Select the correct radio button (select a default one first in case we
  707. // don't find the setting in the array, meaning it is installtechUndefined).
  708. //
  709. CheckRadioButton(hwnd, IDC_APP_TYPE_GEN, IDC_APP_TYPE_INF, IDC_APP_TYPE_GEN);
  710. bFound = FALSE;
  711. for ( dwIndex = 0; ( dwIndex < AS(g_crbChecked) ) && !bFound ; dwIndex++ )
  712. {
  713. if ( bFound = ( lpApp->itSectionType == g_crbChecked[dwIndex].itSectionType ) )
  714. CheckRadioButton(hwnd, IDC_APP_TYPE_GEN, IDC_APP_TYPE_INF, g_crbChecked[dwIndex].iButtonId);
  715. }
  716. // Set the check boxes that are set in the app struct.
  717. //
  718. CheckDlgButton(hwnd, IDC_APP_REBOOT, GETBIT(lpApp->dwFlags, APP_FLAG_REBOOT) ? BST_CHECKED : BST_UNCHECKED);
  719. CheckDlgButton(hwnd, IDC_APP_STAGE, GETBIT(lpApp->dwFlags, APP_FLAG_STAGE) ? BST_CHECKED : BST_UNCHECKED);
  720. // Make sure the right controls are enable/disabled.
  721. //
  722. EnableControls(hwnd, IDC_APP_STAGE);
  723. EnableControls(hwnd, IDC_APP_TYPE_GEN);
  724. // Always return false to WM_INITDIALOG.
  725. //
  726. return FALSE;
  727. }
  728. case WM_COMMAND:
  729. switch ( LOWORD(wParam) )
  730. {
  731. case IDOK:
  732. // Make sure we have valid info and then save to the app struct and fall
  733. // through to end the dialog.
  734. //
  735. if ( !SaveOneApp(hwnd, (LPAPPENTRY) GetWindowLongPtr(hwnd, DWLP_USER)) )
  736. break;
  737. case IDCANCEL:
  738. EndDialog(hwnd, LOWORD(wParam));
  739. break;
  740. case IDC_APP_ADVANCED:
  741. AdvancedView(hwnd, TRUE);
  742. break;
  743. case IDC_APP_STAGE:
  744. case IDC_APP_TYPE_GEN:
  745. case IDC_APP_TYPE_MSI:
  746. case IDC_APP_TYPE_INF:
  747. EnableControls(hwnd, LOWORD(wParam));
  748. break;
  749. case IDC_APP_BROWSE:
  750. {
  751. TCHAR szFileName[MAX_PATH] = NULLSTR;
  752. GetDlgItemText(hwnd, IDC_APP_PATH, szFileName, AS(szFileName));
  753. if ( BrowseForFile(hwnd, IDS_BROWSE, IDS_INSTFILES, IDS_EXE, szFileName, AS(szFileName), g_App.szBrowseFolder, 0) )
  754. {
  755. LPTSTR lpFilePart = NULL;
  756. // Save the last browse directory.
  757. //
  758. if ( GetFullPathName(szFileName, AS(g_App.szBrowseFolder), g_App.szBrowseFolder, &lpFilePart) && g_App.szBrowseFolder[0] && lpFilePart )
  759. *lpFilePart = NULLCHR;
  760. // Try to change the path from a local path
  761. // to a network one.
  762. //
  763. FindUncPath(szFileName, AS(szFileName));
  764. // Set the returned text into our edit box.
  765. //
  766. SetDlgItemText(hwnd, IDC_APP_PATH, szFileName);
  767. }
  768. break;
  769. }
  770. }
  771. return FALSE;
  772. default:
  773. return FALSE;
  774. }
  775. return TRUE ;
  776. }
  777. static BOOL OnInit(HWND hwnd, HWND hwndFocus, LPARAM lParam)
  778. {
  779. HWND hwndLV = GetDlgItem(hwnd, IDC_APPLIST);
  780. HIMAGELIST hImages;
  781. TCHAR szBuffer[256];
  782. LPTSTR lpIniFile = GET_FLAG(OPK_BATCHMODE) ? g_App.szOpkWizIniFile : g_App.szWinBomIniFile;
  783. LVCOLUMN col;
  784. RECT rc;
  785. UINT uCount = 0,
  786. uIndex;
  787. //
  788. // First do some of the basic init stuff.
  789. //
  790. // Write out the version information for the OPKWIZ INI file so Windows thinks it is an INF
  791. //
  792. WritePrivateProfileString(INI_SEC_VERSION, INI_KEY_SIG, INI_VAL_SIG, g_App.szOpkWizIniFile);
  793. WritePrivateProfileString(INI_SEC_VERSION, INI_KEY_SIG, INI_VAL_SIG, g_App.szWinBomIniFile);
  794. // Load the right click menu.
  795. //
  796. g_hMenu = LoadMenu(g_App.hInstance, MAKEINTRESOURCE(IDR_LVRCLICK));
  797. //
  798. // Load the user credential stuff.
  799. //
  800. // Make sure the option is enabled.
  801. //
  802. if ( GetPrivateProfileInt(INI_SEC_GENERAL, INI_KEY_APPCREDENTIALS, 0, g_App.szOpkWizIniFile) )
  803. {
  804. // Check the button.
  805. //
  806. CheckDlgButton(hwnd, IDC_APP_CREDENTIALS, BST_CHECKED);
  807. // NTRAID#NTBUG9-531482-2002/02/27-stelo,swamip - Password stored in plain text
  808. //
  809. // Get the user name first.
  810. //
  811. szBuffer[0] = NULLCHR;
  812. GetPrivateProfileString(WBOM_FACTORY_SECTION, INI_VAL_WBOM_USERNAME, NULLSTR, szBuffer, AS(szBuffer), lpIniFile);
  813. SetDlgItemText(hwnd, IDC_APP_USERNAME, szBuffer);
  814. // Then the password and confirmation password.
  815. //
  816. szBuffer[0] = NULLCHR;
  817. GetPrivateProfileString(WBOM_FACTORY_SECTION, INI_VAL_WBOM_PASSWORD, NULLSTR, szBuffer, AS(szBuffer), lpIniFile);
  818. SetDlgItemText(hwnd, IDC_APP_PASSWORD, szBuffer);
  819. SetDlgItemText(hwnd, IDC_APP_CONFIRM, szBuffer);
  820. }
  821. //
  822. // Init the List view control (columns and titles).
  823. //
  824. // At this point, we have no apps installed so disable edit
  825. // and delete buttons. Add should always be available.
  826. //
  827. EnableWindow(GetDlgItem(hwnd, IDC_APPINST_EDIT), FALSE);
  828. EnableWindow(GetDlgItem(hwnd, IDC_APPINST_DELETE), FALSE);
  829. // Enable/Disable the arrow buttons based on the position of the app in the list
  830. //
  831. EnableWindow(GetDlgItem(hwnd, IDC_APP_UP), FALSE);
  832. EnableWindow(GetDlgItem(hwnd, IDC_APP_DOWN), FALSE);
  833. // Get an image list for the list view control. We will start with a default one.
  834. //
  835. if ( (hImages = ImageList_Create(16, 16, ILC_MASK, 2, 0)) )
  836. {
  837. ImageList_AddIcon(hImages, LoadImage(g_App.hInstance, MAKEINTRESOURCE(IDI_USERAPP), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR));
  838. ListView_SetImageList(hwndLV, hImages, LVSIL_SMALL);
  839. }
  840. // Set extended LV style for whole line selection.
  841. //
  842. SendMessage(hwndLV, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
  843. // Setup the unchanging header values.
  844. //
  845. GetClientRect(hwndLV, &rc);
  846. ZeroMemory(&col, sizeof(LVCOLUMN));
  847. col.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH;
  848. col.pszText = szBuffer;
  849. // Setup all the column headers.
  850. //
  851. for ( uIndex = 0; uIndex < NUM_COLUMNS; uIndex++ )
  852. {
  853. // Load the header string for this column.
  854. //
  855. LoadString(NULL, g_cuHeader[uIndex], szBuffer, STRSIZE(szBuffer));
  856. // Determine the width of the header. The last width should be marked
  857. // with zero so we can use up the rest of the space.
  858. //
  859. if ( ( g_cuWidth[uIndex] == 0 ) &&
  860. ( (uIndex + 1) == NUM_COLUMNS) )
  861. {
  862. // This is the last item and it's width is zero, so we use up the
  863. // rest of the space in the list view control.
  864. //
  865. col.cx = (INT) (rc.right - uCount - GetSystemMetrics(SM_CYHSCROLL));
  866. }
  867. else
  868. {
  869. // The default is to make the column width what is in g_cuWidth[x].
  870. //
  871. col.cx = (INT) (rc.right * g_cuWidth[uIndex] * .01);
  872. uCount += col.cx;
  873. }
  874. // Set rest of the column settings.
  875. //
  876. col.fmt = g_cuFormat[uIndex];
  877. col.iSubItem = uIndex;
  878. // Insert the column.
  879. //
  880. ListView_InsertColumn(hwndLV, uIndex, &col);
  881. }
  882. //
  883. // Read app info from winbom.ini and fill the list view.
  884. //
  885. g_lpAppHead = OpenAppList(lpIniFile);
  886. //
  887. // Now finally fill the list view with all the apps it our global data structure.
  888. //
  889. // Loop through the app entries we have now.
  890. //
  891. RefreshAppList(hwndLV, g_lpAppHead);
  892. // Set the images on the buttons
  893. //
  894. if(g_hArrowUp = LoadImage(g_App.hInstance, MAKEINTRESOURCE(IDI_ARROWUP), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR))
  895. SendMessage(GetDlgItem(hwnd, IDC_APP_UP),BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_hArrowUp);
  896. if(g_hArrowDn = LoadImage(g_App.hInstance, MAKEINTRESOURCE(IDI_ARROWDN), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR))
  897. SendMessage(GetDlgItem(hwnd, IDC_APP_DOWN),BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_hArrowDn);
  898. // Make sure the controls are enabled/disable correctly.
  899. //
  900. EnableControls(hwnd, IDC_APP_CREDENTIALS);
  901. // Always return false to WM_INITDIALOG.
  902. //
  903. return FALSE;
  904. }
  905. static void OnCommand(HWND hwnd, INT id, HWND hwndCtl, UINT codeNotify)
  906. {
  907. APPENTRY app;
  908. HWND hwndLV = GetDlgItem(hwnd, IDC_APPLIST);
  909. LVITEM lvItem;
  910. switch ( id )
  911. {
  912. case ID_ADD:
  913. case IDC_APPINST_ADD:
  914. //
  915. // Two ways to get here - add button, or right-click add.
  916. //
  917. // Zero out our app structure and set the default values.
  918. //
  919. ZeroMemory(&app, sizeof(APPENTRY));
  920. app.itSectionType = installtechUndefined;
  921. // Exec dialog to get info, pass it our app struct. The dialog
  922. // will fill it in and make sure it is valid.
  923. //
  924. if ( DialogBoxParam(g_App.hInstance, MAKEINTRESOURCE(IDD_APP), hwnd, (DLGPROC) OneAppDlgProc, (LPARAM) &app) == IDOK )
  925. {
  926. LPAPPENTRY lpApp;
  927. // They clicked OK, so add a new entry to our link list (the
  928. // function allocs a new struct and copies from the one we
  929. // pass in so it is safe to blow it away). Make sure it was
  930. // added and inserted into the list. If the add fails, we
  931. // are out of memory.
  932. //
  933. if ( lpApp = ManageAppList(&g_lpAppHead, &app, APP_ADD) )
  934. AddAppToListView(GetDlgItem(hwnd, IDC_APPLIST), lpApp);
  935. else
  936. {
  937. MsgBox(GetParent(hwnd), IDS_OUTOFMEM, IDS_APPNAME, MB_ERRORBOX);
  938. WIZ_EXIT(hwnd);
  939. }
  940. RefreshAppList(hwndLV, g_lpAppHead);
  941. }
  942. break;
  943. case ID_EDIT:
  944. case IDC_APPINST_EDIT:
  945. //
  946. // Two ways to get here - edit button, or right-click edit.
  947. //
  948. // Get the selected item.
  949. //
  950. ZeroMemory(&lvItem, sizeof(LVITEM));
  951. if ( (lvItem.iItem = ListView_GetNextItem(hwndLV, -1, LVNI_SELECTED)) >= 0 )
  952. {
  953. // Retrieve lParam for selected item which is the
  954. // corresponding APPENTRY item.
  955. //
  956. lvItem.mask = LVIF_PARAM;
  957. ListView_GetItem(hwndLV, &lvItem);
  958. // Pop up dialog inited with the app entry.
  959. //
  960. if ( lvItem.lParam && ( DialogBoxParam(g_App.hInstance, MAKEINTRESOURCE(IDD_APP), hwnd, (DLGPROC) OneAppDlgProc, lvItem.lParam) == IDOK ) )
  961. {
  962. // This is to fix the problem where we would always put the item we
  963. // just edited at the bottom of the list. I'm going to leave all the
  964. // old code in here, because it seems to me that we would have done
  965. // all this crap for some reason. But for now it seems to work fine
  966. // this way.
  967. //
  968. RefreshAppList(hwndLV, g_lpAppHead);
  969. ListView_SetItemState(hwndLV, lvItem.iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
  970. SetFocus(hwndLV);
  971. /* Old code... just be careful if you want to try to use it again becase
  972. we don't support the LPSTR_TEXTCALLBACK anymore.
  973. // Hmm, the following wierdness is because the listview
  974. // seems to remember the size of the first thing we put
  975. // in it and will truncate if we edit and make the thing
  976. // larger. Resetting the pszText field seems to have it
  977. // recalc this buffer.
  978. //
  979. CopyMemory(&app, (LPAPPENTRY) lvItem.lParam, sizeof(APPENTRY));
  980. ManageAppList(&g_lpAppHead, ((LPAPPENTRY) lvItem.lParam), APP_DELETE);
  981. ManageAppList(&g_lpAppHead, &app, APP_ADD);
  982. lvItem.mask = LVIF_TEXT;
  983. lvItem.pszText = LPSTR_TEXTCALLBACK;
  984. ListView_SetItem(hwndLV, &lvItem);
  985. // The app entry is updated by the Dialog, so just tell
  986. // list view control to redraw. The item gets deslected
  987. // at this point so I manually select it and give it focus
  988. //
  989. ListView_SetItemState(hwndLV, lvItem.iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
  990. ListView_RedrawItems(hwndLV, lvItem.iItem, lvItem.iItem);
  991. SetFocus(hwndLV);
  992. RefreshAppList(hwndLV, g_lpAppHead);
  993. */
  994. }
  995. }
  996. break;
  997. case ID_DELETE:
  998. case IDC_APPINST_DELETE:
  999. //
  1000. // Two ways to get here - delete button, or right-click delete.
  1001. //
  1002. // Get the selected item.
  1003. //
  1004. ZeroMemory(&lvItem, sizeof(LVITEM));
  1005. if ( (lvItem.iItem = ListView_GetNextItem(hwndLV, -1, LVNI_SELECTED)) >= 0 )
  1006. {
  1007. // Retrieve lParam for selected item which is the
  1008. // corresponding APPENTRY item.
  1009. //
  1010. lvItem.mask = LVIF_PARAM;
  1011. ListView_GetItem(hwndLV, &lvItem);
  1012. // Make sure we clean out the sections that could be in the ini files
  1013. // before we nuke the entry.
  1014. //
  1015. CleanupSections(((LPAPPENTRY) lvItem.lParam)->szSectionName, TRUE);
  1016. CleanupSections(((LPAPPENTRY) lvItem.lParam)->szSectionName, FALSE);
  1017. // Delete from link list and the list view.
  1018. //
  1019. ManageAppList(&g_lpAppHead, (LPAPPENTRY) lvItem.lParam, APP_DELETE);
  1020. ListView_DeleteItem(hwndLV, lvItem.iItem);
  1021. }
  1022. break;
  1023. case IDC_APP_UP:
  1024. case IDC_APP_DOWN:
  1025. {
  1026. LPAPPENTRY lpApp;
  1027. // Get the selected item.
  1028. //
  1029. ZeroMemory(&lvItem, sizeof(LVITEM));
  1030. if ( (lvItem.iItem = ListView_GetNextItem(hwndLV, -1, LVNI_SELECTED)) >= 0 )
  1031. {
  1032. // Retrieve lParam for selected item which is the
  1033. // corresponding APPENTRY item.
  1034. //
  1035. lvItem.mask = LVIF_PARAM;
  1036. if (id==IDC_APP_UP)
  1037. lvItem.iItem--;
  1038. ListView_GetItem(hwndLV, &lvItem);
  1039. }
  1040. if(lpApp = (LPAPPENTRY) lvItem.lParam)
  1041. {
  1042. ManageAppList(&g_lpAppHead, lpApp, APP_MOVE_DOWN);
  1043. RefreshAppList(hwndLV, g_lpAppHead);
  1044. ListView_SetItemState(hwndLV, lvItem.iItem + ((id==IDC_APP_UP) ? 0 : 1), LVIS_SELECTED | LVIS_FOCUSED,
  1045. LVIS_SELECTED | LVIS_FOCUSED);
  1046. // Make sure the listview regains focus otherwise the alt+n won't
  1047. // work to navigate to next page
  1048. //
  1049. SetFocus(hwndLV);
  1050. }
  1051. }
  1052. break;
  1053. case IDC_APP_CREDENTIALS:
  1054. EnableControls(hwnd, id);
  1055. break;
  1056. }
  1057. }
  1058. static LRESULT OnListViewNotify(HWND hwnd, UINT uMsg, WPARAM wParam, NMLVDISPINFO * lpnmlvdi)
  1059. {
  1060. static TCHAR szYes[32] = NULLSTR,
  1061. szNo[32] = NULLSTR;
  1062. HWND hwndLV = GetDlgItem(hwnd, IDC_APPLIST);
  1063. LPAPPENTRY lpApp;
  1064. POINT ptScreen,
  1065. ptClient;
  1066. HMENU hPopupMenu;
  1067. LVHITTESTINFO lvHitInfo;
  1068. LVITEM lvItem;
  1069. // Load the Yes/No strings into the statics.
  1070. //
  1071. LoadString(NULL, IDS_YES, szYes, STRSIZE(szYes));
  1072. LoadString(NULL, IDS_NO, szNo, STRSIZE(szNo));
  1073. // See what the notification message that was sent to the list view.
  1074. //
  1075. switch ( lpnmlvdi->hdr.code )
  1076. {
  1077. case NM_RCLICK:
  1078. // Get cursor position, translate to client coordinates and
  1079. // do a listview hit test.
  1080. //
  1081. GetCursorPos(&ptScreen);
  1082. ptClient.x = ptScreen.x;
  1083. ptClient.y = ptScreen.y;
  1084. MapWindowPoints(NULL, hwndLV, &ptClient, 1);
  1085. lvHitInfo.pt.x = ptClient.x;
  1086. lvHitInfo.pt.y = ptClient.y;
  1087. ListView_HitTest(hwndLV, &lvHitInfo);
  1088. hPopupMenu = GetSubMenu(g_hMenu, 0);
  1089. //
  1090. // ISSUE-2002/02/27-stelo,swamip - Make sure the handle to the submenu is valid.
  1091. //
  1092. // Test if item was clicked.
  1093. //
  1094. lpApp = NULL;
  1095. if ( lvHitInfo.flags & LVHT_ONITEM )
  1096. {
  1097. // Activate clicked item and bring up a popup menu.
  1098. //
  1099. ListView_SetItemState(hwndLV, lvHitInfo.iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
  1100. // Retrieve lParam for selected item which is the
  1101. // corresponding APPENTRY item.
  1102. //
  1103. ZeroMemory(&lvItem, sizeof(lvItem));
  1104. lvItem.iItem = lvHitInfo.iItem;
  1105. lvItem.mask = LVIF_PARAM;
  1106. ListView_GetItem(hwndLV, &lvItem);
  1107. if ( lpApp = (LPAPPENTRY) lvItem.lParam )
  1108. {
  1109. // Enable/disable the edit menu item (always enabled).
  1110. //
  1111. EnableMenuItem(hPopupMenu, ID_EDIT, MF_BYCOMMAND | MF_ENABLED);
  1112. // Enable/disable the delete (disabled if not
  1113. // a user app).
  1114. //
  1115. EnableMenuItem(hPopupMenu, ID_DELETE, MF_BYCOMMAND | MF_ENABLED);
  1116. }
  1117. }
  1118. if ( lpApp == NULL )
  1119. {
  1120. // User right clicked in control but not on an item, so we uncheck and gray everything except Add.
  1121. //
  1122. EnableMenuItem(hPopupMenu, ID_EDIT, MF_BYCOMMAND | MF_GRAYED);
  1123. EnableMenuItem(hPopupMenu, ID_DELETE, MF_BYCOMMAND | MF_GRAYED);
  1124. }
  1125. // Show the right-click popup menu.
  1126. //
  1127. TrackPopupMenu(hPopupMenu, 0, ptScreen.x, ptScreen.y, 0, hwnd, NULL);
  1128. break;
  1129. case NM_DBLCLK:
  1130. // Get cursor position, translate to client coordinates and
  1131. // do a listview hittest.
  1132. //
  1133. GetCursorPos(&ptScreen);
  1134. ptClient.x = ptScreen.x;
  1135. ptClient.y = ptScreen.y;
  1136. MapWindowPoints(NULL, hwndLV, &ptClient, 1);
  1137. lvHitInfo.pt.x = ptClient.x;
  1138. lvHitInfo.pt.y = ptClient.y;
  1139. ListView_HitTest(hwndLV, &lvHitInfo);
  1140. // Test if item was clicked.
  1141. //
  1142. if ( lvHitInfo.flags & LVHT_ONITEM )
  1143. {
  1144. // Select the item and send a message to the dialog the user wants
  1145. // edit the selected item.
  1146. //
  1147. ListView_SetItemState(hwndLV, lvHitInfo.iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
  1148. SendMessage(hwnd, WM_COMMAND, ID_EDIT, 0);
  1149. }
  1150. break;
  1151. /* Don't need to do this anymore because we don't need to use LPSTR_TEXTCALLBACK.
  1152. case LVN_GETDISPINFO:
  1153. // Display the appropriate item, getting the text from the
  1154. // APPENTRY structure for this item.
  1155. //
  1156. lpApp = (LPAPPENTRY) lpnmlvdi->item.lParam;
  1157. switch ( lpnmlvdi->item.iSubItem )
  1158. {
  1159. case 0: // Display Name:
  1160. lpnmlvdi->item.pszText = lpApp->szDisplayName;
  1161. break;
  1162. case 1: // Command line:
  1163. lpnmlvdi->item.pszText = lpApp->szSourcePath;
  1164. break;
  1165. }
  1166. break;
  1167. */
  1168. case LVN_COLUMNCLICK:
  1169. //
  1170. // TODO: Maybe it would be nice at this point to sort the
  1171. // list by the selected column
  1172. //
  1173. break;
  1174. case LVN_ITEMCHANGED:
  1175. // We capture all change messages here, although all we care
  1176. // about is whether we are changing from an item selected to
  1177. // no item selected or vice-versa. However, I see no other
  1178. // message that indicates this (like LB_SELCHANGE for Listbox).
  1179. //
  1180. ZeroMemory(&lvItem, sizeof(LVITEM));
  1181. if ( (lvItem.iItem = ListView_GetNextItem(GetDlgItem(hwnd, IDC_APPLIST), -1, LVNI_SELECTED)) >= 0 )
  1182. {
  1183. LPAPPENTRY lpAppPrev = NULL,
  1184. lpAppSearch = NULL;
  1185. // We need the flags for this item so we can find it in the list.
  1186. // So retrieve lParam for selected item which is the corresponding
  1187. // APPENTRY item.
  1188. //
  1189. lvItem.mask = LVIF_PARAM;
  1190. ListView_GetItem(hwndLV, &lvItem);
  1191. lpApp = (LPAPPENTRY) lvItem.lParam;
  1192. // Search to see if there is a previous/next item so we can
  1193. // enable/disable the up/down buttons.
  1194. //
  1195. for ( lpAppSearch = g_lpAppHead; lpAppSearch && (lpAppSearch !=lpApp); lpAppSearch = lpAppSearch->lpNext )
  1196. {
  1197. lpAppPrev = lpAppSearch;
  1198. }
  1199. // Something is selected so enable the edit button and the delete
  1200. // button.
  1201. //
  1202. EnableWindow(GetDlgItem(hwnd, IDC_APPINST_EDIT), TRUE);
  1203. EnableWindow(GetDlgItem(hwnd, IDC_APPINST_DELETE), TRUE);
  1204. // Enable/disable the up/down buttons.
  1205. //
  1206. EnableWindow(GetDlgItem(hwnd, IDC_APP_UP),(lpAppPrev ? TRUE : FALSE) );
  1207. EnableWindow(GetDlgItem(hwnd, IDC_APP_DOWN),(lpApp->lpNext ? TRUE : FALSE) );
  1208. }
  1209. else
  1210. {
  1211. // Nothing is selected do disable these buttons.
  1212. //
  1213. EnableWindow(GetDlgItem(hwnd, IDC_APPINST_EDIT), FALSE);
  1214. EnableWindow(GetDlgItem(hwnd, IDC_APPINST_DELETE), FALSE);
  1215. // Enable/Disable the arrow buttons based on the position of the app in the list
  1216. //
  1217. EnableWindow(GetDlgItem(hwnd, IDC_APP_UP),FALSE);
  1218. EnableWindow(GetDlgItem(hwnd, IDC_APP_DOWN),FALSE);
  1219. }
  1220. break;
  1221. }
  1222. return 0L;
  1223. }
  1224. static BOOL SaveData(HWND hwnd)
  1225. {
  1226. TCHAR szUsername[256] = NULLSTR,
  1227. szPassword[256] = _T("\"");
  1228. BOOL bUser = ( IsDlgButtonChecked(hwnd, IDC_APP_CREDENTIALS) == BST_CHECKED );
  1229. HRESULT hrCat;
  1230. // See if we need to write out credetials. Start with the passwords
  1231. // so we can make sure they are the same.
  1232. //
  1233. if ( bUser )
  1234. {
  1235. // First get the password and confirmation of the password and
  1236. // make sure they match.
  1237. //
  1238. GetDlgItemText(hwnd, IDC_APP_PASSWORD, szPassword + 1, AS(szPassword) - 1);
  1239. GetDlgItemText(hwnd, IDC_APP_CONFIRM, szUsername, AS(szUsername));
  1240. if ( lstrcmp(szPassword + 1, szUsername) != 0 )
  1241. {
  1242. // Didn't match, so error out.
  1243. //
  1244. MsgBox(hwnd, IDS_ERR_CONFIRMPASSWORD, IDS_APPNAME, MB_ERRORBOX);
  1245. SetDlgItemText(hwnd, IDC_APP_PASSWORD, NULLSTR);
  1246. SetDlgItemText(hwnd, IDC_APP_CONFIRM, NULLSTR);
  1247. SetFocus(GetDlgItem(hwnd, IDC_APP_PASSWORD));
  1248. return FALSE;
  1249. }
  1250. // If there is a password, add the trailing quote.
  1251. //
  1252. if ( szPassword[1] )
  1253. hrCat=StringCchCat(szPassword, AS(szPassword), _T("\""));
  1254. else
  1255. szPassword[0] = NULLCHR;
  1256. // Now get the user name.
  1257. //
  1258. szUsername[0] = NULLCHR;
  1259. GetDlgItemText(hwnd, IDC_APP_USERNAME, szUsername, AS(szUsername));
  1260. }
  1261. // Now write out the settings, or delete if the option is not set.
  1262. //
  1263. // NTRAID#NTBUG9-531482-2002/02/27-stelo,swamip - Password stored in plain text
  1264. //
  1265. WritePrivateProfileString(WBOM_FACTORY_SECTION, INI_VAL_WBOM_USERNAME, ( bUser ? szUsername : NULL ), g_App.szOpkWizIniFile);
  1266. WritePrivateProfileString(WBOM_FACTORY_SECTION, INI_VAL_WBOM_USERNAME, ( bUser ? szUsername : NULL ), g_App.szWinBomIniFile);
  1267. WritePrivateProfileString(WBOM_FACTORY_SECTION, INI_VAL_WBOM_PASSWORD, ( bUser ? szPassword : NULL ), g_App.szOpkWizIniFile);
  1268. WritePrivateProfileString(WBOM_FACTORY_SECTION, INI_VAL_WBOM_PASSWORD, ( bUser ? szPassword : NULL ), g_App.szWinBomIniFile);
  1269. WritePrivateProfileString(INI_SEC_GENERAL, INI_KEY_APPCREDENTIALS, ( bUser ? STR_ONE : NULL ), g_App.szOpkWizIniFile);
  1270. if ( !SaveAppList(g_lpAppHead, g_App.szWinBomIniFile, g_App.szOpkWizIniFile) )
  1271. {
  1272. MsgBox(GetParent(hwnd), IDS_OUTOFMEM, IDS_APPNAME, MB_ERRORBOX);
  1273. WIZ_EXIT(hwnd);
  1274. return FALSE;
  1275. }
  1276. return TRUE;
  1277. }
  1278. static BOOL SaveOneApp(HWND hwnd, LPAPPENTRY lpApp)
  1279. {
  1280. APPENTRY app;
  1281. LPTSTR lpFilePart;
  1282. DWORD dwIndex;
  1283. BOOL bFound;
  1284. //
  1285. // First do some checks to make sure all the data that
  1286. // they entered is valid. Once that is done, we can go
  1287. // ahead and save the data.
  1288. //
  1289. // Make sure we have a pointer to a valid structure.
  1290. //
  1291. if ( lpApp == NULL )
  1292. return FALSE;
  1293. // Copy the current app structure into the temporary one.
  1294. //
  1295. CopyMemory(&app, lpApp, sizeof(APPENTRY));
  1296. // Make sure they have a display name.
  1297. //
  1298. app.szDisplayName[0] = NULLCHR;
  1299. GetDlgItemText(hwnd, IDC_APP_NAME, app.szDisplayName, AS(app.szDisplayName));
  1300. if ( app.szDisplayName[0] == NULLCHR )
  1301. {
  1302. MsgBox(hwnd, IDS_BLANKNAME, IDS_APPNAME, MB_ERRORBOX);
  1303. SetFocus(GetDlgItem(hwnd, IDC_APP_NAME));
  1304. return FALSE;
  1305. }
  1306. #if 0
  1307. //
  1308. // We should have this safety check for resreved names, but I don't
  1309. // want to mess around with adding error strings at this point. We should
  1310. // add this code in after we ship.
  1311. //
  1312. // IDS_RESERVEDNAME "The name ""%s"" is a reserved name and cannot be used. Please choose another name for your application install."
  1313. //
  1314. // Make sure the display name isn't a reserved one.
  1315. //
  1316. if ( AppInternal(app.szDisplayName) )
  1317. {
  1318. MsgBox(hwnd, IDS_RESERVEDNAME, IDS_APPNAME, MB_OK | MB_ICONINFORMATION, app.szDisplayName);
  1319. SetFocus(GetDlgItem(hwnd, IDC_APP_NAME));
  1320. return FALSE;
  1321. }
  1322. #endif
  1323. // The app display name must be unique, so search through
  1324. // our applist to see if this is a dup.
  1325. //
  1326. // Now that we use the WINBOM instead of the registry, this is no longer
  1327. // true. The display names do not need to be unique.
  1328. /*
  1329. LPAPPENTRY lpAppSearch;
  1330. for ( lpAppSearch = g_lpAppHead; lpAppSearch; lpAppSearch = lpAppSearch->lpNext )
  1331. {
  1332. if ( ( lpAppSearch != lpApp ) &&
  1333. ( lstrcmpi(lpAppSearch->szDisplayName, app.szDisplayName) == 0 ) )
  1334. {
  1335. MsgBox(hwnd, IDS_DUPNAME, IDS_APPNAME, MB_ERRORBOX);
  1336. SetFocus(GetDlgItem(hwnd, IDC_APP_NAME));
  1337. return FALSE;
  1338. }
  1339. }
  1340. */
  1341. // Get the source path and setup file.
  1342. //
  1343. app.szSourcePath[0] = NULLCHR;
  1344. GetDlgItemText(hwnd, IDC_APP_PATH, app.szSourcePath, AS(app.szSourcePath));
  1345. // Now need to split the file name from the source path.
  1346. //
  1347. if ( app.szSourcePath[0] &&
  1348. GetFullPathName(app.szSourcePath, AS(app.szSetupFile), app.szSetupFile, &lpFilePart) &&
  1349. app.szSetupFile[0] &&
  1350. lpFilePart )
  1351. {
  1352. DWORD dwPathLen = lstrlen(app.szSourcePath) - lstrlen(lpFilePart);
  1353. lstrcpyn(app.szSetupFile, app.szSourcePath + dwPathLen, AS(app.szSetupFile));
  1354. app.szSourcePath[dwPathLen] = NULLCHR;
  1355. }
  1356. // Make sure they have a setup file.
  1357. //
  1358. if ( app.szSetupFile[0] == NULLCHR )
  1359. {
  1360. MsgBox(hwnd, IDS_BLANKPATH, IDS_APPNAME, MB_ERRORBOX);
  1361. SetFocus(GetDlgItem(hwnd, IDC_APP_PATH));
  1362. return FALSE;
  1363. }
  1364. // Get any command line arguments they have.
  1365. //
  1366. app.szCommandLine[0] = NULLCHR;
  1367. GetDlgItemText(hwnd, IDC_APP_ARGS, app.szCommandLine, AS(app.szCommandLine));
  1368. // Find out which radio button is checked.
  1369. //
  1370. bFound = FALSE;
  1371. app.itSectionType = installtechUndefined;
  1372. for ( dwIndex = 0; ( dwIndex < AS(g_crbChecked) ) && !bFound ; dwIndex++ )
  1373. {
  1374. if ( bFound = ( IsDlgButtonChecked(hwnd, g_crbChecked[dwIndex].iButtonId) == BST_CHECKED ) )
  1375. app.itSectionType = g_crbChecked[dwIndex].itSectionType;
  1376. }
  1377. // Set the bits you can set in this dialog.
  1378. //
  1379. SETBIT(app.dwFlags, APP_FLAG_REBOOT, IsDlgButtonChecked(hwnd, IDC_APP_REBOOT));
  1380. SETBIT(app.dwFlags, APP_FLAG_STAGE, IsDlgButtonChecked(hwnd, IDC_APP_STAGE));
  1381. // If this is a generic app, and we are not rebooting or
  1382. // staging, then no need to do an advanced section.
  1383. //
  1384. if ( ( installtechApp == app.itSectionType ) &&
  1385. ( !GETBIT(app.dwFlags, APP_FLAG_REBOOT) ) &&
  1386. ( !GETBIT(app.dwFlags, APP_FLAG_STAGE) ) )
  1387. {
  1388. // This means we are just going to do the one line thing,
  1389. // no advanced parameters.
  1390. //
  1391. app.itSectionType = installtechUndefined;
  1392. }
  1393. // There is some special stuff to get if this is an INF install.
  1394. //
  1395. if ( installtechINF == app.itSectionType )
  1396. {
  1397. // Get the section name and make sure we have it.
  1398. //
  1399. app.szInfSectionName[0] = NULLCHR;
  1400. GetDlgItemText(hwnd, IDC_APP_INF_SECTION, app.szInfSectionName, AS(app.szInfSectionName));
  1401. if ( NULLCHR == app.szInfSectionName[0] )
  1402. {
  1403. MsgBox(hwnd, IDS_ERR_NOSECTION, IDS_APPNAME, MB_ERRORBOX);
  1404. SetFocus(GetDlgItem(hwnd, IDC_APP_INF_SECTION));
  1405. return FALSE;
  1406. }
  1407. }
  1408. // There is also some special stuff to get if this is a staged install.
  1409. //
  1410. if ( GETBIT(app.dwFlags, APP_FLAG_STAGE) )
  1411. {
  1412. // Get the staged folder and make sure we have it.
  1413. //
  1414. app.szStagePath[0] = NULLCHR;
  1415. GetDlgItemText(hwnd, IDC_APP_STAGEPATH, app.szStagePath, AS(app.szStagePath));
  1416. if ( NULLCHR == app.szStagePath[0] )
  1417. {
  1418. MsgBox(hwnd, IDS_ERR_NOSTAGEPATH, IDS_APPNAME, MB_ERRORBOX);
  1419. SetFocus(GetDlgItem(hwnd, IDC_APP_STAGEPATH));
  1420. return FALSE;
  1421. }
  1422. }
  1423. //
  1424. // Now that we are sure that we have valid data, we can return the data
  1425. // we collected into the supplied buffer.
  1426. //
  1427. // Start by moving over the data from our temporary buffer.
  1428. //
  1429. CopyMemory(lpApp, &app, sizeof(APPENTRY));
  1430. // If we made it this far, we must return TRUE.
  1431. //
  1432. return TRUE;
  1433. }
  1434. static LPAPPENTRY ManageAppList(LPLPAPPENTRY lpAppHead, LPAPPENTRY lpAppAdd, DWORD dwFlag)
  1435. {
  1436. LPAPPENTRY lpAppNew = NULL,
  1437. *lpAppSearch;
  1438. // Make sure the head pointer is valid.
  1439. //
  1440. if ( lpAppHead == NULL )
  1441. return NULL;
  1442. // See if we are freeing the list.
  1443. //
  1444. if ( lpAppAdd == NULL )
  1445. {
  1446. // Don't keep going when we hit the last NULL next pointer.
  1447. //
  1448. if ( *lpAppHead != NULL )
  1449. {
  1450. ManageAppList(&((*lpAppHead)->lpNext), NULL, 0);
  1451. FREE(*lpAppHead);
  1452. }
  1453. }
  1454. // OK, how about removing an item.
  1455. //
  1456. else if ( dwFlag == APP_DELETE )
  1457. {
  1458. // Search for the item we want to delete.
  1459. //
  1460. for ( lpAppSearch = lpAppHead; *lpAppSearch && ( *lpAppSearch != lpAppAdd ); lpAppSearch = &((*lpAppSearch)->lpNext) );
  1461. // Make sure we found the item we were looking for.
  1462. //
  1463. if ( *lpAppSearch )
  1464. {
  1465. // Setup the list to skip over the item we are going to delete.
  1466. //
  1467. *lpAppSearch = (*lpAppSearch)->lpNext;
  1468. // Then NULL the next pointer of the item we are removing.
  1469. //
  1470. lpAppAdd->lpNext = NULL;
  1471. // Call this function again with the pointer to the item to delete
  1472. // as the head parameter to free it up.
  1473. //
  1474. ManageAppList(&lpAppAdd, NULL, 0);
  1475. }
  1476. }
  1477. // Must be adding a new one. Allocate a new structure for the
  1478. // item we are adding.
  1479. //
  1480. else if ( (dwFlag == APP_ADD) && (lpAppNew = (LPAPPENTRY) MALLOC(sizeof(APPENTRY))) )
  1481. {
  1482. // Copy contents of the passed in structure to the newly
  1483. // allocated one.
  1484. //
  1485. CopyMemory(lpAppNew, lpAppAdd, sizeof(APPENTRY));
  1486. // Reset the new next pointer to NULL.
  1487. //
  1488. lpAppNew->lpNext = NULL;
  1489. lpAppSearch = lpAppHead;
  1490. while (*lpAppSearch)
  1491. lpAppSearch = &((*lpAppSearch)->lpNext);
  1492. // Insert the new APPENTRY into the correct position
  1493. //
  1494. lpAppNew->lpNext = (*lpAppSearch);
  1495. *lpAppSearch = lpAppNew;
  1496. }
  1497. else if ( dwFlag == APP_MOVE_DOWN )
  1498. {
  1499. LPAPPENTRY lpAppPrev = NULL;
  1500. for ( lpAppNew = (*lpAppHead); lpAppNew && ((lpAppNew) != lpAppAdd); lpAppNew = lpAppNew->lpNext )
  1501. {
  1502. lpAppPrev = lpAppNew;
  1503. }
  1504. if ( lpAppNew )
  1505. {
  1506. if ( lpAppPrev )
  1507. lpAppPrev->lpNext = lpAppNew->lpNext;
  1508. else
  1509. g_lpAppHead = lpAppNew->lpNext;
  1510. lpAppNew->lpNext = lpAppNew->lpNext->lpNext;
  1511. }
  1512. if (lpAppPrev )
  1513. lpAppPrev->lpNext->lpNext = lpAppNew;
  1514. else
  1515. g_lpAppHead->lpNext = lpAppNew;
  1516. }
  1517. return lpAppNew;
  1518. }
  1519. static void AddAppToListView(HWND hwndLV, LPAPPENTRY lpApp)
  1520. {
  1521. LVITEM lvItem;
  1522. HIMAGELIST hImages;
  1523. TCHAR szFullCmdLine[(MAX_PATH * 2) + MAX_CMDLINE + 1];
  1524. HRESULT hrCat;
  1525. // Don't show internal apps.
  1526. //
  1527. if ( GETBIT(lpApp->dwFlags, APP_FLAG_INTERNAL) )
  1528. {
  1529. return;
  1530. }
  1531. // Init the list view item structure with some of the things common
  1532. // to all list view entries.
  1533. //
  1534. ZeroMemory(&lvItem, sizeof(LVITEM));
  1535. lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
  1536. lvItem.state = 0;
  1537. lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
  1538. lvItem.iSubItem = 0;
  1539. lvItem.lParam = (LPARAM) lpApp;
  1540. // Get the index for this item (number currently in list since
  1541. // we are zero-based).
  1542. //
  1543. lvItem.iItem = ListView_GetItemCount(hwndLV);
  1544. // I didn't see the need to do use the LPSTR_TEXTCALLBACK way of displaying
  1545. // the text. It seems to work fine this new way. Although I left the old
  1546. // code in here that handled the LVN_GETDISPINFO in case we need to go back
  1547. // to this way.
  1548. //
  1549. lvItem.pszText = lpApp->szDisplayName;
  1550. // Create the full path part of the command line out of the data we have.
  1551. //
  1552. lstrcpyn(szFullCmdLine, lpApp->szSourcePath, AS(szFullCmdLine));
  1553. if ( szFullCmdLine[0] )
  1554. AddPathN(szFullCmdLine, lpApp->szSetupFile,AS(szFullCmdLine));
  1555. else
  1556. lstrcpyn(szFullCmdLine, lpApp->szSetupFile, AS(szFullCmdLine));
  1557. // Add the icon for tha app the the list view's image list.
  1558. //
  1559. if ( ( *(lpApp->szSourcePath) ) &&
  1560. ( *(lpApp->szSetupFile) ) &&
  1561. ( hImages = ListView_GetImageList(hwndLV, LVSIL_SMALL) ) )
  1562. {
  1563. SHFILEINFO shfiIcon;
  1564. // Now get the icon from the install file.
  1565. //
  1566. ZeroMemory(&shfiIcon, sizeof(SHFILEINFO));
  1567. if ( SHGetFileInfo(szFullCmdLine, 0, &shfiIcon, sizeof(SHFILEINFO), SHGFI_ICON | SHGFI_SMALLICON) && shfiIcon.hIcon )
  1568. {
  1569. // Try to add the icon to our list... if it fails, use the default icon
  1570. // for this item.
  1571. //
  1572. lvItem.iImage = ImageList_AddIcon(hImages, shfiIcon.hIcon);
  1573. if ( lvItem.iImage < 0 )
  1574. lvItem.iImage = 0;
  1575. }
  1576. }
  1577. // Add on our command line for the display of the sub-item.
  1578. hrCat=StringCchCat(szFullCmdLine, AS(szFullCmdLine), _T(" "));
  1579. hrCat=StringCchCat(szFullCmdLine, AS(szFullCmdLine), lpApp->szCommandLine);
  1580. // Insert the main guy.
  1581. //
  1582. ListView_InsertItem(hwndLV, &lvItem);
  1583. // Insert each of the other columns.
  1584. //
  1585. // Only one other column for now, so just do it this way.
  1586. //
  1587. ListView_SetItemText(hwndLV, lvItem.iItem, 1, szFullCmdLine);
  1588. /* This is the old code in case we want to do more than one later.
  1589. for (lvItem.iSubItem = 1; lvItem.iSubItem < NUM_COLUMNS; lvItem.iSubItem++)
  1590. {
  1591. switch ( lvItem.iSubItem )
  1592. {
  1593. case 1:
  1594. ListView_SetItemText(hwndLV, lvItem.iItem, lvItem.iSubItem, szFullCmdLine);
  1595. break;
  1596. }
  1597. }
  1598. */
  1599. }
  1600. static BOOL RefreshAppList(HWND hwnd, LPAPPENTRY lpAppHead)
  1601. {
  1602. LPAPPENTRY lpApp;
  1603. ListView_DeleteAllItems(hwnd);
  1604. for ( lpApp = lpAppHead; lpApp; lpApp = lpApp->lpNext )
  1605. AddAppToListView(hwnd, lpApp);
  1606. return TRUE;
  1607. }
  1608. static BOOL AdvancedView(HWND hwnd, BOOL bChange)
  1609. {
  1610. static int iMaxHeight = 0;
  1611. RECT rc;
  1612. LPTSTR lpText;
  1613. BOOL bAdvanced;
  1614. GetWindowRect(hwnd, &rc);
  1615. // If this is the first time called, iMaxHeight will be
  1616. // zero and we will be in advanced view.
  1617. //
  1618. if ( 0 == iMaxHeight )
  1619. {
  1620. bAdvanced = TRUE;
  1621. iMaxHeight = rc.bottom - rc.top;
  1622. }
  1623. else
  1624. bAdvanced = (rc.bottom - rc.top == iMaxHeight);
  1625. // Only toggle if they want us to change the view.
  1626. //
  1627. if ( bChange )
  1628. {
  1629. if ( bAdvanced = !bAdvanced )
  1630. {
  1631. // Going into advanced view.
  1632. //
  1633. SetWindowPos(hwnd, NULL, 0, 0, rc.right - rc.left, iMaxHeight, SWP_NOMOVE | SWP_NOZORDER);
  1634. if ( lpText = AllocateString(NULL, IDS_APP_STANDARD) )
  1635. {
  1636. SetWindowText(GetDlgItem(hwnd, IDC_APP_ADVANCED), lpText);
  1637. FREE(lpText);
  1638. }
  1639. }
  1640. else
  1641. {
  1642. int iWidth = rc.right - rc.left,
  1643. iHeight = rc.top;
  1644. // Going into standard view.
  1645. //
  1646. GetWindowRect(GetDlgItem(hwnd, IDC_APP_DIVIDER), &rc);
  1647. SetWindowPos(hwnd, NULL, 0, 0, iWidth, rc.bottom - iHeight, SWP_NOMOVE | SWP_NOZORDER);
  1648. if ( lpText = AllocateString(NULL, IDS_APP_ADVANCED) )
  1649. {
  1650. SetWindowText(GetDlgItem(hwnd, IDC_APP_ADVANCED), lpText);
  1651. FREE(lpText);
  1652. }
  1653. }
  1654. }
  1655. return bAdvanced;
  1656. }
  1657. static void CleanupSections(LPTSTR lpSection, BOOL bStage)
  1658. {
  1659. TCHAR szSection[MAX_SECTIONNAME];
  1660. LPTSTR lpEnd;
  1661. HRESULT hrPrintf;
  1662. // Make our own copy of the section name to play with.
  1663. // Also need to get a pointer to the end of it.
  1664. //
  1665. lstrcpyn(szSection, lpSection, AS(szSection));
  1666. lpEnd = szSection + lstrlen(szSection);
  1667. // Now clean up the sections we don't want.
  1668. //
  1669. if ( bStage )
  1670. {
  1671. //
  1672. // We do this because this might have been a
  1673. // standard install before and we don't want to
  1674. // leave the section laying around.
  1675. //
  1676. // Just nuke the section name from both files.
  1677. //
  1678. WritePrivateProfileString(szSection, NULL, NULL, g_App.szWinBomIniFile);
  1679. WritePrivateProfileString(szSection, NULL, NULL, g_App.szWinBomIniFile);
  1680. }
  1681. else
  1682. {
  1683. //
  1684. // This could have been a staged install before, so
  1685. // have to remove the three possible sections that
  1686. // we could have created.
  1687. //
  1688. // Nuke the attach section from both files.
  1689. //
  1690. hrPrintf=StringCchPrintf(lpEnd, (MAX_SECTIONNAME-lstrlen(szSection)), STR_INI_SEC_ADVAPP_STAGE, INI_VAL_WBOM_ATTACH);
  1691. WritePrivateProfileString(szSection, NULL, NULL, g_App.szWinBomIniFile);
  1692. WritePrivateProfileString(szSection, NULL, NULL, g_App.szWinBomIniFile);
  1693. // Nuke the detach section from both files.
  1694. //
  1695. hrPrintf=StringCchPrintf(lpEnd, (MAX_SECTIONNAME-lstrlen(szSection)), STR_INI_SEC_ADVAPP_STAGE, INI_VAL_WBOM_DETACH);
  1696. WritePrivateProfileString(szSection, NULL, NULL, g_App.szWinBomIniFile);
  1697. WritePrivateProfileString(szSection, NULL, NULL, g_App.szWinBomIniFile);
  1698. // Nuke the stage section from both files.
  1699. //
  1700. hrPrintf=StringCchPrintf(lpEnd, (MAX_SECTIONNAME-lstrlen(szSection)), STR_INI_SEC_ADVAPP_STAGE, INI_VAL_WBOM_STAGE);
  1701. WritePrivateProfileString(szSection, NULL, NULL, g_App.szWinBomIniFile);
  1702. WritePrivateProfileString(szSection, NULL, NULL, g_App.szWinBomIniFile);
  1703. }
  1704. }
  1705. static void StrCpyDbl(LPTSTR lpDst, LPTSTR lpSrc)
  1706. {
  1707. while ( *lpDst++ = *lpSrc )
  1708. {
  1709. if ( CHR_QUOTE == *lpSrc )
  1710. *lpDst++ = *lpSrc;
  1711. lpSrc++;
  1712. }
  1713. }
  1714. static BOOL FindUncPath(LPTSTR lpPath, DWORD cbPath)
  1715. {
  1716. TCHAR szUnc[MAX_PATH],
  1717. szFullPath[MAX_PATH] = NULLSTR,
  1718. szSearch[MAX_PATH],
  1719. szFullSearch[MAX_PATH],
  1720. szDrive[] = _T("_:");
  1721. LPTSTR lpFilePart;
  1722. DWORD cbUnc = AS(szUnc);
  1723. BOOL bRet = FALSE;
  1724. HRESULT hrCat;
  1725. // Make sure we have a full path.
  1726. //
  1727. if ( GetFullPathName(lpPath, AS(szFullPath), szFullPath, &lpFilePart) && szFullPath[0] && ISLET(szFullPath[0]) )
  1728. {
  1729. //
  1730. // First see if the drive is actually a network drive.
  1731. //
  1732. // This will get the UNC if the drive passed in is a mapped
  1733. // network drive.
  1734. //
  1735. szDrive[0] = szFullPath[0];
  1736. if ( WNetGetConnection(szDrive, szUnc, &cbUnc) == NO_ERROR )
  1737. {
  1738. // Only add on the rest of the path if more than the root
  1739. // directory was passed in.
  1740. //
  1741. if ( lstrlen(szFullPath) > 3 )
  1742. {
  1743. hrCat=StringCchCat(szUnc, AS(szUnc), szFullPath+2);
  1744. }
  1745. // Copy the path to return back and set the return value
  1746. // to true.
  1747. //
  1748. lstrcpyn(lpPath, szUnc, cbPath);
  1749. bRet = TRUE;
  1750. }
  1751. else
  1752. {
  1753. //
  1754. // Must be local, so try to see if the path, or any parent
  1755. // path is shared out.
  1756. //
  1757. // Start our search from the directory passed in and then
  1758. // loop down the path until we find a shared folder, or the
  1759. // root directory.
  1760. //
  1761. lstrcpyn(szFullSearch, szFullPath, AS(szFullSearch));
  1762. if ( lpFilePart && !DirectoryExists(szFullSearch) )
  1763. {
  1764. // If we know that what they passed in isn't a directory (most
  1765. // likely a file name then), then we can just chop off the file
  1766. // part and start with the directory and not the file.
  1767. //
  1768. szFullSearch[lstrlen(szFullSearch) - lstrlen(lpFilePart)] = NULLCHR;
  1769. }
  1770. do
  1771. {
  1772. // If the folder is shared, use it.
  1773. //
  1774. if ( DirectoryExists(szFullSearch) &&
  1775. IsFolderShared(szFullSearch, szUnc, AS(szUnc)) )
  1776. {
  1777. // Only add on the rest of the path if more than the root
  1778. // directory was passed in.
  1779. //
  1780. if ( lstrlen(szFullPath) > 3 )
  1781. {
  1782. AddPathN(szUnc, szFullPath + lstrlen(szFullSearch),AS(szUnc));
  1783. }
  1784. // Copy the path to return back and set the return value
  1785. // to true.
  1786. //
  1787. lstrcpyn(lpPath, szUnc, cbPath);
  1788. bRet = TRUE;
  1789. }
  1790. else
  1791. {
  1792. // Not shared, so try the parent folder. We will quit when
  1793. // we reach the root.
  1794. //
  1795. lstrcpyn(szSearch, szFullSearch, AS(szSearch));
  1796. AddPathN(szSearch, _T(".."),AS(szSearch));
  1797. }
  1798. }
  1799. while ( ( !bRet ) &&
  1800. ( lstrlen(szFullSearch) > 3 ) &&
  1801. ( GetFullPathName(szSearch, AS(szFullSearch), szFullSearch, &lpFilePart) ) &&
  1802. ( szFullSearch[0] ) );
  1803. }
  1804. }
  1805. return bRet;
  1806. }
  1807. static void EnableControls(HWND hwnd, UINT uId)
  1808. {
  1809. BOOL fEnable = TRUE;
  1810. switch ( uId )
  1811. {
  1812. case IDC_APP_STAGE:
  1813. // Enable/disable all the stuff under the stage check box.
  1814. //
  1815. fEnable = ( IsDlgButtonChecked(hwnd, IDC_APP_STAGE) == BST_CHECKED );
  1816. EnableWindow(GetDlgItem(hwnd, IDC_APP_STAGEPATH_TEXT), fEnable);
  1817. EnableWindow(GetDlgItem(hwnd, IDC_APP_STAGEPATH), fEnable);
  1818. break;
  1819. case IDC_APP_TYPE_GEN:
  1820. case IDC_APP_TYPE_MSI:
  1821. case IDC_APP_TYPE_INF:
  1822. // Enable/disable any stuff under the different radio buttons.
  1823. //
  1824. fEnable = ( IsDlgButtonChecked(hwnd, IDC_APP_TYPE_INF) == BST_CHECKED );
  1825. EnableWindow(GetDlgItem(hwnd, IDC_APP_INF_SECTION_TEXT), fEnable);
  1826. EnableWindow(GetDlgItem(hwnd, IDC_APP_INF_SECTION), fEnable);
  1827. EnableWindow(GetDlgItem(hwnd, IDC_APP_ARGS_TEXT), !fEnable);
  1828. EnableWindow(GetDlgItem(hwnd, IDC_APP_ARGS), !fEnable);
  1829. break;
  1830. case IDC_APP_CREDENTIALS:
  1831. // Enable/disable any stuff under the user credentials check box.
  1832. //
  1833. fEnable = ( IsDlgButtonChecked(hwnd, uId) == BST_CHECKED );
  1834. EnableWindow(GetDlgItem(hwnd, IDC_APP_USERNAME_TEXT), fEnable);
  1835. EnableWindow(GetDlgItem(hwnd, IDC_APP_USERNAME), fEnable);
  1836. EnableWindow(GetDlgItem(hwnd, IDC_APP_PASSWORD_TEXT), fEnable);
  1837. EnableWindow(GetDlgItem(hwnd, IDC_APP_PASSWORD), fEnable);
  1838. EnableWindow(GetDlgItem(hwnd, IDC_APP_CONFIRM_TEXT), fEnable);
  1839. EnableWindow(GetDlgItem(hwnd, IDC_APP_CONFIRM), fEnable);
  1840. break;
  1841. }
  1842. }
  1843. static BOOL AppInternal(LPTSTR lpszAppName)
  1844. {
  1845. BOOL bRet = FALSE,
  1846. bLoop;
  1847. HINF hInf;
  1848. INFCONTEXT InfContext;
  1849. DWORD dwErr,
  1850. dwResId;
  1851. LPTSTR lpszResName;
  1852. // Go through the reserved app names in the input inf file.
  1853. //
  1854. if ( (hInf = SetupOpenInfFile(g_App.szOpkInputInfFile, NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, &dwErr)) != INVALID_HANDLE_VALUE )
  1855. {
  1856. // Loop thru each line in the reserved App Names section.
  1857. //
  1858. for ( bLoop = SetupFindFirstLine(hInf, INI_SEC_RESERVEDNAMES, NULL, &InfContext);
  1859. bLoop && !bRet;
  1860. bLoop = SetupFindNextLine(&InfContext, &InfContext) )
  1861. {
  1862. // Get the resreved name resource ID.
  1863. //
  1864. dwResId = 0;
  1865. if ( ( SetupGetIntField(&InfContext, 1, &dwResId) && dwResId ) &&
  1866. ( lpszResName = AllocateString(NULL, dwResId) ) )
  1867. {
  1868. // If the match (case and all) then they can't use
  1869. // this name because we use it for internal stuff.
  1870. //
  1871. if ( lstrcmp(lpszAppName, lpszResName) == 0 )
  1872. {
  1873. bRet = TRUE;
  1874. }
  1875. FREE(lpszResName);
  1876. }
  1877. }
  1878. // We are done, so close the INF file.
  1879. //
  1880. SetupCloseInfFile(hInf);
  1881. }
  1882. return bRet;
  1883. }