Leaked source code of windows server 2003
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.

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