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.

1356 lines
49 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. //---------------------------------------------------------------------------
  4. #include "grpconv.h"
  5. #include "gcinst.h"
  6. #include "util.h"
  7. #include <shellp.h>
  8. #include <trayp.h>
  9. #include <regstr.h>
  10. #include <shguidp.h>
  11. #include <windowsx.h>
  12. #include "rcids.h"
  13. #include "group.h"
  14. #include <..\..\inc\krnlcmn.h> // GetProcessDword
  15. #ifdef WINNT
  16. // NT is unicode so use a larger buffer since sizeof(TCHAR) is > than on win95
  17. #define BUFSIZES 40960
  18. #else
  19. // on win95 GetPrivateProfileSection has a 32767 char limit, so
  20. // we make this a bit smaller
  21. #define BUFSIZES 20480
  22. #endif // WINNT
  23. // Define checkbox states for listview
  24. #define LVIS_GCNOCHECK 0x1000
  25. #define LVIS_GCCHECK 0x2000
  26. #define HSZNULL 0
  27. #define HCONVNULL 0
  28. #define HCONVLISTNULL 0
  29. #define DDETIMEOUT 20*1000
  30. extern UINT GC_TRACE;
  31. extern const TCHAR c_szMapGroups[];
  32. BOOL g_fDoProgmanDde = FALSE;
  33. BOOL g_fInitDDE = FALSE;
  34. #define CH_COLON TEXT(':')
  35. //---------------------------------------------------------------------------
  36. // Global to this file only...
  37. static const TCHAR c_szGrpConvInf[] = TEXT("setup.ini");
  38. static const TCHAR c_szGrpConvInfOld[] = TEXT("setup.old");
  39. static const TCHAR c_szExitProgman[] = TEXT("[ExitProgman(1)]");
  40. static const TCHAR c_szAppProgman[] = TEXT("AppProgman");
  41. static const TCHAR c_szEnableDDE[] = TEXT("EnableDDE");
  42. static const TCHAR c_szProgmanOnly[] = TEXT("progman.only");
  43. static const TCHAR c_szProgmanGroups[] = TEXT("progman.groups");
  44. static const TCHAR c_szDesktopGroups[] = TEXT("desktop.groups");
  45. static const TCHAR c_szStartupGroups[] = TEXT("startup.groups");
  46. static const TCHAR c_szSendToGroups[] = TEXT("sendto.groups");
  47. static const TCHAR c_szRecentDocsGroups[] = TEXT("recentdocs.groups");
  48. //---------------------------------------------------------------------------
  49. const TCHAR c_szProgmanIni[] = TEXT("progman.ini");
  50. const TCHAR c_szStartup[] = TEXT("Startup");
  51. const TCHAR c_szProgmanExe[] = TEXT("progman.exe");
  52. const TCHAR c_szProgman[] = TEXT("Progman");
  53. // NB This must match the one in cabinet.
  54. static const TCHAR c_szRUCabinet[] = TEXT("[ConfirmCabinetID]");
  55. typedef struct
  56. {
  57. DWORD dwInst;
  58. HCONVLIST hcl;
  59. HCONV hconv;
  60. BOOL fStartedProgman;
  61. } PMDDE, *PPMDDE;
  62. //
  63. // This function grovles HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\ShellFolders
  64. // and creates a DPA with strings of all the speial folders
  65. //
  66. BOOL CreateSpecialFolderDPA(HDPA* phdpaSF)
  67. {
  68. HKEY hkSP;
  69. TCHAR szValueName[MAX_PATH];
  70. DWORD cbValueName;
  71. DWORD cbData;
  72. DWORD dwIndex = 0;
  73. LONG lRet = ERROR_SUCCESS;
  74. // we should only ever be called once to populate the dpa
  75. if (*phdpaSF != NULL)
  76. return FALSE;
  77. if (RegOpenKeyEx(HKEY_CURRENT_USER,
  78. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"),
  79. 0,
  80. KEY_QUERY_VALUE,
  81. &hkSP) != ERROR_SUCCESS)
  82. {
  83. // couldnt open the key, so bail
  84. return FALSE;
  85. }
  86. *phdpaSF = DPA_Create(4);
  87. do
  88. {
  89. cbValueName = ARRAYSIZE(szValueName);
  90. lRet = RegEnumValue(hkSP,
  91. dwIndex,
  92. szValueName,
  93. &cbValueName,
  94. NULL,
  95. NULL,
  96. NULL,
  97. &cbData);
  98. if (lRet == ERROR_SUCCESS)
  99. {
  100. LPTSTR pszValueData = LocalAlloc(LPTR, cbData);
  101. if (!pszValueData)
  102. break;
  103. if (RegQueryValueEx(hkSP,
  104. szValueName,
  105. NULL,
  106. NULL,
  107. (LPBYTE)pszValueData,
  108. &cbData) == ERROR_SUCCESS)
  109. {
  110. DPA_AppendPtr(*phdpaSF, pszValueData);
  111. }
  112. }
  113. dwIndex++;
  114. } while (lRet != ERROR_NO_MORE_ITEMS);
  115. return TRUE;
  116. }
  117. //
  118. // SafeRemoveDirectory checks to make sure that we arent removing a "special"
  119. // folder. On win95 when we remove the last shortcut from the %windir%\desktop folder,
  120. // we go and remove that as well. This causes the shell to hang among other bad things.
  121. //
  122. BOOL SafeRemoveDirectory(LPCTSTR pszDir)
  123. {
  124. static HDPA hdpaSF = NULL;
  125. int iMax;
  126. int iIndex;
  127. if (!hdpaSF && !CreateSpecialFolderDPA(&hdpaSF))
  128. {
  129. // if we cant read the special folders, error on the
  130. // side of caution
  131. return FALSE;
  132. }
  133. iMax = DPA_GetPtrCount(hdpaSF);
  134. for (iIndex = 0; iIndex < iMax; iIndex++)
  135. {
  136. LPTSTR pszSpecialFolder = DPA_GetPtr(hdpaSF, iIndex);
  137. if (!pszSpecialFolder)
  138. continue;
  139. if (lstrcmpi(pszDir, pszSpecialFolder) == 0)
  140. return FALSE;
  141. }
  142. // no special folders matched, so its ok to delete it
  143. return Win32RemoveDirectory(pszDir);
  144. }
  145. //---------------------------------------------------------------------------
  146. void Progman_ReplaceItem(PPMDDE ppmdde, LPCTSTR szName, LPCTSTR pszCL,
  147. LPCTSTR szArgs, LPCTSTR szIP, int iIcon, LPCTSTR szWD)
  148. {
  149. TCHAR szBuf[512];
  150. if (g_fDoProgmanDde)
  151. {
  152. wsprintf(szBuf, TEXT("[ReplaceItem(\"%s\")]"), szName);
  153. DdeClientTransaction((LPBYTE)szBuf,
  154. (lstrlen(szBuf)+1)*SIZEOF(TCHAR),
  155. ppmdde->hconv,
  156. HSZNULL,
  157. 0,
  158. XTYP_EXECUTE,
  159. DDETIMEOUT,
  160. NULL);
  161. wsprintf(szBuf, TEXT("[AddItem(\"%s %s\",\"%s\",%s,%d,-1,-1,%s)]"), pszCL, szArgs,
  162. szName, szIP, iIcon, szWD);
  163. DdeClientTransaction((LPBYTE)szBuf, (lstrlen(szBuf)+1)*SIZEOF(TCHAR), ppmdde->hconv, HSZNULL, 0,
  164. XTYP_EXECUTE, DDETIMEOUT, NULL);
  165. }
  166. }
  167. //---------------------------------------------------------------------------
  168. void Progman_DeleteItem(PPMDDE ppmdde, LPCTSTR szName)
  169. {
  170. // NB Progman only support 256 char commands.
  171. TCHAR szBuf[256];
  172. if (g_fDoProgmanDde)
  173. {
  174. wsprintf(szBuf, TEXT("[DeleteItem(%s)]"), szName);
  175. DdeClientTransaction((LPBYTE)szBuf, (lstrlen(szBuf)+1)*SIZEOF(TCHAR), ppmdde->hconv, HSZNULL, 0,
  176. XTYP_EXECUTE, DDETIMEOUT, NULL);
  177. }
  178. }
  179. //---------------------------------------------------------------------------
  180. void Reg_SetMapGroupEntry(LPCTSTR pszOld, LPCTSTR pszNew)
  181. {
  182. Reg_SetString(g_hkeyGrpConv, c_szMapGroups, pszOld, pszNew);
  183. DebugMsg(DM_TRACE, TEXT("gc.r_cmge: From %s to %s"), pszOld, pszNew);
  184. }
  185. //---------------------------------------------------------------------------
  186. void GetProperGroupName(LPCTSTR pszGroupPath, LPTSTR pszGroup, int cchGroup)
  187. {
  188. LPTSTR pszGroupName;
  189. // Progman only supports a single level hierachy so...
  190. pszGroupName = PathFindFileName(pszGroupPath);
  191. // NB If we do have a group within a group then we should add a
  192. // MapGroup entry to the registry so running GrpConv in the
  193. // future won't cause groups to get duplicated.
  194. if (lstrcmpi(pszGroupName, pszGroupPath) != 0)
  195. {
  196. Reg_SetMapGroupEntry(pszGroupName, pszGroupPath);
  197. }
  198. // A missing group name implies use a default.
  199. if (!pszGroupName || !*pszGroupName)
  200. {
  201. LoadString(g_hinst, IDS_PROGRAMS, pszGroup, cchGroup);
  202. }
  203. else
  204. {
  205. lstrcpyn(pszGroup, pszGroupName, cchGroup);
  206. }
  207. }
  208. //---------------------------------------------------------------------------
  209. BOOL Progman_CreateGroup(PPMDDE ppmdde, LPCTSTR pszGroupPath)
  210. {
  211. // NB Progman only support 256 char commands.
  212. TCHAR szBuf[256];
  213. TCHAR szGroup[MAX_PATH];
  214. HDDEDATA hdata;
  215. GetProperGroupName(pszGroupPath, szGroup, ARRAYSIZE(szGroup));
  216. if (g_fDoProgmanDde)
  217. {
  218. wsprintf(szBuf, TEXT("[CreateGroup(%s)]"), szGroup);
  219. hdata = DdeClientTransaction((LPBYTE)szBuf, (lstrlen(szBuf)+1)*SIZEOF(TCHAR), ppmdde->hconv, HSZNULL, 0,
  220. XTYP_EXECUTE, DDETIMEOUT, NULL);
  221. Assert(hdata);
  222. }
  223. else
  224. return FALSE;
  225. return hdata ? TRUE : FALSE;
  226. }
  227. //---------------------------------------------------------------------------
  228. BOOL Progman_ShowGroup(PPMDDE ppmdde, LPCTSTR pszGroupPath)
  229. {
  230. // NB Progman only support 256 char commands.
  231. TCHAR szBuf[256];
  232. TCHAR szGroup[MAX_PATH];
  233. HDDEDATA hdata;
  234. GetProperGroupName(pszGroupPath, szGroup, ARRAYSIZE(szGroup));
  235. if (g_fDoProgmanDde)
  236. {
  237. wsprintf(szBuf, TEXT("[ShowGroup(%s, %d)]"), szGroup, SW_SHOWNORMAL);
  238. hdata = DdeClientTransaction((LPBYTE)szBuf, (lstrlen(szBuf)+1)*SIZEOF(TCHAR), ppmdde->hconv, HSZNULL, 0,
  239. XTYP_EXECUTE, DDETIMEOUT, NULL);
  240. Assert(hdata);
  241. }
  242. else
  243. return FALSE;
  244. return hdata ? TRUE : FALSE;
  245. }
  246. // Given a string that potentially could be "::{GUID}:DATA::....::{GUID}:DATA::Path",
  247. // return the pointer to the path. This starts after the last double-colon sequence.
  248. // (Darwin and Logo3 uses this format.)
  249. LPTSTR FindPathSection(LPCTSTR pszPath)
  250. {
  251. LPCTSTR psz = pszPath;
  252. LPCTSTR pszFirstColon = NULL;
  253. LPCTSTR pszDblColon = NULL;
  254. // Find the last double-colon sequence
  255. while (*psz)
  256. {
  257. if (*psz == CH_COLON)
  258. {
  259. // Was the previous character a colon too?
  260. if (pszFirstColon)
  261. {
  262. // Yes; remember that position
  263. pszDblColon = pszFirstColon;
  264. pszFirstColon = NULL;
  265. }
  266. else
  267. {
  268. // No; remember this as a potential for being the first colon
  269. // in a double-colon sequence
  270. pszFirstColon = psz;
  271. }
  272. }
  273. else
  274. pszFirstColon = NULL;
  275. psz = CharNext(psz);
  276. }
  277. if (pszDblColon)
  278. return (LPTSTR)pszDblColon+2; // skip the double-colon
  279. return (LPTSTR)pszPath;
  280. }
  281. #define BG_DELETE_EMPTY 0x0001
  282. #define BG_PROG_GRP_CREATED 0x0002
  283. #define BG_PROG_GRP_SHOWN 0x0004
  284. #define BG_SEND_TO_GRP 0x0008
  285. #define BG_LFN 0x0010
  286. #define BG_RECENT_DOCS 0x0020
  287. #define BG_SET_PROGRESS_TEXT 0x0040
  288. #define BG_FORCE_DESKTOP 0x0080
  289. #define BG_FORCE_STARTUP 0x0100
  290. #define BG_FORCE_RECENT 0x0200
  291. #define BG_FORCE_SENDTO 0x0400
  292. //---------------------------------------------------------------------------
  293. void BuildGroup(LPCTSTR lpszIniFileName, LPCTSTR lpszSection,
  294. LPCTSTR lpszGroupName, PPMDDE ppmdde, BOOL fUpdFolder, DWORD dwFlags)
  295. {
  296. // Data associated with readining in section.
  297. HGLOBAL hg;
  298. LPTSTR lpBuf; // Pointer to buffer to read section into
  299. int cb;
  300. LPTSTR pszLine;
  301. IShellLink *psl;
  302. TCHAR szName[MAX_PATH];
  303. TCHAR szCL[3*MAX_PATH]; // we make this 3*MAX_PATH so that DARWIN and LOGO3 callers can pass the extra information
  304. TCHAR szIP[2*MAX_PATH];
  305. TCHAR szArgs[2*MAX_PATH];
  306. TCHAR szGroupFolder[MAX_PATH];
  307. TCHAR szSpecialGrp[32];
  308. WCHAR wszPath[2*MAX_PATH];
  309. TCHAR szWD[2*MAX_PATH];
  310. TCHAR szDesc[3*MAX_PATH];
  311. TCHAR szNum[8]; // Should never exceed this!
  312. LPTSTR lpszArgs;
  313. TCHAR szCLPathPart[3*MAX_PATH]; // this 3*MAX_PATH because we use them to twiddle with szCL
  314. TCHAR szCLSpecialPart[3*MAX_PATH]; // this 3*MAX_PATH because we use them to twiddle with szCL
  315. int iLen;
  316. int iIcon;
  317. LPTSTR pszExt;
  318. // DWORD dwFlags = BG_DELETE_EMPTY;
  319. // BOOL fDeleteEmpty = TRUE;
  320. // BOOL fProgGrpCreated = FALSE;
  321. // BOOL fProgGrpShown = FALSE;
  322. // BOOL fSendToGrp = FALSE;
  323. // BOOL fLFN;
  324. Log(TEXT("Setup.Ini: %s"), lpszGroupName);
  325. DebugMsg(GC_TRACE, TEXT("gc.bg: Rebuilding %s"), (LPTSTR) lpszGroupName);
  326. // Special case [SendTo] section name - this stuff doesn't
  327. // need to be added to progman.
  328. LoadString(g_hinst, IDS_SENDTO, szSpecialGrp, ARRAYSIZE(szSpecialGrp));
  329. if ((dwFlags & BG_FORCE_SENDTO) || (lstrcmpi(lpszSection, szSpecialGrp) == 0))
  330. {
  331. DebugMsg(GC_TRACE, TEXT("gc.bg: SendTo section - no Progman group"));
  332. // fSendToGrp = TRUE;
  333. dwFlags |= BG_SEND_TO_GRP;
  334. }
  335. // Now lets read in the section for the group from the ini file
  336. // First allocate a buffer to read the section into
  337. hg = GlobalAlloc(GPTR, BUFSIZES); // Should never exceed 64K?
  338. if (hg)
  339. {
  340. lpBuf = GlobalLock(hg);
  341. // Special case the startup group.
  342. LoadString(g_hinst, IDS_STARTUP, szSpecialGrp, ARRAYSIZE(szSpecialGrp));
  343. // Is this the startup group?
  344. szGroupFolder[0] = TEXT('\0');
  345. if ((dwFlags & BG_FORCE_STARTUP) || (lstrcmpi(szSpecialGrp, lpszGroupName) == 0))
  346. {
  347. DebugMsg(DM_TRACE, TEXT("gc.bg: Startup group..."));
  348. // Yep, Try to get the new location.
  349. Reg_GetString(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER_SHELLFOLDERS, c_szStartup,
  350. szGroupFolder, SIZEOF(szGroupFolder));
  351. // fDeleteEmpty = FALSE;
  352. dwFlags &= ~BG_DELETE_EMPTY;
  353. }
  354. // Is this the desktop folder?
  355. LoadString(g_hinst, IDS_DESKTOP, szSpecialGrp, ARRAYSIZE(szSpecialGrp));
  356. if ((dwFlags & BG_FORCE_RECENT) || (lstrcmp(szSpecialGrp, PathFindFileName(lpszGroupName)) == 0))
  357. {
  358. DebugMsg(DM_TRACE, TEXT("gc.bg: Desktop group..."));
  359. // fDeleteEmpty = FALSE;
  360. dwFlags &= ~BG_DELETE_EMPTY;
  361. }
  362. // Special case the recent folder.
  363. LoadString(g_hinst, IDS_RECENT, szSpecialGrp, ARRAYSIZE(szSpecialGrp));
  364. if (lstrcmp(szSpecialGrp, lpszGroupName) == 0)
  365. {
  366. DebugMsg(DM_TRACE, TEXT("gc.bg: Recent group..."));
  367. dwFlags |= BG_RECENT_DOCS;
  368. dwFlags &= ~BG_DELETE_EMPTY;
  369. }
  370. if (SUCCEEDED(ICoCreateInstance(&CLSID_ShellLink, &IID_IShellLink, &psl)))
  371. {
  372. IPersistFile *ppf;
  373. psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, &ppf);
  374. // now Read in the secint into our buffer
  375. cb = GetPrivateProfileSection(lpszSection, lpBuf, BUFSIZES/SIZEOF(TCHAR), lpszIniFileName);
  376. if (cb > 0)
  377. {
  378. pszLine = lpBuf;
  379. // Create the folder...
  380. // Use a generic name until we get items to add so we
  381. // don't stick group names like "AT&T" in users faces
  382. // when all we're trying to do is delete items from them.
  383. Group_SetProgressNameAndRange((LPCTSTR)-1, cb);
  384. // Did we fill in the szGroupFolder yet?
  385. if (!*szGroupFolder)
  386. {
  387. // some people pass us a fully qualified path for lpszGroupName (eg c:\foo\bar or \\pyrex\user\foo)
  388. // if that is the case, then use the path they specify
  389. if ((PathGetDriveNumber((LPTSTR)lpszGroupName) != -1) || PathIsUNC((LPTSTR)lpszGroupName))
  390. {
  391. lstrcpy(szGroupFolder, lpszGroupName);
  392. iLen = 2; // let PathRemoveIllegalChars validate the whole string after "c:" or "\\"
  393. }
  394. else
  395. {
  396. // non-fully qualified groupname, so just construct it under startmenu\programs
  397. SHGetSpecialFolderPath(NULL, szGroupFolder, CSIDL_PROGRAMS, TRUE);
  398. iLen = lstrlen(szGroupFolder);
  399. PathAppend(szGroupFolder, lpszGroupName);
  400. }
  401. PathRemoveIllegalChars(szGroupFolder, iLen, PRICF_ALLOWSLASH);
  402. // This should take care of mapping it if machine does not support LFNs.
  403. PathQualify(szGroupFolder);
  404. }
  405. else
  406. {
  407. DebugMsg(DM_TRACE, TEXT("gc.bg: Startup group mapped to %s."), szGroupFolder);
  408. }
  409. if (fUpdFolder && !(dwFlags & BG_RECENT_DOCS))
  410. {
  411. if (!PathFileExists(szGroupFolder))
  412. {
  413. if (SHCreateDirectory(NULL, szGroupFolder) != 0)
  414. {
  415. DebugMsg(DM_ERROR, TEXT("gc.bg: Can't create %s folder."), (LPTSTR) szGroupFolder);
  416. }
  417. }
  418. }
  419. // Keep track if we can create LFN link names on this drive.
  420. // fLFN = IsLFNDrive(szGroupFolder);
  421. if (IsLFNDrive((LPCTSTR)szGroupFolder))
  422. dwFlags |= BG_LFN;
  423. #ifdef DEBUG
  424. if (!(dwFlags & BG_LFN))
  425. DebugMsg(DM_TRACE, TEXT("gc.bg: Using short names for this group."), szName);
  426. #endif
  427. // Add the items...
  428. //
  429. // Warning: it appears like the data in the setup.ini file does not
  430. // match the standard x=y, but is simpy x or x,y,z so we must
  431. // 1 bias the indexes to ParseField
  432. while (*pszLine)
  433. {
  434. // Set progress on how many bytes we have processed.
  435. Group_SetProgress((int)(pszLine-lpBuf));
  436. DebugMsg(GC_TRACE, TEXT("gc.bg: Create Link:%s"), (LPTSTR)pszLine);
  437. // Add item.
  438. // Get the short name if we're on a SFN drive.
  439. szName[0] = TEXT('\0');
  440. if (!(dwFlags & BG_LFN))
  441. ParseField(pszLine, 7, szName, ARRAYSIZE(szName));
  442. // Get the long name if we're not on an SFN drive
  443. // or if there is no short name.
  444. if (!*szName)
  445. ParseField(pszLine, 1, szName, ARRAYSIZE(szName));
  446. DebugMsg(GC_TRACE, TEXT(" Link:%s"), (LPTSTR)szName);
  447. // Dutch/French sometimes have illegal chars in their ini files.
  448. // NB Progman needs the unmangled names so only remove illegal chars
  449. // from the Explorer string, not szName.
  450. // NB Names can contain slashes so PathFindFileName() isn't very
  451. // useful here.
  452. iLen = lstrlen(szGroupFolder);
  453. PathAppend(szGroupFolder, szName);
  454. PathRemoveIllegalChars(szGroupFolder, iLen+1, PRICF_NORMAL);
  455. // Handle LFNs on a SFN volume.
  456. PathQualify(szGroupFolder);
  457. if (ParseField(pszLine, 2, szCL, ARRAYSIZE(szCL)) && (*szCL != 0))
  458. {
  459. // assume that this is not a DARWIN or LOGO3 special link, and thus
  460. // the path is just what we just read (szCL)
  461. lstrcpy(szCLPathPart, szCL);
  462. lstrcpy(szCLSpecialPart, szCL);
  463. // We're going to have to add something to the group,
  464. // switch to using it's real name.
  465. if (!(dwFlags & BG_SET_PROGRESS_TEXT))
  466. {
  467. dwFlags |= BG_SET_PROGRESS_TEXT;
  468. Group_SetProgressNameAndRange(lpszGroupName, cb);
  469. }
  470. // see if we have ":: or just :: which indicates a special link.
  471. // special links have a path that is of the form:
  472. //
  473. // ::{GUID1}:data1::{GUID2}:data2::fullpathtolinktarget
  474. //
  475. // where there could be any number of guid+data sections and the full
  476. // path to the link target at the end is optional.
  477. //
  478. // We seperate this out into the "special" part which contains the guids
  479. // and the "path" part which has the fullpathtolinktarget at the end.
  480. if (szCLSpecialPart[0]==TEXT('"') && szCLSpecialPart[1]==TEXT(':') && szCLSpecialPart[2]==TEXT(':'))
  481. {
  482. // the string was quoted and it is a special string
  483. LPTSTR pszRealPathBegins;
  484. int cch = lstrlen(szCLSpecialPart)+1;
  485. // get rid of the leading "
  486. hmemcpy(szCLSpecialPart, szCLSpecialPart+1, cch * SIZEOF(TCHAR));
  487. // find where the real path begins
  488. pszRealPathBegins = FindPathSection(szCLSpecialPart);
  489. if (*pszRealPathBegins)
  490. {
  491. // a path part exists, so add a leading ", and copy
  492. // the real fullpathtolinktarget there.
  493. lstrcpy(szCLPathPart, TEXT("\""));
  494. lstrcat(szCLPathPart, pszRealPathBegins);
  495. // terminate the special part after the last ::
  496. *pszRealPathBegins = TEXT('\0');
  497. }
  498. else
  499. {
  500. // no there is no real path, just special info
  501. *szCLPathPart = TEXT('\0');
  502. }
  503. }
  504. else if (szCLSpecialPart[0]==TEXT(':') && szCLSpecialPart[1]==TEXT(':'))
  505. {
  506. // the string was not quoted and it is a special string
  507. LPTSTR pszRealPathBegins = FindPathSection(szCLSpecialPart);
  508. if (*pszRealPathBegins)
  509. {
  510. // we have a real path, so save it
  511. lstrcpy(szCLPathPart, pszRealPathBegins);
  512. // terminate the special part after the last ::
  513. *pszRealPathBegins = TEXT('\0');
  514. }
  515. else
  516. {
  517. // no there is no real path, just special info
  518. *szCLPathPart = TEXT('\0');
  519. }
  520. }
  521. else
  522. {
  523. // not a "special" link
  524. *szCLSpecialPart = TEXT('\0');
  525. }
  526. if (*szCLPathPart)
  527. {
  528. // we have a command line so check for args
  529. szArgs[0] = TEXT('\0');
  530. lpszArgs = PathGetArgs(szCLPathPart);
  531. if (*lpszArgs)
  532. {
  533. *(lpszArgs-1) = TEXT('\0');
  534. lstrcpyn(szArgs, lpszArgs, ARRAYSIZE(szArgs));
  535. DebugMsg(GC_TRACE, TEXT(" Cmd Args:%s"), szArgs);
  536. }
  537. psl->lpVtbl->SetArguments(psl, szArgs); // arguments
  538. PathUnquoteSpaces(szCLPathPart);
  539. PathResolve(szCLPathPart, NULL, 0);
  540. DebugMsg(GC_TRACE, TEXT(" cmd:%s"), (LPTSTR)szCLPathPart);
  541. }
  542. if (*szCLPathPart && (dwFlags & BG_RECENT_DOCS))
  543. {
  544. SHAddToRecentDocs(SHARD_PATH, szCLPathPart);
  545. // Progman is just going to get a group called "Documents".
  546. if (!(dwFlags & BG_PROG_GRP_CREATED))
  547. {
  548. if (Progman_CreateGroup(ppmdde, lpszGroupName))
  549. dwFlags |= BG_PROG_GRP_CREATED;
  550. }
  551. if (dwFlags & BG_PROG_GRP_CREATED)
  552. Progman_ReplaceItem(ppmdde, szName, szCLPathPart, NULL, NULL, 0, NULL);
  553. }
  554. else if (*szCLPathPart || *szCLSpecialPart)
  555. {
  556. // all we need to call is setpath, it takes care of creating the
  557. // pidl for us. We have to put back the special / path portions here
  558. // so we can pass the full DARWIN or LOGO3 information.
  559. lstrcpy(szCL, szCLSpecialPart);
  560. lstrcat(szCL, szCLPathPart);
  561. psl->lpVtbl->SetPath(psl, szCL);
  562. // Icon file.
  563. ParseField(pszLine, 3, szIP, ARRAYSIZE(szIP));
  564. ParseField(pszLine, 4, szNum, ARRAYSIZE(szNum));
  565. iIcon = StrToInt(szNum);
  566. DebugMsg(GC_TRACE, TEXT(" Icon:%s"), (LPTSTR)szIP);
  567. psl->lpVtbl->SetIconLocation(psl, szIP, iIcon);
  568. lstrcat(szGroupFolder, TEXT(".lnk"));
  569. // NB Field 5 is dependancy stuff that we don't
  570. // care about.
  571. // WD
  572. #ifdef WINNT
  573. /* For NT default to the users home directory, not nothing (which results in
  574. / the current directory, which is unpredictable) */
  575. lstrcpy( szWD, TEXT("%HOMEDRIVE%%HOMEPATH%") );
  576. #else
  577. szWD[0] = TEXT('\0');
  578. #endif
  579. ParseField(pszLine, 6, szWD, ARRAYSIZE(szWD));
  580. psl->lpVtbl->SetWorkingDirectory(psl, szWD);
  581. // Field 8 is description for the link
  582. ParseField(pszLine, 8, szDesc, ARRAYSIZE(szDesc));
  583. DebugMsg(GC_TRACE, TEXT(" Description:%s"), (LPTSTR)szDesc);
  584. psl->lpVtbl->SetDescription(psl, szDesc);
  585. StrToOleStrN(wszPath, ARRAYSIZE(wszPath), szGroupFolder, -1);
  586. if (fUpdFolder)
  587. ppf->lpVtbl->Save(ppf, wszPath, TRUE);
  588. // We've added stuff so don't bother trying to delete the folder
  589. // later.
  590. // fDeleteEmpty = FALSE;
  591. dwFlags &= ~BG_DELETE_EMPTY;
  592. // Defer group creation.
  593. // if (!fSendToGrp && !fProgGrpCreated)
  594. if (!(dwFlags & BG_SEND_TO_GRP) && !(dwFlags & BG_PROG_GRP_CREATED))
  595. {
  596. if (Progman_CreateGroup(ppmdde, lpszGroupName))
  597. dwFlags |= BG_PROG_GRP_CREATED;
  598. }
  599. // if (fProgGrpCreated)
  600. if (dwFlags & BG_PROG_GRP_CREATED)
  601. {
  602. // use szCLPathPart for good ol'e progman
  603. Progman_ReplaceItem(ppmdde, szName, szCLPathPart, szArgs, szIP, iIcon, szWD);
  604. }
  605. }
  606. else
  607. {
  608. // NB The assumption is that setup.ini will only contain links
  609. // to files that exist. If they don't exist we assume we have
  610. // a bogus setup.ini and skip to the next item.
  611. DebugMsg(DM_ERROR, TEXT("gc.bg: Bogus link info for item %s in setup.ini"), szName);
  612. }
  613. }
  614. else
  615. {
  616. // Delete all links with this name.
  617. // NB We need to get this from the registry eventually.
  618. if (fUpdFolder)
  619. {
  620. pszExt = szGroupFolder + lstrlen(szGroupFolder);
  621. lstrcpy(pszExt, TEXT(".lnk"));
  622. Win32DeleteFile(szGroupFolder);
  623. lstrcpy(pszExt, TEXT(".pif"));
  624. Win32DeleteFile(szGroupFolder);
  625. }
  626. // Tell progman too. Be careful not to create empty groups just
  627. // to try to delete items from it.
  628. // if (!fProgGrpShown)
  629. if (!(dwFlags & BG_PROG_GRP_SHOWN))
  630. {
  631. // Does the group already exist?
  632. if (Progman_ShowGroup(ppmdde, lpszGroupName))
  633. dwFlags |= BG_PROG_GRP_SHOWN;
  634. // if (fProgGrpShown)
  635. if (dwFlags & BG_PROG_GRP_SHOWN)
  636. {
  637. // Yep, activate it.
  638. Progman_CreateGroup(ppmdde, lpszGroupName);
  639. }
  640. }
  641. // If it exists, then delete the item otherwise don't bother.
  642. // if (fProgGrpShown)
  643. if (dwFlags & BG_PROG_GRP_SHOWN)
  644. Progman_DeleteItem(ppmdde, szName);
  645. }
  646. PathRemoveFileSpec(szGroupFolder); // rip the link name off for next link
  647. // Now point to the next line
  648. pszLine += lstrlen(pszLine) + 1;
  649. }
  650. }
  651. // The group might now be empty now - try to delete it, if there's still
  652. // stuff in there then this will safely fail. NB We don't delete empty
  653. // Startup groups to give users a clue that it's something special.
  654. // if (fUpdFolder && fDeleteEmpty && *szGroupFolder)
  655. if (fUpdFolder && (dwFlags & BG_DELETE_EMPTY) && *szGroupFolder)
  656. {
  657. DebugMsg(DM_TRACE, TEXT("gc.bg: Deleting %s"), szGroupFolder);
  658. // keep trying to remove any directories up the path,
  659. // so we dont leave an empty directory tree structure.
  660. //
  661. // SafeRemoveDirectory fails if the directory is a special folder
  662. if(SafeRemoveDirectory(szGroupFolder))
  663. {
  664. while(PathRemoveFileSpec(szGroupFolder))
  665. {
  666. if (!SafeRemoveDirectory(szGroupFolder))
  667. break;
  668. }
  669. }
  670. }
  671. ppf->lpVtbl->Release(ppf);
  672. psl->lpVtbl->Release(psl);
  673. }
  674. }
  675. if(hg)
  676. {
  677. GlobalFree(hg);
  678. }
  679. Log(TEXT("Setup.Ini: %s done."), lpszGroupName);
  680. }
  681. //---------------------------------------------------------------------------
  682. HDDEDATA CALLBACK DdeCallback(UINT uType, UINT uFmt, HCONV hconv, HSZ hsz1,
  683. HSZ hsz2, HDDEDATA hdata, ULONG_PTR dwData1, ULONG_PTR dwData2)
  684. {
  685. return (HDDEDATA) NULL;
  686. }
  687. //---------------------------------------------------------------------------
  688. BOOL _PartnerIsCabinet(HCONV hconv)
  689. {
  690. //
  691. // (reinerf)
  692. // this sends the magical string [ConfirmCabinetID] to our current DDE partner.
  693. // Explorer.exe will return TRUE here, so we can distinguish it from progman.exe
  694. // which returns FALSE.
  695. //
  696. if (DdeClientTransaction((LPBYTE)c_szRUCabinet, SIZEOF(c_szRUCabinet),
  697. hconv, HSZNULL, 0, XTYP_EXECUTE, DDETIMEOUT, NULL))
  698. {
  699. return TRUE;
  700. }
  701. else
  702. {
  703. return FALSE;
  704. }
  705. }
  706. //---------------------------------------------------------------------------
  707. // If progman is not the shell then it will be refusing DDE messages so we
  708. // have to enable it here.
  709. void _EnableProgmanDDE(void)
  710. {
  711. HWND hwnd;
  712. hwnd = FindWindow(c_szProgman, NULL);
  713. while (hwnd)
  714. {
  715. // Is it progman?
  716. if (GetProp(hwnd, c_szAppProgman))
  717. {
  718. DebugMsg(DM_TRACE, TEXT("gc.epd: Found progman, enabling dde."));
  719. // NB Progman will clean this up at terminate time.
  720. SetProp(hwnd, c_szEnableDDE, (HANDLE)TRUE);
  721. break;
  722. }
  723. hwnd = GetWindow(hwnd, GW_HWNDNEXT);
  724. }
  725. }
  726. //---------------------------------------------------------------------------
  727. // Will the real progman please stand up?
  728. BOOL Progman_DdeConnect(PPMDDE ppmdde, HSZ hszService, HSZ hszTopic)
  729. {
  730. HCONV hconv = HCONVNULL;
  731. Assert(ppmdde);
  732. DebugMsg(DM_TRACE, TEXT("gc.p_dc: Looking for progman..."));
  733. _EnableProgmanDDE();
  734. ppmdde->hcl = DdeConnectList(ppmdde->dwInst, hszService, hszTopic, HCONVLISTNULL, NULL);
  735. if (ppmdde->hcl)
  736. {
  737. hconv = DdeQueryNextServer(ppmdde->hcl, hconv);
  738. while (hconv)
  739. {
  740. // DdeQueryConvInfo(hconv, QID_SYNC, &ci);
  741. if (!_PartnerIsCabinet(hconv))
  742. {
  743. DebugMsg(DM_TRACE, TEXT("gc.p_dc: Found likely candidate %x"), hconv);
  744. ppmdde->hconv = hconv;
  745. return TRUE;
  746. }
  747. else
  748. {
  749. DebugMsg(DM_TRACE, TEXT("gc.p_dc: Ignoring %x"), hconv);
  750. }
  751. hconv = DdeQueryNextServer(ppmdde->hcl, hconv);
  752. }
  753. }
  754. DebugMsg(DM_TRACE, TEXT("gc.p_dc: Couldn't find it."));
  755. return FALSE;
  756. }
  757. //---------------------------------------------------------------------------
  758. BOOL Window_CreatedBy16bitProcess(HWND hwnd)
  759. {
  760. DWORD idProcess;
  761. #ifdef WINNT
  762. return( LOWORD(GetWindowLongPtr(hwnd,GWLP_HINSTANCE)) != 0 );
  763. #else
  764. GetWindowThreadProcessId(hwnd, &idProcess);
  765. return GetProcessDword(idProcess, GPD_FLAGS) & GPF_WIN16_PROCESS;
  766. #endif
  767. }
  768. //---------------------------------------------------------------------------
  769. // (reinerf)
  770. //
  771. // check what the user has as their shell= set to (this is in the
  772. // registry on NT and in the win.ini on win95/memphis.
  773. BOOL IsShellExplorer()
  774. {
  775. TCHAR szShell[MAX_PATH];
  776. #ifdef WINNT
  777. {
  778. HKEY hKeyWinlogon;
  779. DWORD dwSize;
  780. szShell[0] = TEXT('\0');
  781. // Starting with NT4 Service Pack 3, NT honors the value in HKCU over
  782. // the one in HKLM, so read that first.
  783. if (RegOpenKeyEx(HKEY_CURRENT_USER,
  784. TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"),
  785. 0L,
  786. KEY_QUERY_VALUE,
  787. &hKeyWinlogon) == ERROR_SUCCESS)
  788. {
  789. dwSize = SIZEOF(szShell);
  790. RegQueryValueEx(hKeyWinlogon, TEXT("shell"), NULL, NULL, (LPBYTE)szShell, &dwSize);
  791. RegCloseKey(hKeyWinlogon);
  792. }
  793. if (!szShell[0])
  794. {
  795. // no HKCU value, so check HKLM
  796. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  797. TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"),
  798. 0L,
  799. KEY_QUERY_VALUE,
  800. &hKeyWinlogon) == ERROR_SUCCESS)
  801. {
  802. dwSize = SIZEOF(szShell);
  803. RegQueryValueEx(hKeyWinlogon, TEXT("shell"), NULL, NULL, (LPBYTE)szShell, &dwSize);
  804. RegCloseKey(hKeyWinlogon);
  805. }
  806. }
  807. }
  808. #else
  809. {
  810. // on win95 we need to read the shell= line from the win.ini
  811. GetPrivateProfileString(TEXT("boot"),
  812. TEXT("shell"),
  813. TEXT("explorer.exe"),
  814. szShell,
  815. MAX_PATH,
  816. TEXT("system.ini"));
  817. }
  818. #endif
  819. if (lstrcmpi(TEXT("explorer.exe"), szShell) == 0)
  820. return TRUE;
  821. else
  822. return FALSE;
  823. }
  824. //---------------------------------------------------------------------------
  825. BOOL Progman_IsRunning(void)
  826. {
  827. HWND hwnd;
  828. TCHAR sz[MAX_PATH] = {0};
  829. hwnd = GetWindow(GetDesktopWindow(), GW_CHILD);
  830. while (hwnd)
  831. {
  832. GetClassName(hwnd, sz, ARRAYSIZE(sz));
  833. #ifdef WINNT
  834. if (lstrcmpi(sz, c_szProgman) == 0)
  835. #else
  836. if (Window_CreatedBy16bitProcess(hwnd) &&
  837. (lstrcmpi(sz, c_szProgman) == 0))
  838. #endif
  839. {
  840. return TRUE;
  841. }
  842. hwnd = GetWindow(hwnd, GW_HWNDNEXT);
  843. }
  844. return FALSE;
  845. }
  846. //---------------------------------------------------------------------------
  847. BOOL Progman_Startup(PPMDDE ppmdde)
  848. {
  849. HSZ hszService, hszTopic;
  850. TCHAR szWindowsDir[MAX_PATH];
  851. int i = 0;
  852. Assert(ppmdde);
  853. // if the users shell is explorer, we dont bother
  854. // launching progman.exe, or doing any DDE bullshit
  855. if (IsShellExplorer())
  856. {
  857. g_fInitDDE = FALSE;
  858. g_fDoProgmanDde = FALSE;
  859. ppmdde->fStartedProgman = FALSE;
  860. return FALSE;
  861. }
  862. // Is Progman running?
  863. if (Progman_IsRunning())
  864. {
  865. // Yep.
  866. DebugMsg(DM_TRACE, TEXT("gc.p_s: Progman is already running."));
  867. ppmdde->fStartedProgman = FALSE;
  868. }
  869. else
  870. {
  871. // Nope - we'll try to startit.
  872. DebugMsg(DM_TRACE, TEXT("gc.p_s: Starting Progman..."));
  873. ppmdde->fStartedProgman = TRUE;
  874. GetWindowsDirectory(szWindowsDir, MAX_PATH);
  875. #ifdef UNICODE
  876. // on WINNT progman lives in %windir%\system32
  877. lstrcat(szWindowsDir, TEXT("\\System32\\"));
  878. #else
  879. // on win95 & memphis, progman lives in %windir%
  880. lstrcat(szWindowsDir, TEXT("\\"));
  881. #endif
  882. lstrcat(szWindowsDir, c_szProgmanExe);
  883. #ifdef UNICODE
  884. {
  885. STARTUPINFO si;
  886. PROCESS_INFORMATION pi;
  887. si.cb = SIZEOF(si);
  888. si.lpReserved = NULL;
  889. si.lpDesktop = NULL;
  890. si.lpTitle = NULL;
  891. si.dwX = (DWORD)CW_USEDEFAULT;
  892. si.dwY = (DWORD)CW_USEDEFAULT;
  893. si.dwXSize = (DWORD)CW_USEDEFAULT;
  894. si.dwYSize = (DWORD)CW_USEDEFAULT;
  895. si.dwXCountChars = 0;
  896. si.dwYCountChars = 0;
  897. si.dwFillAttribute = 0;
  898. si.dwFlags = STARTF_USESHOWWINDOW;
  899. si.wShowWindow = SW_HIDE;
  900. si.cbReserved2 = 0;
  901. si.lpReserved2 = 0;
  902. si.hStdInput = NULL;
  903. si.hStdOutput = NULL;
  904. si.hStdError = NULL;
  905. if (CreateProcess(szWindowsDir, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
  906. {
  907. CloseHandle(pi.hProcess);
  908. CloseHandle(pi.hThread);
  909. }
  910. }
  911. #else
  912. WinExec(szWindowsDir, SW_HIDE);
  913. #endif
  914. // Give progman a bit of time to startup but bail after 10s.
  915. while (!Progman_IsRunning() && (i < 10))
  916. {
  917. Sleep(1000);
  918. i++;
  919. }
  920. }
  921. // Just a bit longer.
  922. Sleep(1000);
  923. // Grab the focus back?
  924. if (g_hwndProgress)
  925. SetForegroundWindow(g_hwndProgress);
  926. // we are going to try to do DDE, so set g_fInitDDE = TRUE,
  927. // so that we know to call DdeUninitialize later
  928. g_fInitDDE = TRUE;
  929. ppmdde->dwInst = 0;
  930. DdeInitialize(&ppmdde->dwInst, DdeCallback, APPCLASS_STANDARD|APPCMD_CLIENTONLY, 0);
  931. hszService = DdeCreateStringHandle(ppmdde->dwInst, (LPTSTR)c_szProgman, CP_WINNEUTRAL);
  932. hszTopic = DdeCreateStringHandle(ppmdde->dwInst, (LPTSTR)c_szProgman, CP_WINNEUTRAL);
  933. g_fDoProgmanDde = Progman_DdeConnect(ppmdde, hszService, hszTopic);
  934. DdeFreeStringHandle(ppmdde->dwInst, hszService);
  935. DdeFreeStringHandle(ppmdde->dwInst, hszTopic);
  936. return g_fDoProgmanDde;
  937. }
  938. //---------------------------------------------------------------------------
  939. BOOL FindProgmanIni(LPTSTR pszPath)
  940. {
  941. OFSTRUCT os;
  942. #ifdef UNICODE
  943. LPTSTR lpszFilePart;
  944. #endif
  945. // NB Don't bother looking for the old windows directory, in the case of
  946. // an upgrade it will be the current windows directory.
  947. GetWindowsDirectory(pszPath, MAX_PATH);
  948. PathAppend(pszPath, c_szProgmanIni);
  949. if (PathFileExists(pszPath))
  950. {
  951. return TRUE;
  952. }
  953. #ifdef UNICODE
  954. else if (SearchPath(NULL, c_szProgmanIni, NULL, MAX_PATH, pszPath, &lpszFilePart) != 0)
  955. {
  956. return TRUE;
  957. }
  958. #else
  959. else if (OpenFile(c_szProgmanIni, &os, OF_EXIST) != -1)
  960. {
  961. lstrcpy(pszPath, os.szPathName);
  962. return TRUE;
  963. }
  964. #endif
  965. DebugMsg(DM_ERROR, TEXT("Can't find progman.ini"));
  966. return FALSE;
  967. }
  968. //---------------------------------------------------------------------------
  969. void UpdateTimeStampCallback(LPCTSTR lpszGroup)
  970. {
  971. WIN32_FIND_DATA fd;
  972. HANDLE hff;
  973. DebugMsg(DM_TRACE, TEXT("gc.utc: Updating timestamp for %s."), lpszGroup);
  974. hff = FindFirstFile(lpszGroup, &fd);
  975. if (hff != INVALID_HANDLE_VALUE)
  976. {
  977. Group_WriteLastModDateTime(lpszGroup,fd.ftLastWriteTime.dwLowDateTime);
  978. FindClose(hff);
  979. }
  980. }
  981. //---------------------------------------------------------------------------
  982. void Progman_Shutdown(PPMDDE ppmdde)
  983. {
  984. TCHAR szIniFile[MAX_PATH];
  985. // only shutdown progman if we actually started it and we
  986. // were doing DDE with it.
  987. if (ppmdde->fStartedProgman && g_fDoProgmanDde)
  988. {
  989. Log(TEXT("p_s: Shutting down progman..."));
  990. Log(TEXT("p_s: DdeClientTransaction."));
  991. DebugMsg(DM_TRACE, TEXT("gc.p_s: Shutting down progman."));
  992. DdeClientTransaction((LPBYTE)c_szExitProgman, SIZEOF(c_szExitProgman),
  993. ppmdde->hconv, HSZNULL, 0, XTYP_EXECUTE, DDETIMEOUT, NULL);
  994. }
  995. // if we initialzied DDE then uninit it now...
  996. if (g_fInitDDE)
  997. {
  998. Log(TEXT("p_s: DdeDisconnect."));
  999. DdeDisconnectList(ppmdde->hcl);
  1000. Log(TEXT("p_s: DdeUnitialize."));
  1001. DdeUninitialize(ppmdde->dwInst);
  1002. }
  1003. // We just went and modified all progman groups so update the time stamps.
  1004. FindProgmanIni(szIniFile);
  1005. Log(TEXT("p_s: Updating time stamps."));
  1006. Group_Enum(UpdateTimeStampCallback, FALSE, TRUE);
  1007. // Re-do the timestamp so that cab32 won't do another gprconv.
  1008. UpdateTimeStampCallback(szIniFile);
  1009. Log(TEXT("p_s: Done."));
  1010. }
  1011. //----------------------------------------------------------------------------
  1012. void BuildSectionGroups(LPCTSTR lpszIniFile, LPCTSTR lpszSection,
  1013. PPMDDE ppmdde, BOOL fUpdFolder, DWORD dwFlags)
  1014. {
  1015. int cb = 0;
  1016. LPTSTR pszLine;
  1017. TCHAR szSectName[CCHSZSHORT];
  1018. TCHAR szGroupName[2*MAX_PATH];
  1019. LPTSTR lpBuf;
  1020. // First allocate a buffer to read the section into
  1021. lpBuf = (LPTSTR) GlobalAlloc(GPTR, BUFSIZES); // Should never exceed 64K?
  1022. if (lpBuf)
  1023. {
  1024. // Now Read in the secint into our buffer.
  1025. if (PathFileExists(lpszIniFile))
  1026. cb = GetPrivateProfileSection(lpszSection, lpBuf, BUFSIZES/SIZEOF(TCHAR), lpszIniFile);
  1027. if (cb > 0)
  1028. {
  1029. Group_SetProgressDesc(IDS_CREATINGNEWSCS);
  1030. pszLine = lpBuf;
  1031. while (*pszLine)
  1032. {
  1033. // Make sure we did not fall off the deep end
  1034. if (cb < (int)(pszLine - lpBuf))
  1035. {
  1036. Assert(FALSE);
  1037. break;
  1038. }
  1039. // Now lets extract the fields off of the line
  1040. ParseField(pszLine, 0, szSectName, ARRAYSIZE(szSectName));
  1041. ParseField(pszLine, 1, szGroupName, ARRAYSIZE(szGroupName));
  1042. // Pass off to build that group and update progman.
  1043. BuildGroup(lpszIniFile, szSectName, szGroupName, ppmdde, fUpdFolder, dwFlags);
  1044. // Now setup process the next line in the section
  1045. pszLine += lstrlen(pszLine) + 1;
  1046. }
  1047. }
  1048. GlobalFree((HGLOBAL)lpBuf);
  1049. SHChangeNotify( 0, SHCNF_FLUSH, NULL, NULL); // Kick tray into updating for real
  1050. }
  1051. }
  1052. #ifdef WINNT
  1053. typedef UINT (__stdcall * PFNGETSYSTEMWINDOWSDIRECTORYW)(LPWSTR pwszBuffer, UINT cchSize);
  1054. //
  1055. // we need a wrapper for this since it only exists on NT5
  1056. //
  1057. UINT Wrap_GetSystemWindowsDirectoryW(LPWSTR pszBuffer, UINT cchBuff)
  1058. {
  1059. static PFNGETSYSTEMWINDOWSDIRECTORYW s_pfn = (PFNGETSYSTEMWINDOWSDIRECTORYW)-1;
  1060. if (s_pfn)
  1061. {
  1062. HINSTANCE hinst = GetModuleHandle(TEXT("KERNEL32.DLL"));
  1063. if (hinst)
  1064. s_pfn = (PFNGETSYSTEMWINDOWSDIRECTORYW)GetProcAddress(hinst, "GetSystemWindowsDirectoryW");
  1065. else
  1066. s_pfn = NULL;
  1067. }
  1068. if (s_pfn)
  1069. return s_pfn(pszBuffer, cchBuff);
  1070. SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
  1071. return 0;
  1072. }
  1073. #endif // WINNT
  1074. //
  1075. // We now look for setup.ini in 3 places: first in %userprofile%, next GetWindowsDirectory(),
  1076. // and finally in the GetWindowsSystemDirectory() (since hydra can change the return value for
  1077. // GetWindowsDirectory but apps still might be putting stuff there).
  1078. //
  1079. // The reason we look in the %USERPROFILE% directory is that Win2000's new high-security model
  1080. // does not give default users write permission to %windir%, so apps will not be able to even
  1081. // create a setup.ini in that location. This breaks the per-user install stubs (ie4uinit.exe),
  1082. // who are now going to create the setup.ini in %USERPROFILE%, where the user will always have
  1083. // write permission.
  1084. //
  1085. void FindSetupIni(LPTSTR szSetupIniPath, int cchSetupIniPath)
  1086. {
  1087. TCHAR szPath[MAX_PATH];
  1088. ExpandEnvironmentStrings(TEXT("%USERPROFILE%"), szPath, ARRAYSIZE(szPath));
  1089. PathAppend(szPath, c_szGrpConvInf);
  1090. if (PathFileExists(szPath))
  1091. {
  1092. lstrcpyn(szSetupIniPath, szPath, cchSetupIniPath);
  1093. return;
  1094. }
  1095. // next try GetWindowsDirectory()
  1096. GetWindowsDirectory(szPath, ARRAYSIZE(szPath));
  1097. PathAppend(szPath, c_szGrpConvInf);
  1098. if (PathFileExists(szPath))
  1099. {
  1100. lstrcpyn(szSetupIniPath, szPath, cchSetupIniPath);
  1101. return;
  1102. }
  1103. #ifdef WINNT
  1104. // finally if we are on NT try GetWindowsSystemDirectory()
  1105. if (Wrap_GetSystemWindowsDirectoryW(szPath, ARRAYSIZE(szPath)))
  1106. {
  1107. PathAppend(szPath, c_szGrpConvInf);
  1108. if (PathFileExists(szPath))
  1109. {
  1110. lstrcpyn(szSetupIniPath, szPath, cchSetupIniPath);
  1111. return;
  1112. }
  1113. }
  1114. #endif
  1115. // We faild to find it! For compat reasons, we just do what the old code
  1116. // does: GetWindowsDirectory() and PathAppend() and plow ahead...
  1117. GetWindowsDirectory(szPath, ARRAYSIZE(szPath));
  1118. PathAppend(szPath, c_szGrpConvInf);
  1119. return;
  1120. }
  1121. //---------------------------------------------------------------------------
  1122. // This parses the grpconv.inf file and creates the appropriate programs
  1123. // folders.
  1124. void BuildDefaultGroups(void)
  1125. {
  1126. TCHAR szPath[MAX_PATH];
  1127. PMDDE pmdde;
  1128. Log(TEXT("bdg: ..."));
  1129. // seek and you will find...
  1130. FindSetupIni(szPath, ARRAYSIZE(szPath));
  1131. // Now lets walk through the different items in this section
  1132. Group_CreateProgressDlg();
  1133. // Change the text in the progress dialog so people don't think we're
  1134. // doing the same thing twice.
  1135. // Group_SetProgressDesc(IDS_CREATINGNEWSCS);
  1136. // Crank up Progman.
  1137. Progman_Startup(&pmdde);
  1138. // Build the stuff.
  1139. BuildSectionGroups(szPath, c_szProgmanGroups, &pmdde, TRUE, BG_DELETE_EMPTY);
  1140. BuildSectionGroups(szPath, c_szProgmanOnly, &pmdde, FALSE, BG_DELETE_EMPTY);
  1141. // Custom sections.
  1142. BuildSectionGroups(szPath, c_szDesktopGroups, &pmdde, FALSE, BG_FORCE_DESKTOP);
  1143. BuildSectionGroups(szPath, c_szStartupGroups, &pmdde, FALSE, BG_FORCE_STARTUP);
  1144. BuildSectionGroups(szPath, c_szSendToGroups, &pmdde, FALSE, BG_FORCE_SENDTO);
  1145. BuildSectionGroups(szPath, c_szRecentDocsGroups, &pmdde, FALSE, BG_FORCE_RECENT);
  1146. // Close down progman.
  1147. Progman_Shutdown(&pmdde);
  1148. Group_DestroyProgressDlg();
  1149. // HACKHACK (reinerf) - we cant rename setup.ini -> setup.old because this causes problems
  1150. // the second time when it will fail because setup.old already exists (and we possibly dont
  1151. // have acls to overwrite it), and when it fails we orpan setup.ini as well (because the
  1152. // rename failed!!). This after this, all fututre attempts to create a setup.ini will fail,
  1153. // because one already exists, and we may not have acls to overwrite it. So, we just always
  1154. // delete setup.ini when we are done.
  1155. Win32DeleteFile(szPath);
  1156. Log(TEXT("bdg: Done."));
  1157. }