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.

3866 lines
122 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. //---------------------------------------------------------------------------
  4. #include "grpconv.h"
  5. #include "util.h"
  6. #include "rcids.h"
  7. #include "group.h"
  8. #include "gcinst.h"
  9. #include <port32.h>
  10. #include <regstr.h>
  11. #define INITGUID
  12. #include <initguid.h>
  13. #pragma data_seg(DATASEG_READONLY)
  14. #include <coguid.h>
  15. #include <oleguid.h>
  16. #pragma data_seg()
  17. #ifdef DEBUG
  18. extern UINT GC_TRACE;
  19. #endif
  20. //---------------------------------------------------------------------------
  21. // Exported.
  22. const TCHAR c_szMapGroups[] = TEXT("MapGroups");
  23. #ifndef WINNT
  24. const TCHAR c_szDelGroups[] = TEXT("DelGroups");
  25. #endif
  26. //---------------------------------------------------------------------------
  27. // Global to this file only;
  28. static const TCHAR c_szGrpConv[] = TEXT("Grpconv");
  29. static const TCHAR c_szLastModDateTime[] = TEXT("LastModDateTime");
  30. static const TCHAR c_szRegistry[] = TEXT("Registry");
  31. static const TCHAR c_szDefaultUser[] = TEXT("DefaultUser");
  32. static const TCHAR c_szGrpConvData[] = TEXT("compat.csv");
  33. static const TCHAR c_szProgmanStartup[] = TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Program Manager\\Settings\\Startup");
  34. static const TCHAR c_szDotPif[] = TEXT(".pif");
  35. // New group stuff
  36. HDSA hdsaPMItems; // current group
  37. HDSA g_hdsaAppList;
  38. HKEY hkeyGroups = NULL;
  39. BOOL g_fDoingCommonGroups = FALSE;
  40. #pragma pack(1)
  41. typedef struct tagRECTS
  42. {
  43. short left;
  44. short top;
  45. short right;
  46. short bottom;
  47. } RECTS;
  48. typedef struct
  49. {
  50. LPTSTR lpszDesc;
  51. LPTSTR lpszCL;
  52. LPTSTR lpszWD;
  53. LPTSTR lpszIconPath;
  54. WORD wiIcon;
  55. WORD wHotKey;
  56. int nShowCmd;
  57. #ifdef WINNT
  58. BOOL bSepVdm;
  59. #endif
  60. } PMITEM, *PPMITEM, *LPPMITEM;
  61. // Old Progman stuff.
  62. #define GROUP_MAGIC 0x43434D50L // 'PMCC'
  63. #define GROUP_UNICODE 0x43554D50L // 'PMUC'
  64. /*
  65. * Win 3.1 .GRP file formats (ITEMDEF for items, GROUPDEF for groups)
  66. */
  67. typedef struct
  68. {
  69. POINTS pt;
  70. WORD iIcon;
  71. WORD cbHeader;
  72. WORD cbANDPlane;
  73. WORD cbXORPlane;
  74. WORD pHeader;
  75. WORD pANDPlane;
  76. WORD pXORPlane;
  77. WORD pName;
  78. WORD pCommand;
  79. WORD pIconPath;
  80. } ITEMDEF, *PITEMDEF, *LPITEMDEF;
  81. typedef struct
  82. {
  83. DWORD dwMagic;
  84. WORD wCheckSum;
  85. WORD cbGroup;
  86. WORD nCmdShow;
  87. RECTS rcNormal;
  88. POINTS ptMin;
  89. WORD pName;
  90. WORD cxIcon;
  91. WORD cyIcon;
  92. WORD wIconFormat;
  93. WORD wReserved;
  94. WORD cItems;
  95. } GROUPDEF, *PGROUPDEF, *LPGROUPDEF;
  96. typedef struct
  97. {
  98. WORD wID;
  99. WORD wItem;
  100. WORD cb;
  101. } PMTAG, *PPMTAG, *LPPMTAG;
  102. // Thank God the tag stuff never really caught on.
  103. #define TAG_MAGIC GROUP_MAGIC
  104. #define ID_MAINTAIN 0x8000
  105. #define ID_MAGIC 0x8000
  106. #define ID_WRITERVERSION 0x8001
  107. #define ID_APPLICATIONDIR 0x8101
  108. #define ID_HOTKEY 0x8102
  109. #define ID_MINIMIZE 0x8103
  110. #ifdef WINNT
  111. #define ID_NEWVDM 0x8104
  112. #endif
  113. #define ID_LASTTAG 0xFFFF
  114. /*
  115. * NT 3.1 Ansi .GRP File format structures
  116. */
  117. typedef struct tagGROUPDEF_A {
  118. DWORD dwMagic; /* magical bytes 'PMCC' */
  119. WORD wCheckSum; /* adjust this for zero sum of file */
  120. WORD cbGroup; /* length of group segment */
  121. RECT rcNormal; /* rectangle of normal window */
  122. POINT ptMin; /* point of icon */
  123. WORD nCmdShow; /* min, max, or normal state */
  124. WORD pName; /* name of group */
  125. /* these four change interpretation */
  126. WORD cxIcon; /* width of icons */
  127. WORD cyIcon; /* hieght of icons */
  128. WORD wIconFormat; /* planes and BPP in icons */
  129. WORD wReserved; /* This word is no longer used. */
  130. WORD cItems; /* number of items in group */
  131. WORD rgiItems[1]; /* array of ITEMDEF offsets */
  132. } NT_GROUPDEF_A, *PNT_GROUPDEF_A;
  133. typedef NT_GROUPDEF_A *LPNT_GROUPDEF_A;
  134. typedef struct tagITEMDEF_A {
  135. POINT pt; /* location of item icon in group */
  136. WORD idIcon; /* id of item icon */
  137. WORD wIconVer; /* icon version */
  138. WORD cbIconRes; /* size of icon resource */
  139. WORD indexIcon; /* index of item icon */
  140. WORD dummy2; /* - not used anymore */
  141. WORD pIconRes; /* offset of icon resource */
  142. WORD dummy3; /* - not used anymore */
  143. WORD pName; /* offset of name string */
  144. WORD pCommand; /* offset of command string */
  145. WORD pIconPath; /* offset of icon path */
  146. } NT_ITEMDEF_A, *PNT_ITEMDEF_A;
  147. typedef NT_ITEMDEF_A *LPNT_ITEMDEF_A;
  148. /*
  149. * NT 3.1a Unicode .GRP File format structures
  150. */
  151. typedef struct tagGROUPDEF {
  152. DWORD dwMagic; /* magical bytes 'PMCC' */
  153. DWORD cbGroup; /* length of group segment */
  154. RECT rcNormal; /* rectangle of normal window */
  155. POINT ptMin; /* point of icon */
  156. WORD wCheckSum; /* adjust this for zero sum of file */
  157. WORD nCmdShow; /* min, max, or normal state */
  158. DWORD pName; /* name of group */
  159. /* these four change interpretation */
  160. WORD cxIcon; /* width of icons */
  161. WORD cyIcon; /* hieght of icons */
  162. WORD wIconFormat; /* planes and BPP in icons */
  163. WORD wReserved; /* This word is no longer used. */
  164. WORD cItems; /* number of items in group */
  165. WORD Reserved1;
  166. DWORD Reserved2;
  167. DWORD rgiItems[1]; /* array of ITEMDEF offsets */
  168. } NT_GROUPDEF, *PNT_GROUPDEF;
  169. typedef NT_GROUPDEF *LPNT_GROUPDEF;
  170. typedef struct tagITEMDEF {
  171. POINT pt; /* location of item icon in group */
  172. WORD iIcon; /* id of item icon */
  173. WORD wIconVer; /* icon version */
  174. WORD cbIconRes; /* size of icon resource */
  175. WORD wIconIndex; /* index of the item icon (not the same as the id) */
  176. DWORD pIconRes; /* offset of icon resource */
  177. DWORD pName; /* offset of name string */
  178. DWORD pCommand; /* offset of command string */
  179. DWORD pIconPath; /* offset of icon path */
  180. } NT_ITEMDEF, *PNT_ITEMDEF;
  181. typedef NT_ITEMDEF *LPNT_ITEMDEF;
  182. typedef struct _tag
  183. {
  184. WORD wID; // tag identifier
  185. WORD dummy1; // need this for alignment!
  186. int wItem; // (unde the covers 32 bit point!)item the tag belongs to
  187. WORD cb; // size of record, including id and count
  188. WORD dummy2; // need this for alignment!
  189. BYTE rgb[1];
  190. } NT_PMTAG, * LPNT_PMTAG;
  191. /* the pointers in the above structures are short pointers relative to the
  192. * beginning of the segments. This macro converts the short pointer into
  193. * a long pointer including the proper segment/selector value. It assumes
  194. * that its argument is an lvalue somewhere in a group segment, for example,
  195. * PTR(lpgd->pName) returns a pointer to the group name, but k=lpgd->pName;
  196. * PTR(k) is obviously wrong as it will use either SS or DS for its segment,
  197. * depending on the storage class of k.
  198. */
  199. #define PTR(base, offset) (LPBYTE)((PBYTE)base + offset)
  200. /* PTR2 is used for those cases where a variable already contains an offset
  201. * (The "case that doesn't work", above)
  202. */
  203. #define PTR2(lp,offset) ((LPBYTE)MAKELONG(offset,HIWORD(lp)))
  204. /* this macro is used to retrieve the i-th item in the group segment. Note
  205. * that this pointer will NOT be NULL for an unused slot.
  206. */
  207. #define ITEM(lpgd,i) ((LPNT_ITEMDEF)PTR(lpgd, lpgd->rgiItems[i]))
  208. /* Keeping things starting on aligned boundaries allows faster access on
  209. * most platforms.
  210. */
  211. #define MyDwordAlign(size) (((size) + 3) & ~3)
  212. #pragma pack()
  213. #define CFree(a) if(a) Free(a)
  214. //---------------------------------------------------------------------------
  215. #define Stream_Write(ps, pv, cb) SUCCEEDED((ps)->lpVtbl->Write(ps, pv, cb, NULL))
  216. #define Stream_Close(ps) (void)(ps)->lpVtbl->Release(ps)
  217. #define VOF_BAD 0
  218. #define VOF_WIN31 1
  219. #define VOF_WINNT 2
  220. int ConvertToUnicodeGroup(LPNT_GROUPDEF_A lpGroupORI, LPHANDLE lphNewGroup);
  221. //---------------------------------------------------------------------------
  222. // Init the group stuff
  223. BOOL ItemList_Create(LPCTSTR lpszGroup)
  224. {
  225. if (!hdsaPMItems)
  226. hdsaPMItems = DSA_Create(SIZEOF(PMITEM), 16);
  227. if (hdsaPMItems)
  228. return TRUE;
  229. DebugMsg(DM_ERROR, TEXT("cg.gi: Unable to init."));
  230. return FALSE;
  231. }
  232. //---------------------------------------------------------------------------
  233. // Tidyup.
  234. void ItemList_Destroy(void)
  235. {
  236. int i;
  237. int cItems;
  238. LPPMITEM lppmitem;
  239. // Clean up the items.
  240. cItems = DSA_GetItemCount(hdsaPMItems);
  241. for(i=0; i < cItems; i++)
  242. {
  243. lppmitem = DSA_GetItemPtr(hdsaPMItems, 0);
  244. // Nuke the strings.
  245. CFree(lppmitem->lpszDesc);
  246. CFree(lppmitem->lpszCL);
  247. CFree(lppmitem->lpszWD);
  248. CFree(lppmitem->lpszIconPath);
  249. // Nuke the structure.
  250. DSA_DeleteItem(hdsaPMItems, 0);
  251. }
  252. DSA_Destroy(hdsaPMItems);
  253. hdsaPMItems = NULL;
  254. }
  255. //---------------------------------------------------------------------------
  256. // Returns TRUE if the file smells like an old PM group, the title of the
  257. // group is returned in lpszTitle which must be at least 32 chars big.
  258. // REVIEW - Is it worth checking the checksum?
  259. UINT Group_ValidOldFormat(LPCTSTR lpszOldGroup, LPTSTR lpszTitle)
  260. {
  261. #ifdef UNICODE
  262. HANDLE fh;
  263. DWORD dwBytesRead;
  264. #else
  265. HFILE fh;
  266. #endif
  267. UINT nCode;
  268. GROUPDEF grpdef;
  269. // Find and open the group file.
  270. #ifdef UNICODE
  271. fh = CreateFile(
  272. lpszOldGroup,
  273. GENERIC_READ,
  274. FILE_SHARE_READ,
  275. NULL,
  276. OPEN_EXISTING,
  277. 0,
  278. NULL
  279. );
  280. if (fh != INVALID_HANDLE_VALUE)
  281. #else
  282. fh = _lopen(lpszOldGroup, OF_READ | OF_SHARE_DENY_NONE);
  283. if (fh != HFILE_ERROR)
  284. #endif
  285. {
  286. // Get the definition.
  287. #ifdef UNICODE
  288. ReadFile(fh, &grpdef, SIZEOF(grpdef), &dwBytesRead, NULL);
  289. #else
  290. _lread(fh, &grpdef, SIZEOF(grpdef));
  291. #endif
  292. // Does it have the right magic bytes?.
  293. switch( grpdef.dwMagic )
  294. {
  295. case GROUP_UNICODE:
  296. {
  297. NT_GROUPDEF nt_grpdef;
  298. #ifdef UNICODE
  299. SetFilePointer(fh, 0, NULL, FILE_BEGIN);
  300. ReadFile(fh, &nt_grpdef, SIZEOF(nt_grpdef), &dwBytesRead, NULL);
  301. #else
  302. _llseek(fh, 0, 0); // Back to the start
  303. _lread(fh, &nt_grpdef, SIZEOF(nt_grpdef));
  304. #endif
  305. // Yep, Get it's size..
  306. // Is it at least as big as the header says it is?
  307. #ifdef UNICODE
  308. if ( nt_grpdef.cbGroup <= (DWORD)SetFilePointer(fh, 0L, NULL, FILE_END))
  309. #else
  310. if ( nt_grpdef.cbGroup <= (DWORD)_llseek(fh, 0L, 2))
  311. #endif
  312. {
  313. WCHAR wchGroupName[MAXGROUPNAMELEN+1];
  314. // Yep, probably valid.
  315. // Get its title.
  316. #ifdef UNICODE
  317. SetFilePointer(fh, nt_grpdef.pName, 0, FILE_BEGIN);
  318. ReadFile(fh, wchGroupName, SIZEOF(wchGroupName), &dwBytesRead, NULL);
  319. lstrcpy(lpszTitle, wchGroupName);
  320. #else
  321. _llseek(fh, nt_grpdef.pName, 0);
  322. _lread(fh,wchGroupName, SIZEOF(wchGroupName));
  323. WideCharToMultiByte (CP_ACP, 0, wchGroupName, -1,
  324. lpszTitle, MAXGROUPNAMELEN+1, NULL, NULL);
  325. #endif
  326. nCode = VOF_WINNT;
  327. }
  328. else
  329. {
  330. // No. Too small.
  331. DebugMsg(DM_TRACE, TEXT("gc.gvof: File has invalid size."));
  332. nCode = VOF_BAD;
  333. }
  334. }
  335. break;
  336. case GROUP_MAGIC:
  337. {
  338. CHAR chGroupName[MAXGROUPNAMELEN+1];
  339. // Yep, Get it's size..
  340. // Is it at least as big as the header says it is?
  341. #ifdef UNICODE
  342. if (grpdef.cbGroup <= (WORD) SetFilePointer(fh, 0L, NULL, FILE_END))
  343. #else
  344. if (grpdef.cbGroup <= (WORD) _llseek(fh, 0L, 2))
  345. #endif
  346. {
  347. // Check to make sure there is a name embedded in the
  348. // .grp file. If not, just use the filename
  349. if (grpdef.pName==0)
  350. {
  351. LPTSTR lpszFile, lpszExt, lpszDest = lpszTitle;
  352. lpszFile = PathFindFileName( lpszOldGroup );
  353. lpszExt = PathFindExtension( lpszOldGroup );
  354. for( ;
  355. lpszFile && lpszExt && (lpszFile != lpszExt);
  356. *lpszDest++ = *lpszFile++
  357. );
  358. *lpszDest = TEXT('\0');
  359. }
  360. else
  361. {
  362. // Yep, probably valid.
  363. // Get it's title.
  364. #ifdef UNICODE
  365. SetFilePointer(fh, grpdef.pName, NULL, FILE_BEGIN);
  366. ReadFile(fh, chGroupName, MAXGROUPNAMELEN+1, &dwBytesRead, NULL);
  367. MultiByteToWideChar(
  368. CP_ACP,
  369. MB_PRECOMPOSED,
  370. chGroupName,
  371. -1,
  372. lpszTitle,
  373. MAXGROUPNAMELEN+1
  374. ) ;
  375. #else
  376. _llseek(fh, grpdef.pName, 0);
  377. _lread(fh, lpszTitle, MAXGROUPNAMELEN+1);
  378. #endif
  379. }
  380. nCode = VOF_WIN31;
  381. }
  382. else
  383. {
  384. // No. Too small.
  385. DebugMsg(DM_TRACE, TEXT("gc.gvof: File has invalid size."));
  386. nCode = VOF_BAD;
  387. }
  388. break;
  389. }
  390. default:
  391. // No, the magic bytes are wrong.
  392. DebugMsg(DM_TRACE, TEXT("gc.gvof: File has invalid magic bytes."));
  393. nCode = VOF_BAD;
  394. break;
  395. }
  396. #ifdef UNICODE
  397. CloseHandle(fh);
  398. #else
  399. _lclose(fh);
  400. #endif
  401. }
  402. else
  403. {
  404. // No. Can't even read the file.
  405. DebugMsg(DM_TRACE, TEXT("gc.gvof: File is unreadble."));
  406. nCode = VOF_BAD;
  407. }
  408. return nCode;
  409. }
  410. BOOL _IsValidFileNameChar(TBYTE ch, UINT flags)
  411. {
  412. switch (ch) {
  413. case TEXT('\\'): // path separator
  414. return flags & PRICF_ALLOWSLASH;
  415. case TEXT(';'): // terminator
  416. case TEXT(','): // terminator
  417. case TEXT('|'): // pipe
  418. case TEXT('>'): // redir
  419. case TEXT('<'): // redir
  420. case TEXT('"'): // quote
  421. case TEXT('?'): // wc we only do wilds here because they're
  422. case TEXT('*'): // wc legal for qualifypath
  423. case TEXT(':'): // drive colon
  424. case TEXT('/'): // path sep
  425. return FALSE;
  426. }
  427. // Can not be a control char...
  428. return ch >= TEXT(' ');
  429. }
  430. void PathRemoveIllegalChars(LPTSTR pszPath, int iGroupName, UINT flags)
  431. {
  432. LPTSTR pszT = pszPath + iGroupName;
  433. // Map all of the strange characters out of the name for both LFn and not
  434. // machines
  435. while (*pszT)
  436. {
  437. if (!_IsValidFileNameChar(*pszT, flags))
  438. *pszT = TEXT('_'); // Don't Allow invalid chars in names
  439. pszT = CharNext(pszT);
  440. }
  441. }
  442. //---------------------------------------------------------------------------
  443. // We want certain groups to end up in a new location eg Games is now
  444. // Applications\Games.
  445. void MapGroupTitle(LPCTSTR lpszOld, LPTSTR lpszNew, UINT cchNew)
  446. {
  447. // Is there a mapping?
  448. if (!Reg_GetString(g_hkeyGrpConv, c_szMapGroups, lpszOld, lpszNew, cchNew*sizeof(TCHAR)))
  449. {
  450. // Nope, just use the given name.
  451. lstrcpyn(lpszNew, lpszOld, cchNew);
  452. }
  453. DebugMsg(DM_TRACE, TEXT("gc.mgr: From %s to %s"), lpszOld, lpszNew);
  454. }
  455. #undef PathRemoveExtension
  456. //---------------------------------------------------------------------------
  457. void PathRemoveExtension(LPTSTR pszPath)
  458. {
  459. LPTSTR pExt = PathFindExtension(pszPath);
  460. if (*pExt)
  461. {
  462. Assert(*pExt == TEXT('.'));
  463. *pExt = 0; // null out the "."
  464. }
  465. }
  466. //---------------------------------------------------------------------------
  467. // Given a path to an old group, create and return a path to where the new
  468. // group will be.
  469. BOOL Group_GenerateNewGroupPath(HWND hwnd, LPCTSTR lpszOldGrpTitle,
  470. LPTSTR lpszNewGrpPath, LPCTSTR pszOldGrpPath)
  471. {
  472. int iLen;
  473. TCHAR szGrpTitle[MAX_PATH];
  474. TCHAR szOldGrpTitle[32];
  475. // Get the location for all the special shell folders.
  476. if (g_fDoingCommonGroups)
  477. SHGetSpecialFolderPath(hwnd, lpszNewGrpPath, CSIDL_COMMON_PROGRAMS, TRUE);
  478. else
  479. SHGetSpecialFolderPath(hwnd, lpszNewGrpPath, CSIDL_PROGRAMS, TRUE);
  480. if (IsLFNDrive(lpszNewGrpPath))
  481. {
  482. // Fix it a bit.
  483. lstrcpyn(szOldGrpTitle, lpszOldGrpTitle, ARRAYSIZE(szOldGrpTitle));
  484. PathRemoveIllegalChars(szOldGrpTitle, 0, PRICF_NORMAL);
  485. // Munge the names so that things move to the new locations.
  486. MapGroupTitle(szOldGrpTitle, szGrpTitle, ARRAYSIZE(szGrpTitle));
  487. // Stick on the new group name.
  488. PathAddBackslash(lpszNewGrpPath);
  489. iLen = lstrlen(lpszNewGrpPath);
  490. // NB Don't use PathAppend() - very bad if there's a colons in the title.
  491. lstrcpyn(lpszNewGrpPath+iLen, szGrpTitle, MAX_PATH-iLen);
  492. PathRemoveIllegalChars(lpszNewGrpPath, iLen, PRICF_ALLOWSLASH);
  493. }
  494. else
  495. {
  496. // Just use the old group file name - this will make sure the group
  497. // names remain unique.
  498. PathAppend(lpszNewGrpPath, PathFindFileName(pszOldGrpPath));
  499. PathRemoveExtension(lpszNewGrpPath);
  500. }
  501. if (!PathFileExists(lpszNewGrpPath))
  502. {
  503. // Folder doesn't exist.
  504. // return Win32CreateDirectory(lpszNewGrpPath, NULL);
  505. return (SHCreateDirectory(hwnd, lpszNewGrpPath) == 0);
  506. }
  507. // Folder already exists.
  508. return TRUE;
  509. }
  510. //---------------------------------------------------------------------------
  511. // Returns true if the offsets given in the item def are valid-ish.
  512. BOOL CheckItemDef(LPITEMDEF lpitemdef, WORD cbGroup)
  513. {
  514. if (lpitemdef->pHeader < cbGroup && lpitemdef->pANDPlane < cbGroup &&
  515. lpitemdef->pXORPlane < cbGroup && lpitemdef->pName < cbGroup &&
  516. lpitemdef->pCommand < cbGroup && lpitemdef->pIconPath < cbGroup &&
  517. lpitemdef->pHeader && lpitemdef->pXORPlane && lpitemdef->pCommand)
  518. return TRUE;
  519. else
  520. {
  521. return FALSE;
  522. }
  523. }
  524. //---------------------------------------------------------------------------
  525. // Returns true if the offsets given in the item def are valid-ish.
  526. BOOL CheckItemDefNT(LPNT_ITEMDEF lpitemdef, DWORD cbGroup)
  527. {
  528. if (lpitemdef->pName < cbGroup &&
  529. lpitemdef->pCommand < cbGroup &&
  530. lpitemdef->pIconPath < cbGroup &&
  531. lpitemdef->pCommand)
  532. return TRUE;
  533. else
  534. {
  535. return FALSE;
  536. }
  537. }
  538. //---------------------------------------------------------------------------
  539. // Read the tags info from the given file handle from the given offset.
  540. #ifdef UNICODE
  541. void HandleTags(HANDLE fh, WORD oTags)
  542. #else
  543. void HandleTags(int fh, WORD oTags)
  544. #endif
  545. {
  546. LONG cbGroupReal;
  547. PMTAG pmtag;
  548. BOOL fTags = TRUE;
  549. TCHAR szText[MAX_PATH];
  550. BOOL fFirstTag = FALSE;
  551. LPPMITEM lppmitem;
  552. WORD wHotKey;
  553. #ifdef UNICODE
  554. DWORD dwBytesRead;
  555. #endif
  556. DebugMsg(DM_TRACE, TEXT("cg.ht: Reading tags."));
  557. #ifdef UNICODE
  558. cbGroupReal = SetFilePointer(fh, 0, NULL, FILE_END);
  559. #else
  560. cbGroupReal = (WORD) _llseek(fh, 0L, 2);
  561. #endif
  562. if (cbGroupReal <= (LONG) oTags)
  563. {
  564. // No tags in this file.
  565. return;
  566. }
  567. // Get to the tags section.
  568. #ifdef UNICODE
  569. SetFilePointer(fh, oTags, NULL, FILE_BEGIN);
  570. #else
  571. _llseek(fh, oTags, 0);
  572. #endif
  573. while (fTags)
  574. {
  575. #ifdef UNICODE
  576. if (!ReadFile(fh, &pmtag, SIZEOF(pmtag), &dwBytesRead, NULL) || dwBytesRead == 0) {
  577. fTags = FALSE;
  578. break;
  579. }
  580. #else
  581. fTags = _lread(fh, &pmtag, SIZEOF(pmtag));
  582. #endif
  583. switch (pmtag.wID)
  584. {
  585. case ID_MAGIC:
  586. {
  587. // DebugMsg(DM_TRACE, "gc.ht: First tag found.");
  588. fFirstTag = TRUE;
  589. #ifdef UNICODE
  590. SetFilePointer(fh, pmtag.cb - SIZEOF(PMTAG), NULL, FILE_CURRENT);
  591. #else
  592. _llseek(fh, pmtag.cb - SIZEOF(PMTAG), 1);
  593. #endif
  594. break;
  595. }
  596. case ID_LASTTAG:
  597. {
  598. // DebugMsg(DM_TRACE, "gc.ht: Last tag found.");
  599. fTags = FALSE;
  600. break;
  601. }
  602. case ID_APPLICATIONDIR:
  603. {
  604. // DebugMsg(DM_TRACE, "gc.ht: App dir %s found for %d.", (LPSTR) szText, pmtag.wItem);
  605. fgets(szText, ARRAYSIZE(szText), fh);
  606. lppmitem = DSA_GetItemPtr(hdsaPMItems, pmtag.wItem);
  607. if (lppmitem)
  608. {
  609. Str_SetPtr(&lppmitem->lpszCL, szText);
  610. }
  611. #ifdef DEBUG
  612. else
  613. {
  614. DebugMsg(DM_ERROR, TEXT("gc.ht: Item is invalid."));
  615. }
  616. #endif
  617. break;
  618. }
  619. case ID_HOTKEY:
  620. {
  621. // DebugMsg(DM_TRACE, "gc.ht: Hotkey found for %d.", pmtag.wItem);
  622. #ifdef UNICODE
  623. ReadFile(fh, &wHotKey, SIZEOF(wHotKey), &dwBytesRead, NULL);
  624. #else
  625. _lread(fh, &wHotKey, SIZEOF(wHotKey));
  626. #endif
  627. lppmitem = DSA_GetItemPtr(hdsaPMItems, pmtag.wItem);
  628. if (lppmitem)
  629. {
  630. lppmitem->wHotKey = wHotKey;
  631. }
  632. #ifdef DEBUG
  633. else
  634. {
  635. DebugMsg(DM_ERROR, TEXT("gc.ht: Item is invalid."));
  636. }
  637. #endif
  638. break;
  639. }
  640. case ID_MINIMIZE:
  641. {
  642. // DebugMsg(DM_TRACE, "gc.ht: Minimise flag found for %d.", pmtag.wItem);
  643. lppmitem = DSA_GetItemPtr(hdsaPMItems, pmtag.wItem);
  644. if (lppmitem)
  645. {
  646. lppmitem->nShowCmd = SW_SHOWMINNOACTIVE;
  647. }
  648. #ifdef DEBUG
  649. else
  650. {
  651. DebugMsg(DM_ERROR, TEXT("gc.ht: Item is invalid."));
  652. }
  653. #endif
  654. // Skip to the next tag.
  655. #ifdef UNICODE
  656. SetFilePointer(fh, pmtag.cb - SIZEOF(PMTAG), NULL, FILE_CURRENT);
  657. #else
  658. _llseek(fh, pmtag.cb - SIZEOF(PMTAG), 1);
  659. #endif
  660. break;
  661. }
  662. #ifdef WINNT
  663. case ID_NEWVDM:
  664. {
  665. // DebugMsg(DM_TRACE, "gc.ht: Separate VDM flag found for %d.", pmtag.wItem );
  666. lppmitem = DSA_GetItemPtr(hdsaPMItems, pmtag.wItem);
  667. if (lppmitem)
  668. {
  669. lppmitem->bSepVdm = TRUE;
  670. }
  671. #ifdef DEBUG
  672. else
  673. {
  674. DebugMsg(DM_ERROR, TEXT("gc.ht: Item is invalid."));
  675. }
  676. #endif
  677. // Skip to the next tag.
  678. #ifdef UNICODE
  679. SetFilePointer(fh, pmtag.cb - SIZEOF(PMTAG), NULL, FILE_CURRENT);
  680. #else
  681. _llseek(fh, pmtag.cb - SIZEOF(PMTAG), 1);
  682. #endif
  683. break;
  684. }
  685. #endif
  686. default:
  687. {
  688. // We've found something we don't understand but we haven't
  689. // found the first tag yet - probably a bust file.
  690. if (!fFirstTag)
  691. {
  692. DebugMsg(DM_TRACE, TEXT("gc.ht: No initial tag found - tags section is corrupt."));
  693. fTags = FALSE;
  694. }
  695. else
  696. {
  697. // Some unknown tag.
  698. if (pmtag.cb < SIZEOF(PMTAG))
  699. {
  700. // Can't continue!
  701. DebugMsg(DM_TRACE, TEXT("gc.ht: Tag has invalid size - ignoring remaining tags."));
  702. fTags = FALSE;
  703. }
  704. else
  705. {
  706. // Just ignore its data and continue.
  707. #ifdef UNICODE
  708. SetFilePointer(fh, pmtag.cb - SIZEOF(PMTAG), NULL, FILE_CURRENT);
  709. #else
  710. _llseek(fh, pmtag.cb - SIZEOF(PMTAG), 1);
  711. #endif
  712. }
  713. }
  714. break;
  715. }
  716. }
  717. }
  718. }
  719. //---------------------------------------------------------------------------
  720. // Read the tags info from the given file handle from the given offset.
  721. #ifdef UNICODE
  722. void HandleTagsNT(HANDLE fh, DWORD oTags)
  723. #else
  724. void HandleTagsNT(int fh, DWORD oTags)
  725. #endif
  726. {
  727. DWORD cbGroupReal;
  728. DWORD dwPosition;
  729. NT_PMTAG pmtag;
  730. BOOL fTags = TRUE;
  731. WCHAR wszTemp[MAX_PATH];
  732. TCHAR szText[MAX_PATH];
  733. BOOL fFirstTag = FALSE;
  734. LPPMITEM lppmitem;
  735. WORD wHotKey;
  736. #ifdef UNICODE
  737. DWORD dwBytesRead;
  738. #endif
  739. DebugMsg(DM_TRACE, TEXT("cg.ht: Reading tags."));
  740. #ifdef UNICODE
  741. cbGroupReal = SetFilePointer(fh, 0, NULL, FILE_END);
  742. #else
  743. cbGroupReal = _llseek(fh, 0L, 2);
  744. #endif
  745. if (cbGroupReal <= oTags)
  746. {
  747. // No tags in this file.
  748. return;
  749. }
  750. // Get to the tags section.
  751. dwPosition = oTags;
  752. while (fTags)
  753. {
  754. #ifdef UNICODE
  755. SetFilePointer(fh, dwPosition, NULL, FILE_BEGIN);
  756. if (!ReadFile(fh, &pmtag, SIZEOF(pmtag), &dwBytesRead, NULL) || dwBytesRead == 0) {
  757. fTags = FALSE;
  758. break;
  759. }
  760. #else
  761. _llseek(fh,dwPosition,0);
  762. fTags = _lread(fh, &pmtag, SIZEOF(pmtag));
  763. #endif
  764. switch (pmtag.wID)
  765. {
  766. case ID_MAGIC:
  767. {
  768. // DebugMsg(DM_TRACE, "gc.ht: First tag found.");
  769. fFirstTag = TRUE;
  770. dwPosition += pmtag.cb;
  771. break;
  772. }
  773. case ID_LASTTAG:
  774. {
  775. // DebugMsg(DM_TRACE, "gc.ht: Last tag found.");
  776. fTags = FALSE;
  777. break;
  778. }
  779. case ID_APPLICATIONDIR:
  780. {
  781. #ifdef UNICODE
  782. SetFilePointer(fh, dwPosition+FIELD_OFFSET(NT_PMTAG,rgb[0]), NULL, FILE_BEGIN);
  783. ReadFile(fh, wszTemp, SIZEOF(wszTemp), &dwBytesRead, NULL);
  784. lstrcpy(szText, wszTemp);
  785. #else
  786. _llseek(fh,dwPosition+FIELD_OFFSET(NT_PMTAG,rgb[0]),0);
  787. _lread(fh,wszTemp,SIZEOF(wszTemp));
  788. WideCharToMultiByte (CP_ACP, 0, wszTemp, -1,
  789. szText, ARRAYSIZE(szText), NULL, NULL);
  790. #endif
  791. // DebugMsg(DM_TRACE, "gc.ht: App dir %s found for %d.", (LPSTR) szText, pmtag.wItem);
  792. lppmitem = DSA_GetItemPtr(hdsaPMItems, pmtag.wItem);
  793. if (lppmitem)
  794. {
  795. Str_SetPtr(&lppmitem->lpszCL, szText);
  796. }
  797. #ifdef DEBUG
  798. else
  799. {
  800. DebugMsg(DM_ERROR, TEXT("gc.ht: Item is invalid."));
  801. }
  802. #endif
  803. dwPosition += pmtag.cb;
  804. break;
  805. }
  806. case ID_HOTKEY:
  807. {
  808. // DebugMsg(DM_TRACE, "gc.ht: Hotkey found for %d.", pmtag.wItem);
  809. #ifdef UNICODE
  810. ReadFile(fh, &wHotKey, SIZEOF(wHotKey), &dwBytesRead, NULL);
  811. #else
  812. _lread(fh, &wHotKey, SIZEOF(wHotKey));
  813. #endif
  814. lppmitem = DSA_GetItemPtr(hdsaPMItems, pmtag.wItem);
  815. if (lppmitem)
  816. {
  817. lppmitem->wHotKey = wHotKey;
  818. }
  819. #ifdef DEBUG
  820. else
  821. {
  822. DebugMsg(DM_ERROR, TEXT("gc.ht: Item is invalid."));
  823. }
  824. #endif
  825. dwPosition += pmtag.cb;
  826. break;
  827. }
  828. case ID_MINIMIZE:
  829. {
  830. // DebugMsg(DM_TRACE, "gc.ht: Minimise flag found for %d.", pmtag.wItem);
  831. lppmitem = DSA_GetItemPtr(hdsaPMItems, pmtag.wItem);
  832. if (lppmitem)
  833. {
  834. lppmitem->nShowCmd = SW_SHOWMINNOACTIVE;
  835. }
  836. #ifdef DEBUG
  837. else
  838. {
  839. DebugMsg(DM_ERROR, TEXT("gc.ht: Item is invalid."));
  840. }
  841. #endif
  842. // Skip to the next tag.
  843. dwPosition += pmtag.cb;
  844. break;
  845. }
  846. #ifdef WINNT
  847. case ID_NEWVDM:
  848. {
  849. // DebugMsg(DM_TRACE, "gc.ht: Separate VDM flag found for %d.", pmtag.wItem );
  850. lppmitem = DSA_GetItemPtr(hdsaPMItems, pmtag.wItem);
  851. if (lppmitem)
  852. {
  853. lppmitem->bSepVdm = TRUE;
  854. }
  855. #ifdef DEBUG
  856. else
  857. {
  858. DebugMsg(DM_ERROR, TEXT("gc.ht: Item is invalid."));
  859. }
  860. #endif
  861. // Skip to the next tag.
  862. dwPosition += pmtag.cb;
  863. break;
  864. }
  865. #endif
  866. default:
  867. {
  868. // We've found something we don't understand but we haven't
  869. // found the first tag yet - probably a bust file.
  870. if (!fFirstTag)
  871. {
  872. DebugMsg(DM_TRACE, TEXT("gc.ht: No initial tag found - tags section is corrupt."));
  873. fTags = FALSE;
  874. }
  875. else
  876. {
  877. // Some unknown tag.
  878. if (pmtag.cb < SIZEOF(PMTAG))
  879. {
  880. // Can't continue!
  881. DebugMsg(DM_TRACE, TEXT("gc.ht: Tag has invalid size - ignoring remaining tags."));
  882. fTags = FALSE;
  883. }
  884. else
  885. {
  886. // Just ignore its data and continue.
  887. dwPosition += pmtag.cb;
  888. }
  889. }
  890. break;
  891. }
  892. }
  893. }
  894. }
  895. //---------------------------------------------------------------------------
  896. void DeleteBustedItems(void)
  897. {
  898. int i, cItems;
  899. LPPMITEM ppmitem;
  900. cItems = DSA_GetItemCount(hdsaPMItems);
  901. for (i=0; i<cItems; i++)
  902. {
  903. ppmitem = DSA_GetItemPtr(hdsaPMItems, i);
  904. // Is the item broken?
  905. if (!ppmitem->lpszDesc || !(*ppmitem->lpszDesc))
  906. {
  907. // Yep, delete it.
  908. DSA_DeleteItem(hdsaPMItems, i);
  909. cItems--;
  910. i--;
  911. }
  912. }
  913. }
  914. //---------------------------------------------------------------------------
  915. void ShortenDescriptions(void)
  916. {
  917. int i, cItems;
  918. LPPMITEM ppmitem;
  919. cItems = DSA_GetItemCount(hdsaPMItems);
  920. for (i=0; i<cItems; i++)
  921. {
  922. ppmitem = DSA_GetItemPtr(hdsaPMItems, i);
  923. // Shorten the descriptions
  924. lstrcpyn(ppmitem->lpszDesc, ppmitem->lpszDesc, 9);
  925. }
  926. }
  927. //---------------------------------------------------------------------------
  928. // Kinda like PathFindFileName() but handles things like c:\foo\ differently
  929. // to match progmans code.
  930. LPTSTR WINAPI _PathFindFileName(LPCTSTR pPath)
  931. {
  932. LPCTSTR pT;
  933. for (pT = pPath; *pPath; pPath = CharNext(pPath)) {
  934. if ((pPath[0] == TEXT('\\') || pPath[0] == TEXT(':')) && (pPath[1] != TEXT('\\')))
  935. pT = pPath + 1;
  936. }
  937. return (LPTSTR)pT; // const -> non const
  938. }
  939. //---------------------------------------------------------------------------
  940. // Take a 3.1 format WD and exe and convert them to the new style.
  941. // NB Old style was WD+exename and exepath - new style is exepath+exename and
  942. // WD.
  943. void MungePaths(void)
  944. {
  945. LPTSTR lpszFileName; // Ptr to filename part (plus params).
  946. LPTSTR lpszParams; // Ptr to first char of params.
  947. TCHAR szCL[MAX_PATH];
  948. TCHAR szWD[MAX_PATH];
  949. int i, cItems;
  950. LPPMITEM lppmitem;
  951. cItems = DSA_GetItemCount(hdsaPMItems);
  952. for (i=0; i<cItems; i++)
  953. {
  954. szCL[0] = TEXT('\0');
  955. szWD[0] = TEXT('\0');
  956. lppmitem = DSA_GetItemPtr(hdsaPMItems, i);
  957. // Get the current command line.
  958. Str_GetPtr(lppmitem->lpszCL, szCL, ARRAYSIZE(szCL));
  959. // Get the current working dir.
  960. Str_GetPtr(lppmitem->lpszWD, szWD, ARRAYSIZE(szWD));
  961. #ifdef OLDWAY
  962. // Find the filename part...
  963. // Params will confuse PFFN.
  964. lpszParams = PathGetArgs(szWD);
  965. if (*lpszParams)
  966. {
  967. // Chop them off.
  968. // NB Previous char is a space by definition.
  969. *(lpszParams-1) = TEXT('\0');
  970. lpszFileName = _PathFindFileName(szWD);
  971. // Put them back
  972. *(lpszParams-1) = TEXT(' ');
  973. }
  974. else
  975. {
  976. // No params.
  977. lpszFileName = PathFindFileName(szWD);
  978. }
  979. // Copy this onto the exe path.
  980. lstrcat((LPTSTR) szCL, lpszFileName);
  981. // Remove it from the end of the WD.
  982. *lpszFileName = TEXT('\0');
  983. // For anything but things like c:\ remove the last slash.
  984. if (!PathIsRoot(szWD))
  985. {
  986. *(lpszFileName-1) = TEXT('\0');
  987. }
  988. #else
  989. lpszFileName = szWD;
  990. if (*lpszFileName == TEXT('"'))
  991. {
  992. while (lpszFileName)
  993. {
  994. lpszFileName = StrChr(lpszFileName+1,TEXT('"'));
  995. if (!lpszFileName)
  996. {
  997. //
  998. // The directory is not in quotes and since the command
  999. // path starts with a quote, there is no working directory.
  1000. //
  1001. lpszFileName = szWD;
  1002. break;
  1003. }
  1004. if (*(lpszFileName+1) == TEXT('\\'))
  1005. {
  1006. //
  1007. // The working directory is in quotes.
  1008. //
  1009. lpszFileName++;
  1010. break;
  1011. }
  1012. }
  1013. }
  1014. else
  1015. {
  1016. //
  1017. // if there's a working directory, it is not in quotes
  1018. // Copy up until the last \ preceding any quote, space, or the end
  1019. //
  1020. LPTSTR lpEnd = lpszFileName;
  1021. while (*lpszFileName && *lpszFileName != TEXT('"') && *lpszFileName != TEXT(' '))
  1022. {
  1023. if ((*lpszFileName == TEXT('\\') || *lpszFileName == TEXT(':')) && *(lpszFileName+1) != TEXT('\\'))
  1024. lpEnd = lpszFileName;
  1025. lpszFileName = CharNext(lpszFileName);
  1026. }
  1027. lpszFileName = lpEnd;
  1028. }
  1029. //
  1030. // If the split is at the beginning,
  1031. // then there is no working dir
  1032. //
  1033. if (lpszFileName == szWD)
  1034. {
  1035. lstrcat(szCL, szWD);
  1036. szWD[0] = TEXT('\0');
  1037. }
  1038. else
  1039. {
  1040. lstrcat(szCL, lpszFileName+1);
  1041. *(lpszFileName+1) = TEXT('\0'); // Split it.
  1042. //
  1043. // Remove quotes from the working dir NOW.
  1044. //
  1045. if (szWD[0] == TEXT('"')) {
  1046. LPTSTR lpTemp;
  1047. for (lpTemp = szWD+1; *lpTemp && *lpTemp != TEXT('"'); lpTemp++)
  1048. *(lpTemp-1) = *lpTemp;
  1049. if (*lpTemp == TEXT('"')) {
  1050. *(lpTemp-1) = TEXT('\0');
  1051. }
  1052. }
  1053. // For anything but things like c:\ remove the last slash.
  1054. if (!PathIsRoot(szWD))
  1055. {
  1056. *lpszFileName = TEXT('\0');
  1057. }
  1058. }
  1059. #endif
  1060. // Replace the data.
  1061. Str_SetPtr(&lppmitem->lpszCL, szCL);
  1062. Str_SetPtr(&lppmitem->lpszWD, szWD);
  1063. // DebugMsg(DM_TRACE, "gc.mp: Exe %s, WD %s", (LPSTR)szCL, (LPSTR)szWD);
  1064. }
  1065. }
  1066. //---------------------------------------------------------------------------
  1067. // Set all the fields of the given pmitem to clear;
  1068. void PMItem_Clear(LPPMITEM lppmitem)
  1069. {
  1070. lppmitem->lpszDesc = NULL;
  1071. lppmitem->lpszCL = NULL;
  1072. lppmitem->lpszWD = NULL;
  1073. lppmitem->lpszIconPath = NULL;
  1074. lppmitem->wiIcon = 0;
  1075. lppmitem->wHotKey = 0;
  1076. lppmitem->nShowCmd = SW_SHOWNORMAL;
  1077. #ifdef WINNT
  1078. lppmitem->bSepVdm = FALSE;
  1079. #endif
  1080. }
  1081. //---------------------------------------------------------------------------
  1082. // Read the item data from the file and add it to the list.
  1083. // Returns TRUE if everything went perfectly.
  1084. #ifdef UNICODE
  1085. BOOL GetAllItemData(HANDLE fh, WORD cItems, WORD cbGroup, LPTSTR lpszOldGrpTitle, LPTSTR lpszNewGrpPath)
  1086. #else
  1087. BOOL GetAllItemData(HFILE fh, WORD cItems, WORD cbGroup, LPTSTR lpszOldGrpTitle, LPTSTR lpszNewGrpPath)
  1088. #endif
  1089. {
  1090. UINT cbItemArray;
  1091. WORD *rgItems;
  1092. UINT i, iItem;
  1093. TCHAR szDesc[CCHSZNORMAL];
  1094. TCHAR szCL[CCHSZNORMAL];
  1095. TCHAR szIconPath[CCHSZNORMAL];
  1096. ITEMDEF itemdef;
  1097. BOOL fOK = TRUE;
  1098. UINT cbRead;
  1099. PMITEM pmitem;
  1100. #ifdef UNICODE
  1101. DWORD dwBytesRead;
  1102. #endif
  1103. // Read in the old item table...
  1104. iItem = 0;
  1105. cbItemArray = cItems * SIZEOF(*rgItems);
  1106. rgItems = (WORD *)LocalAlloc(LPTR, cbItemArray);
  1107. if (!rgItems)
  1108. {
  1109. DebugMsg(DM_ERROR, TEXT("gc.gcnfo: Out of memory."));
  1110. return FALSE;
  1111. }
  1112. #ifdef UNICODE
  1113. SetFilePointer(fh, SIZEOF(GROUPDEF), NULL, FILE_BEGIN);
  1114. ReadFile(fh, rgItems, cbItemArray, &dwBytesRead, NULL);
  1115. #else
  1116. _llseek(fh, SIZEOF(GROUPDEF), 0);
  1117. _lread(fh, rgItems, cbItemArray);
  1118. #endif
  1119. // Show progress in two stages, first reading then writing.
  1120. Group_SetProgressNameAndRange(lpszNewGrpPath, (cItems*2)-1);
  1121. // Read in the items.
  1122. // NB Don't just skip busted items since the tag data contains
  1123. // indices to items and that includes busted ones. Just use
  1124. // an empty description to indicate that the link is invalid.
  1125. for (i=0; i<cItems; i++)
  1126. {
  1127. Group_SetProgress(i);
  1128. szDesc[0] = TEXT('\0');
  1129. szCL[0] = TEXT('\0');
  1130. szIconPath[0] = TEXT('\0');
  1131. itemdef.iIcon = 0;
  1132. if (rgItems[i] == 0)
  1133. {
  1134. DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file has empty item definition - skipping."));
  1135. goto AddItem;
  1136. }
  1137. if (rgItems[i] > cbGroup)
  1138. {
  1139. DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (item entry in invalid part of file) - skipping item."));
  1140. fOK = FALSE;
  1141. goto AddItem;
  1142. }
  1143. #ifdef UNICODE
  1144. SetFilePointer(fh, rgItems[i], NULL, FILE_BEGIN);
  1145. ReadFile(fh, &itemdef, SIZEOF(itemdef), &cbRead, NULL);
  1146. #else
  1147. _llseek(fh, rgItems[i], 0);
  1148. cbRead = _lread(fh, &itemdef, SIZEOF(itemdef));
  1149. #endif
  1150. if (cbRead != SIZEOF(itemdef))
  1151. {
  1152. DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (invalid definition) - skipping item %d."), i);
  1153. fOK = FALSE;
  1154. goto AddItem;
  1155. }
  1156. if (!CheckItemDef(&itemdef, cbGroup))
  1157. {
  1158. DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (invalid item field) - skipping item %d."), i);
  1159. fOK = FALSE;
  1160. goto AddItem;
  1161. }
  1162. #ifdef UNICODE
  1163. SetFilePointer(fh, itemdef.pName, NULL, FILE_BEGIN);
  1164. #else
  1165. _llseek(fh, itemdef.pName, 0);
  1166. #endif
  1167. fgets(szDesc, SIZEOF(szDesc), fh);
  1168. if (!*szDesc)
  1169. {
  1170. DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (empty name) - skipping item %d."), i);
  1171. fOK = FALSE;
  1172. goto AddItem;
  1173. }
  1174. #ifdef UNICODE
  1175. SetFilePointer(fh, itemdef.pCommand, NULL, FILE_BEGIN);
  1176. #else
  1177. _llseek(fh, itemdef.pCommand, 0);
  1178. #endif
  1179. fgets(szCL, SIZEOF(szCL), fh);
  1180. // We hit this case with links to c:\ (rare, very rare).
  1181. #if 0
  1182. if (!*szCL)
  1183. {
  1184. DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (empty command line) - skipping item %d."), i);
  1185. // We use a null description to signal a problem with this item.
  1186. szDesc[0] = TEXT('\0');
  1187. fOK = FALSE;
  1188. goto AddItem;
  1189. }
  1190. #endif
  1191. if (itemdef.pIconPath!=0xFFFF)
  1192. {
  1193. #ifdef UNICODE
  1194. SetFilePointer(fh, itemdef.pIconPath, NULL, FILE_BEGIN);
  1195. #else
  1196. _llseek(fh, itemdef.pIconPath, 0);
  1197. #endif
  1198. fgets(szIconPath, SIZEOF(szIconPath), fh);
  1199. }
  1200. else
  1201. {
  1202. szIconPath[ 0 ] = TEXT('\0');
  1203. }
  1204. if (!*szIconPath)
  1205. {
  1206. // NB Do nothing. Empty icon paths are legal - associated apps where the associated
  1207. // app is missing will have an empty icon path.
  1208. }
  1209. // NB Forget about the icon data.
  1210. // DebugMsg(DM_TRACE, "gc.gcnfo: Found item %s.", (LPSTR) szDesc);
  1211. // Store away the data....
  1212. // NB We load the old commands line into the working dir field because
  1213. // only the leaf is the command, the rest is the WD. Once we've been
  1214. // through the tags section we can sort out the mess.
  1215. AddItem:
  1216. PMItem_Clear(&pmitem);
  1217. #ifdef DEBUG
  1218. DebugMsg(GC_TRACE, TEXT("gc.gaid: Desc %s"), (LPTSTR) szDesc);
  1219. DebugMsg(GC_TRACE, TEXT(" WD: %s"), (LPTSTR) szCL);
  1220. DebugMsg(GC_TRACE, TEXT(" IP: %s(%d)"), (LPTSTR) szIconPath, itemdef.iIcon);
  1221. #endif
  1222. // Don't store anything for items with invalid descriptions.
  1223. if (*szDesc)
  1224. {
  1225. // Remove illegal chars.
  1226. PathRemoveIllegalChars(szDesc, 0, PRICF_NORMAL);
  1227. Str_SetPtr(&pmitem.lpszDesc, szDesc);
  1228. Str_SetPtr(&pmitem.lpszWD, szCL);
  1229. Str_SetPtr(&pmitem.lpszIconPath, szIconPath);
  1230. pmitem.wiIcon = itemdef.iIcon;
  1231. }
  1232. DSA_InsertItem(hdsaPMItems, iItem, &pmitem);
  1233. iItem++;
  1234. }
  1235. LocalFree((HLOCAL)rgItems);
  1236. return fOK;
  1237. }
  1238. //-----------------------------------------------------------------------------
  1239. // Functions to try to find out which icon was appropriate given the NT icon
  1240. // identifier number (the identifier for the RT_ICON resource only).
  1241. //-----------------------------------------------------------------------------
  1242. typedef struct _enumstruct {
  1243. UINT iIndex;
  1244. BOOL fFound;
  1245. WORD wIconRTIconID;
  1246. } ENUMSTRUCT, *LPENUMSTRUCT;
  1247. BOOL EnumIconFunc(
  1248. HMODULE hMod,
  1249. LPCTSTR lpType,
  1250. LPTSTR lpName,
  1251. LPARAM lParam
  1252. ) {
  1253. HANDLE h;
  1254. PBYTE p;
  1255. int id;
  1256. LPENUMSTRUCT lpes = (LPENUMSTRUCT)lParam;
  1257. if (!lpName)
  1258. return TRUE;
  1259. h = FindResource(hMod, lpName, lpType);
  1260. if (!h)
  1261. return TRUE;
  1262. h = LoadResource(hMod, h);
  1263. p = LockResource(h);
  1264. id = LookupIconIdFromDirectory(p, TRUE);
  1265. UnlockResource(h);
  1266. FreeResource(h);
  1267. if (id == lpes->wIconRTIconID)
  1268. {
  1269. lpes->fFound = TRUE;
  1270. return FALSE;
  1271. }
  1272. lpes->iIndex++;
  1273. return TRUE;
  1274. }
  1275. WORD FindAppropriateIcon( LPTSTR lpszFileName, WORD wIconRTIconID )
  1276. {
  1277. HINSTANCE hInst;
  1278. TCHAR szExe[MAX_PATH];
  1279. WORD wIcon = wIconRTIconID;
  1280. ENUMSTRUCT es;
  1281. int olderror;
  1282. hInst = FindExecutable(lpszFileName,NULL,szExe);
  1283. if ( hInst <= (HINSTANCE)HINSTANCE_ERROR )
  1284. {
  1285. return 0;
  1286. }
  1287. olderror = SetErrorMode(SEM_FAILCRITICALERRORS);
  1288. hInst = LoadLibraryEx(szExe,NULL, DONT_RESOLVE_DLL_REFERENCES);
  1289. SetErrorMode(olderror);
  1290. if ( hInst <= (HINSTANCE)HINSTANCE_ERROR )
  1291. {
  1292. return 0;
  1293. }
  1294. es.iIndex = 0;
  1295. es.fFound = FALSE;
  1296. es.wIconRTIconID = wIconRTIconID;
  1297. EnumResourceNames( hInst, RT_GROUP_ICON, EnumIconFunc, (LPARAM)&es );
  1298. FreeLibrary( hInst );
  1299. if (es.fFound)
  1300. {
  1301. return (WORD)es.iIndex;
  1302. }
  1303. else
  1304. {
  1305. return 0;
  1306. }
  1307. }
  1308. //---------------------------------------------------------------------------
  1309. // Read the item data from the file and add it to the list.
  1310. // Returns TRUE if everything went perfectly.
  1311. #ifdef UNICODE
  1312. BOOL GetAllItemDataNT(HANDLE fh, WORD cItems, DWORD cbGroup, LPTSTR lpszOldGrpTitle, LPTSTR lpszNewGrpPath)
  1313. #else
  1314. BOOL GetAllItemDataNT(HFILE fh, WORD cItems, DWORD cbGroup, LPTSTR lpszOldGrpTitle, LPTSTR lpszNewGrpPath)
  1315. #endif
  1316. {
  1317. UINT cbItemArray;
  1318. DWORD *rgItems;
  1319. UINT i, iItem;
  1320. WCHAR wszTemp[CCHSZNORMAL];
  1321. TCHAR szDesc[CCHSZNORMAL];
  1322. TCHAR szCL[CCHSZNORMAL];
  1323. TCHAR szIconPath[CCHSZNORMAL];
  1324. NT_ITEMDEF itemdef;
  1325. BOOL fOK = TRUE;
  1326. #ifdef UNICODE
  1327. DWORD cbRead;
  1328. #else
  1329. UINT cbRead;
  1330. #endif
  1331. PMITEM pmitem;
  1332. // Read in the old item table...
  1333. iItem = 0;
  1334. cbItemArray = cItems * SIZEOF(*rgItems);
  1335. rgItems = (DWORD *)LocalAlloc(LPTR, cbItemArray);
  1336. if (!rgItems)
  1337. {
  1338. DebugMsg(DM_ERROR, TEXT("gc.gcnfo: Out of memory."));
  1339. return FALSE;
  1340. }
  1341. #ifdef UNICODE
  1342. SetFilePointer(fh, FIELD_OFFSET(NT_GROUPDEF,rgiItems[0]), NULL, FILE_BEGIN);
  1343. ReadFile(fh, rgItems, cbItemArray, &cbRead, NULL);
  1344. #else
  1345. _llseek(fh, FIELD_OFFSET(NT_GROUPDEF,rgiItems[0]), 0);
  1346. _lread(fh, rgItems, cbItemArray);
  1347. #endif
  1348. // Show progress in two stages, first reading then writing.
  1349. Group_SetProgressNameAndRange(lpszNewGrpPath, (cItems*2)-1);
  1350. // Read in the items.
  1351. // NB Don't just skip busted items since the tag data contains
  1352. // indices to items and that includes busted ones. Just use
  1353. // an empty description to indicate that the link is invalid.
  1354. for (i=0; i<cItems; i++)
  1355. {
  1356. Group_SetProgress(i);
  1357. szDesc[0] = TEXT('\0');
  1358. szCL[0] = TEXT('\0');
  1359. szIconPath[0] = TEXT('\0');
  1360. itemdef.iIcon = 0;
  1361. if (rgItems[i] == 0)
  1362. {
  1363. DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file has empty item definition - skipping."));
  1364. goto AddItem;
  1365. }
  1366. if (rgItems[i] > cbGroup)
  1367. {
  1368. DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (item entry in invalid part of file) - skipping item."));
  1369. fOK = FALSE;
  1370. goto AddItem;
  1371. }
  1372. #ifdef UNICODE
  1373. SetFilePointer(fh, rgItems[i], NULL, FILE_BEGIN);
  1374. ReadFile(fh, &itemdef, SIZEOF(itemdef), &cbRead, NULL);
  1375. #else
  1376. _llseek(fh, rgItems[i], 0);
  1377. cbRead = _lread(fh, &itemdef, SIZEOF(itemdef));
  1378. #endif
  1379. if (cbRead != SIZEOF(itemdef))
  1380. {
  1381. DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (invalid definition) - skipping item %d."), i);
  1382. fOK = FALSE;
  1383. goto AddItem;
  1384. }
  1385. if (!CheckItemDefNT(&itemdef, cbGroup))
  1386. {
  1387. DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (invalid item field) - skipping item %d."), i);
  1388. fOK = FALSE;
  1389. goto AddItem;
  1390. }
  1391. #ifdef UNICODE
  1392. SetFilePointer(fh, itemdef.pName, NULL, FILE_BEGIN);
  1393. ReadFile(fh, wszTemp, SIZEOF(wszTemp), &cbRead, NULL);
  1394. #else
  1395. _llseek(fh, itemdef.pName, 0);
  1396. _lread(fh, wszTemp, SIZEOF(wszTemp)); // There will be a NUL somewhere
  1397. #endif
  1398. if (!*wszTemp)
  1399. {
  1400. DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (empty name) - skipping item %d."), i);
  1401. fOK = FALSE;
  1402. goto AddItem;
  1403. }
  1404. #ifdef UNICODE
  1405. lstrcpy(szDesc, wszTemp);
  1406. #else
  1407. WideCharToMultiByte (CP_ACP, 0, wszTemp, -1,
  1408. szDesc, ARRAYSIZE(szDesc), NULL, NULL);
  1409. #endif
  1410. #ifdef UNICODE
  1411. SetFilePointer(fh, itemdef.pCommand, NULL, FILE_BEGIN);
  1412. ReadFile(fh, &wszTemp, SIZEOF(wszTemp), &cbRead, NULL);
  1413. #else
  1414. _llseek(fh, itemdef.pCommand, 0);
  1415. _lread(fh, wszTemp, SIZEOF(wszTemp));
  1416. #endif
  1417. if (!*wszTemp)
  1418. {
  1419. DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (empty command line) - skipping item %d."), i);
  1420. // We use a null description to signal a problem with this item.
  1421. szDesc[0] = TEXT('\0');
  1422. fOK = FALSE;
  1423. goto AddItem;
  1424. }
  1425. #ifdef UNICODE
  1426. lstrcpy(szCL, wszTemp);
  1427. #else
  1428. WideCharToMultiByte (CP_ACP, 0, wszTemp, -1,
  1429. szCL, ARRAYSIZE(szCL), NULL, NULL);
  1430. #endif
  1431. #ifdef UNICODE
  1432. SetFilePointer(fh, itemdef.pIconPath, NULL, FILE_BEGIN);
  1433. ReadFile(fh, wszTemp, SIZEOF(wszTemp), &cbRead, NULL);
  1434. #else
  1435. _llseek(fh, itemdef.pIconPath, 0);
  1436. _lread(fh, wszTemp, SIZEOF(wszTemp));
  1437. #endif
  1438. if (!*wszTemp)
  1439. {
  1440. // NB Do nothing. Empty icon paths are legal - associated apps where the associated
  1441. // app is missing will have an empty icon path.
  1442. }
  1443. #ifdef UNICODE
  1444. lstrcpy(szIconPath, wszTemp);
  1445. #else
  1446. WideCharToMultiByte (CP_ACP, 0, wszTemp, -1,
  1447. szIconPath, ARRAYSIZE(szIconPath), NULL, NULL);
  1448. #endif
  1449. // NB Forget about the icon data.
  1450. // DebugMsg(DM_TRACE, "gc.gcnfo: Found item %s.", (LPSTR) szDesc);
  1451. // Store away the data....
  1452. // NB We load the old commands line into the working dir field because
  1453. // only the leaf is the command, the rest is the WD. Once we've been
  1454. // through the tags section we can sort out the mess.
  1455. AddItem:
  1456. PMItem_Clear(&pmitem);
  1457. #ifdef DEBUG
  1458. DebugMsg(GC_TRACE, TEXT("gc.gaid: Desc %s"), (LPTSTR) szDesc);
  1459. DebugMsg(GC_TRACE, TEXT(" WD: %s"), (LPTSTR) szCL);
  1460. DebugMsg(GC_TRACE, TEXT(" IP: %s(%d)"), (LPTSTR) szIconPath, itemdef.iIcon);
  1461. #endif
  1462. // Don't store anything for items with invalid descriptions.
  1463. if (*szDesc)
  1464. {
  1465. WORD wIconIndex;
  1466. // Remove illegal chars.
  1467. PathRemoveIllegalChars(szDesc, 0, PRICF_NORMAL);
  1468. Str_SetPtr(&pmitem.lpszDesc, szDesc);
  1469. Str_SetPtr(&pmitem.lpszWD, szCL);
  1470. Str_SetPtr(&pmitem.lpszIconPath, szIconPath);
  1471. wIconIndex = itemdef.wIconIndex;
  1472. if ( wIconIndex == 0 )
  1473. {
  1474. WORD wIcon;
  1475. HICON hIcon;
  1476. if ( *szIconPath == TEXT('\0') )
  1477. {
  1478. FindExecutable(szCL,NULL,szIconPath);
  1479. }
  1480. if ( *szIconPath != TEXT('\0') )
  1481. {
  1482. wIconIndex = FindAppropriateIcon( szIconPath, itemdef.iIcon);
  1483. }
  1484. }
  1485. pmitem.wiIcon = wIconIndex;
  1486. }
  1487. DSA_InsertItem(hdsaPMItems, iItem, &pmitem);
  1488. iItem++;
  1489. }
  1490. LocalFree((HLOCAL)rgItems);
  1491. return fOK;
  1492. }
  1493. //---------------------------------------------------------------------------
  1494. // Create the links in the given dest dir.
  1495. void CreateLinks(LPCTSTR lpszNewGrpPath, BOOL fStartup, INT cItemsStart)
  1496. {
  1497. int i, cItems;
  1498. TCHAR szLinkName[MAX_PATH];
  1499. TCHAR szBuffer[MAX_PATH];
  1500. // we make this 3*MAX_PATH so that DARWIN and LOGO3 callers can pass their extra information
  1501. TCHAR szExpBuff[3*MAX_PATH];
  1502. WCHAR wszPath[MAX_PATH];
  1503. LPTSTR lpszArgs;
  1504. LPCTSTR dirs[2];
  1505. IShellLink *psl;
  1506. LPTSTR pszExt;
  1507. if (SUCCEEDED(ICoCreateInstance(&CLSID_ShellLink, &IID_IShellLink, &psl))) {
  1508. IPersistFile *ppf;
  1509. psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, &ppf);
  1510. cItems = DSA_GetItemCount(hdsaPMItems);
  1511. for (i = 0; i < cItems; i++) {
  1512. LPPMITEM lppmitem = DSA_GetItemPtr(hdsaPMItems, i);
  1513. // We show the progress in 2 halves.
  1514. Group_SetProgress(cItemsStart+(i*cItemsStart/cItems));
  1515. // command line and args.
  1516. // if this command line points to net drives we should add
  1517. // the UNC mapping to the link
  1518. Str_GetPtr(lppmitem->lpszCL, szBuffer, ARRAYSIZE(szBuffer));
  1519. // Spaces at the begining of the CL will confuse us.
  1520. PathRemoveBlanks(szBuffer);
  1521. lpszArgs = PathGetArgs(szBuffer);
  1522. if (*lpszArgs)
  1523. *(lpszArgs-1) = TEXT('\0');
  1524. // NB Special case, remove all links to Progman[.exe] from the
  1525. // Startup Group. A lot of people put it there to give it a hotkey.
  1526. // We want to be able to delete it regardless of its name ie we
  1527. // can't just use setup.ini to do the work.
  1528. if (fStartup)
  1529. {
  1530. if ((lstrcmpi(c_szProgmanExe, PathFindFileName(szBuffer)) == 0) ||
  1531. (lstrcmpi(c_szProgman, PathFindFileName(szBuffer)) == 0))
  1532. continue;
  1533. }
  1534. psl->lpVtbl->SetArguments(psl, lpszArgs);
  1535. //
  1536. // Remove quotes from the command file name NOW.
  1537. //
  1538. if (szBuffer[0] == TEXT('"')) {
  1539. LPTSTR lpTemp;
  1540. for (lpTemp = szBuffer+1; *lpTemp && *lpTemp != TEXT('"'); lpTemp++)
  1541. *(lpTemp-1) = *lpTemp;
  1542. if (*lpTemp == TEXT('"')) {
  1543. *(lpTemp-1) = TEXT('\0');
  1544. }
  1545. }
  1546. // working directory
  1547. // NB Progman assumed an empty WD meant use the windows
  1548. // directory but we want to change this so to be
  1549. // backwards compatable we'll fill in missing WD's here.
  1550. if (!lppmitem->lpszWD || !*lppmitem->lpszWD)
  1551. {
  1552. // NB For links to pif's we don't fill in a default WD
  1553. // so we'll pick it up from pif itself. This fixes a
  1554. // problem upgrading some Compaq Deskpro's.
  1555. pszExt = PathFindExtension(szBuffer);
  1556. if (lstrcmpi(pszExt, c_szDotPif) == 0)
  1557. {
  1558. psl->lpVtbl->SetWorkingDirectory(psl, c_szNULL);
  1559. }
  1560. else
  1561. {
  1562. #ifdef WINNT
  1563. // Avoid setting to %windir%, under NT we want to change to the users home directory.
  1564. psl->lpVtbl->SetWorkingDirectory( psl, TEXT("%HOMEDRIVE%%HOMEPATH%") );
  1565. #else
  1566. // Not a pif. Set the WD to be that of the windows dir.
  1567. psl->lpVtbl->SetWorkingDirectory(psl, TEXT("%windir%"));
  1568. #endif
  1569. }
  1570. }
  1571. else
  1572. {
  1573. psl->lpVtbl->SetWorkingDirectory(psl, lppmitem->lpszWD);
  1574. }
  1575. // icon location
  1576. // REVIEW, do we want to unqualify the icon path if possible? also,
  1577. // if the icon path is the same as the command line we don't need it
  1578. if (lppmitem->wiIcon != 0 || lstrcmpi(lppmitem->lpszIconPath, szBuffer) != 0)
  1579. {
  1580. // Remove args.
  1581. lpszArgs = PathGetArgs(lppmitem->lpszIconPath);
  1582. if (*lpszArgs)
  1583. *(lpszArgs-1) = TEXT('\0');
  1584. psl->lpVtbl->SetIconLocation(psl, lppmitem->lpszIconPath, lppmitem->wiIcon);
  1585. }
  1586. else
  1587. {
  1588. psl->lpVtbl->SetIconLocation(psl, NULL, 0);
  1589. }
  1590. // hotkey
  1591. psl->lpVtbl->SetHotkey(psl, lppmitem->wHotKey);
  1592. // show command
  1593. psl->lpVtbl->SetShowCmd(psl, lppmitem->nShowCmd);
  1594. // Description. Currently pifmgr is the only guy
  1595. // that cares about the description and they use
  1596. // it to overide the default pif description.
  1597. psl->lpVtbl->SetDescription(psl, lppmitem->lpszDesc);
  1598. //
  1599. // NOTE it is very important to set filename *last*
  1600. // because if this is a group item to another link
  1601. // (either .lnk or .pif) we want the link properties
  1602. // to override the ones we just set.
  1603. //
  1604. // qualify path to subject (szBuffer)
  1605. dirs[0] = lppmitem->lpszWD;
  1606. dirs[1] = NULL;
  1607. // Try expanding szBuffer
  1608. ExpandEnvironmentStrings( szBuffer, szExpBuff, MAX_PATH );
  1609. szExpBuff[ MAX_PATH-1 ] = TEXT('\0');
  1610. if (!PathResolve(szExpBuff, dirs, PRF_TRYPROGRAMEXTENSIONS))
  1611. {
  1612. // Just assume the expanded thing was a-ok...
  1613. ExpandEnvironmentStrings(szBuffer, szExpBuff, MAX_PATH);
  1614. szExpBuff[ MAX_PATH-1 ] = TEXT('\0');
  1615. }
  1616. // all we need to call is setpath, it takes care of creating the
  1617. // pidl for us.
  1618. psl->lpVtbl->SetPath( psl, szBuffer );
  1619. #ifdef WINNT
  1620. {
  1621. IShellLinkDataList* psldl;
  1622. if (SUCCEEDED(psl->lpVtbl->QueryInterface(psl, &IID_IShellLinkDataList, (LPVOID)&psldl)))
  1623. {
  1624. DWORD dwFlags;
  1625. if (SUCCEEDED(psldl->lpVtbl->GetFlags(psldl, &dwFlags)))
  1626. {
  1627. if (lppmitem->bSepVdm)
  1628. dwFlags |= SLDF_RUN_IN_SEPARATE;
  1629. else
  1630. dwFlags &= (~SLDF_RUN_IN_SEPARATE);
  1631. psldl->lpVtbl->SetFlags(psldl, dwFlags);
  1632. }
  1633. psldl->lpVtbl->Release(psldl);
  1634. }
  1635. }
  1636. #endif
  1637. // over write the link if it already exists
  1638. PathCombine(szLinkName, lpszNewGrpPath, lppmitem->lpszDesc);
  1639. lstrcat(szLinkName, TEXT(".lnk"));
  1640. PathQualify(szLinkName);
  1641. // OLE string.
  1642. StrToOleStrN(wszPath, ARRAYSIZE(wszPath), szLinkName, -1);
  1643. ppf->lpVtbl->Save(ppf, wszPath, TRUE);
  1644. }
  1645. ppf->lpVtbl->Release(ppf);
  1646. psl->lpVtbl->Release(psl);
  1647. }
  1648. }
  1649. //----------------------------------------------------------------------------
  1650. // Returns TRUE if the specified group title is that of the startup group.
  1651. BOOL StartupCmp(LPTSTR szGrp)
  1652. {
  1653. static TCHAR szOldStartupGrp[MAX_PATH];
  1654. TCHAR szNewStartupPath[MAX_PATH];
  1655. if (!*szOldStartupGrp)
  1656. {
  1657. // Was it over-ridden in progman ini?
  1658. GetPrivateProfileString(c_szSettings, c_szStartup, c_szNULL, szOldStartupGrp,
  1659. ARRAYSIZE(szOldStartupGrp), c_szProgmanIni);
  1660. if (!*szOldStartupGrp)
  1661. {
  1662. LONG lResult;
  1663. DWORD cbSize;
  1664. // No, try reading it from the NT registry
  1665. cbSize = MAX_PATH;
  1666. lResult = RegQueryValue(HKEY_CURRENT_USER, c_szProgmanStartup, szOldStartupGrp, &cbSize );
  1667. // Potential porblem with Kana Start
  1668. if ( lResult != ERROR_SUCCESS )
  1669. {
  1670. // No, use the default name.
  1671. LoadString(g_hinst, IDS_STARTUP, szOldStartupGrp, ARRAYSIZE(szOldStartupGrp));
  1672. }
  1673. }
  1674. if (*szOldStartupGrp)
  1675. {
  1676. // Yes, use the over-riding name by updating the registry.
  1677. SHGetSpecialFolderPath(NULL, szNewStartupPath, CSIDL_PROGRAMS, FALSE);
  1678. PathAddBackslash(szNewStartupPath);
  1679. lstrcat(szNewStartupPath, szOldStartupGrp);
  1680. DebugMsg(DM_TRACE, TEXT("gc.sc: Non-default Startup path is %s."), szNewStartupPath);
  1681. Reg_SetString(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER_SHELLFOLDERS, c_szStartup, szNewStartupPath);
  1682. }
  1683. }
  1684. // Does it match?
  1685. if (*szOldStartupGrp && (lstrcmpi(szGrp, szOldStartupGrp) == 0))
  1686. return TRUE;
  1687. else
  1688. return FALSE;
  1689. }
  1690. //---------------------------------------------------------------------------
  1691. BOOL CALLBACK IsDescUnique(LPCTSTR lpsz, UINT n)
  1692. {
  1693. int i, cItems;
  1694. LPPMITEM pitem;
  1695. // DebugMsg(DM_TRACE, "gc.idu: Checking uniqueness of %s.", lpsz);
  1696. cItems = DSA_GetItemCount(hdsaPMItems);
  1697. for (i=0; i<cItems; i++)
  1698. {
  1699. // N is our guy, skip it.
  1700. if ((UINT)i == n)
  1701. continue;
  1702. pitem = DSA_GetItemPtr(hdsaPMItems, i);
  1703. Assert(pitem);
  1704. if (pitem->lpszDesc && *pitem->lpszDesc && (lstrcmpi(pitem->lpszDesc, lpsz) == 0))
  1705. {
  1706. // DebugMsg(DM_TRACE, "gc.idu: Not Unique.");
  1707. return FALSE;
  1708. }
  1709. }
  1710. // Yep. can't find it, must be unique.
  1711. // DebugMsg(DM_TRACE, "gc.idu: Unique.");
  1712. return TRUE;
  1713. }
  1714. //---------------------------------------------------------------------------
  1715. // If there are two or more items with the same link name then change them so
  1716. // that they are unique.
  1717. void ResolveDuplicates(LPCTSTR pszNewGrpPath)
  1718. {
  1719. LPPMITEM pitem;
  1720. int i, cItems;
  1721. TCHAR szNew[MAX_PATH];
  1722. BOOL fLFN;
  1723. int cchSpace;
  1724. DebugMsg(DM_TRACE, TEXT("gc.rd: Fixing dups..."));
  1725. // How much room is there for adding the #xx stuff?
  1726. cchSpace = (ARRAYSIZE(szNew)-lstrlen(pszNewGrpPath))-2;
  1727. if (cchSpace > 0)
  1728. {
  1729. // LFN's or no?
  1730. fLFN = IsLFNDrive(pszNewGrpPath);
  1731. if (!fLFN && cchSpace > 8)
  1732. cchSpace = 8;
  1733. // Fix dups
  1734. cItems = DSA_GetItemCount(hdsaPMItems);
  1735. for (i=0; i<(cItems-1); i++)
  1736. {
  1737. pitem = DSA_GetItemPtr(hdsaPMItems, i);
  1738. Assert(pitem);
  1739. YetAnotherMakeUniqueName(szNew, cchSpace, pitem->lpszDesc, IsDescUnique, i, fLFN);
  1740. // Did we get a new name?
  1741. if (lstrcmp(szNew, pitem->lpszDesc) != 0)
  1742. {
  1743. // Yep.
  1744. DebugMsg(DM_TRACE, TEXT("gc.rd: %s to %s"), pitem->lpszDesc, szNew);
  1745. Str_SetPtr(&pitem->lpszDesc, szNew);
  1746. }
  1747. }
  1748. }
  1749. DebugMsg(DM_TRACE, TEXT("gc.rd: Done."));
  1750. }
  1751. //---------------------------------------------------------------------------
  1752. typedef struct
  1753. {
  1754. LPTSTR pszName;
  1755. LPTSTR pszPath;
  1756. LPTSTR pszModule;
  1757. LPTSTR pszVer;
  1758. } ALITEM;
  1759. typedef ALITEM *PALITEM;
  1760. //---------------------------------------------------------------------------
  1761. // Record the total list of apps in a DSA.
  1762. void AppList_WriteFile(void)
  1763. {
  1764. int i, cItems;
  1765. PALITEM palitem;
  1766. TCHAR szBetaID[MAX_PATH];
  1767. TCHAR szLine[4*MAX_PATH];
  1768. HANDLE hFile;
  1769. DWORD cbWritten;
  1770. Assert(g_hdsaAppList);
  1771. cItems = DSA_GetItemCount(g_hdsaAppList);
  1772. if (cItems)
  1773. {
  1774. // Get the beta ID.
  1775. szBetaID[0] = TEXT('\0');
  1776. Reg_GetString(HKEY_LOCAL_MACHINE, c_szRegistry, c_szDefaultUser, szBetaID, SIZEOF(szBetaID));
  1777. // Ick - Hard coded file name and in the current dir!
  1778. hFile = CreateFile(c_szGrpConvData, GENERIC_WRITE, FILE_SHARE_READ, NULL,
  1779. CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  1780. if (hFile != INVALID_HANDLE_VALUE)
  1781. {
  1782. for (i=0; i < cItems; i++)
  1783. {
  1784. palitem = DSA_GetItemPtr(g_hdsaAppList, i);
  1785. wsprintf(szLine, TEXT("%s,\"%s\",\"%s\",\"%s\",\"%s\",,,\r\n"), szBetaID, palitem->pszName,
  1786. palitem->pszPath, palitem->pszModule, palitem->pszVer);
  1787. DebugMsg(DM_TRACE,TEXT("gc.al_wf: %s"), szLine);
  1788. WriteFile(hFile, szLine, lstrlen(szLine)*SIZEOF(TCHAR), &cbWritten, NULL);
  1789. }
  1790. CloseHandle(hFile);
  1791. }
  1792. else
  1793. {
  1794. DebugMsg(DM_ERROR, TEXT("gc.al_wf: Can't write file."));
  1795. }
  1796. }
  1797. else
  1798. {
  1799. DebugMsg(DM_TRACE, TEXT("gc.al_wf: Empty app list. Nothing to write."));
  1800. }
  1801. }
  1802. //---------------------------------------------------------------------------
  1803. //#define DSA_AppendItem(hdsa, pitem) DSA_InsertItem(hdsa, 0x7fff, pitem)
  1804. //---------------------------------------------------------------------------
  1805. static TCHAR const c_szTranslation[] = TEXT("\\VarFileInfo\\Translation");
  1806. static TCHAR const c_szStringFileInfo[] = TEXT("\\StringFileInfo\\");
  1807. static TCHAR const c_szEngLangCharSet[] = TEXT("040904e4");
  1808. static TCHAR const c_szSlash[] = TEXT("\\");
  1809. static TCHAR const c_szInternalName[] = TEXT("InternalName");
  1810. static TCHAR const c_szProductVersion[] = TEXT("ProductVersion");
  1811. //----------------------------------------------------------------------------
  1812. BOOL Ver_GetDefaultCharSet(const PVOID pBuf, LPTSTR pszLangCharSet, int cbLangCharSet)
  1813. {
  1814. LPWORD pTransTable;
  1815. DWORD cb;
  1816. Assert(pszLangCharSet);
  1817. Assert(cbLangCharSet > 8);
  1818. if (VerQueryValue(pBuf, (LPTSTR)c_szTranslation, &pTransTable, &cb))
  1819. {
  1820. wsprintf(pszLangCharSet, TEXT("%04X%04X"), *pTransTable, *(pTransTable+1));
  1821. return TRUE;
  1822. }
  1823. return FALSE;
  1824. }
  1825. //----------------------------------------------------------------------------
  1826. BOOL Ver_GetStringFileInfo(PVOID pBuf, LPCTSTR pszLangCharSet,
  1827. LPCTSTR pszStringName, LPTSTR pszValue, int cchValue)
  1828. {
  1829. TCHAR szSubBlock[MAX_PATH];
  1830. LPTSTR pszBuf;
  1831. DWORD cbBuf;
  1832. lstrcpy(szSubBlock, c_szStringFileInfo);
  1833. lstrcat(szSubBlock, pszLangCharSet);
  1834. lstrcat(szSubBlock, c_szSlash);
  1835. lstrcat(szSubBlock, pszStringName);
  1836. if (VerQueryValue(pBuf, szSubBlock, &pszBuf, &cbBuf))
  1837. {
  1838. lstrcpyn(pszValue, pszBuf, cchValue);
  1839. return TRUE;
  1840. }
  1841. return FALSE;
  1842. }
  1843. //---------------------------------------------------------------------------
  1844. void GetVersionInfo(LPTSTR pszPath, LPTSTR pszModule, int cchModule, LPTSTR pszVer, int cchVer)
  1845. {
  1846. DWORD cbBuf;
  1847. LPVOID pBuf;
  1848. TCHAR szCharSet[MAX_PATH];
  1849. DWORD dwWasteOfAnAuto;
  1850. Assert(pszModule);
  1851. Assert(pszVer);
  1852. pszModule[0] = TEXT('\0');
  1853. pszVer[0] = TEXT('\0');
  1854. cbBuf = GetFileVersionInfoSize(pszPath, &dwWasteOfAnAuto);
  1855. if (cbBuf)
  1856. {
  1857. pBuf = SHAlloc(cbBuf);
  1858. if (pBuf)
  1859. {
  1860. if (GetFileVersionInfo(pszPath, 0, cbBuf, pBuf))
  1861. {
  1862. // Try the default language from the translation tables.
  1863. if (Ver_GetDefaultCharSet(pBuf, szCharSet, ARRAYSIZE(szCharSet)))
  1864. {
  1865. Ver_GetStringFileInfo(pBuf, szCharSet, c_szInternalName, pszModule, cchModule);
  1866. Ver_GetStringFileInfo(pBuf, szCharSet, c_szProductVersion, pszVer, cchVer);
  1867. }
  1868. else
  1869. {
  1870. // Try the same language as us.
  1871. LoadString(g_hinst, IDS_DEFLANGCHARSET, szCharSet, ARRAYSIZE(szCharSet));
  1872. Ver_GetStringFileInfo(pBuf, szCharSet, c_szInternalName, pszModule, cchModule);
  1873. Ver_GetStringFileInfo(pBuf, szCharSet, c_szProductVersion, pszVer, cchVer);
  1874. }
  1875. // Last chance - try English.
  1876. if (!*pszModule)
  1877. Ver_GetStringFileInfo(pBuf, c_szEngLangCharSet, c_szInternalName, pszModule, cchModule);
  1878. if (!*pszVer)
  1879. Ver_GetStringFileInfo(pBuf, c_szEngLangCharSet, c_szProductVersion, pszVer, cchVer);
  1880. }
  1881. else
  1882. {
  1883. DebugMsg(DM_TRACE, TEXT("gc.gvi: Can't get version info."));
  1884. }
  1885. SHFree(pBuf);
  1886. }
  1887. else
  1888. {
  1889. DebugMsg(DM_TRACE, TEXT("gc.gvi: Can't allocate version info buffer."));
  1890. }
  1891. }
  1892. else
  1893. {
  1894. DebugMsg(DM_TRACE, TEXT("gc.gvi: No version info."));
  1895. }
  1896. }
  1897. //---------------------------------------------------------------------------
  1898. // Record the total list of apps in a DSA.
  1899. BOOL AppList_Create(void)
  1900. {
  1901. Assert(!g_hdsaAppList);
  1902. g_hdsaAppList = DSA_Create(SIZEOF(ALITEM), 0);
  1903. if (g_hdsaAppList)
  1904. {
  1905. return TRUE;
  1906. }
  1907. else
  1908. {
  1909. DebugMsg(DM_ERROR, TEXT("gc.al_c: Can't create app list."));
  1910. return FALSE;
  1911. }
  1912. }
  1913. //---------------------------------------------------------------------------
  1914. // Record the total list of apps in a DSA.
  1915. void AppList_Destroy(void)
  1916. {
  1917. int i, cItems;
  1918. PALITEM palitem;
  1919. Assert(g_hdsaAppList);
  1920. cItems = DSA_GetItemCount(g_hdsaAppList);
  1921. for (i=0; i < cItems; i++)
  1922. {
  1923. palitem = DSA_GetItemPtr(g_hdsaAppList, i);
  1924. if (palitem->pszName)
  1925. SHFree(palitem->pszName);
  1926. if (palitem->pszPath)
  1927. SHFree(palitem->pszPath);
  1928. if (palitem->pszModule)
  1929. SHFree(palitem->pszModule);
  1930. if (palitem->pszVer)
  1931. SHFree(palitem->pszVer);
  1932. }
  1933. DSA_Destroy(g_hdsaAppList);
  1934. g_hdsaAppList = NULL;
  1935. }
  1936. //---------------------------------------------------------------------------
  1937. // Record the total list of apps in a DSA.
  1938. void AppList_Append(void)
  1939. {
  1940. int i, cItems;
  1941. // char szName[MAX_PATH];
  1942. // char szPath[MAX_PATH];
  1943. TCHAR szModule[MAX_PATH];
  1944. TCHAR szVer[MAX_PATH];
  1945. TCHAR szCL[MAX_PATH];
  1946. LPTSTR lpszArgs;
  1947. LPCTSTR dirs[2];
  1948. ALITEM alitem;
  1949. Assert(g_hdsaAppList);
  1950. cItems = DSA_GetItemCount(hdsaPMItems);
  1951. for (i = 0; i < cItems; i++)
  1952. {
  1953. LPPMITEM lppmitem = DSA_GetItemPtr(hdsaPMItems, i);
  1954. // We show the progress in 2 halves.
  1955. Group_SetProgress(cItems+i);
  1956. // Command line and args.
  1957. Str_GetPtr(lppmitem->lpszCL, szCL, ARRAYSIZE(szCL));
  1958. lpszArgs = PathGetArgs(szCL);
  1959. if (*lpszArgs)
  1960. *(lpszArgs-1) = TEXT('\0');
  1961. dirs[0] = lppmitem->lpszWD;
  1962. dirs[1] = NULL;
  1963. PathResolve(szCL, dirs, PRF_TRYPROGRAMEXTENSIONS);
  1964. // Version info.
  1965. GetVersionInfo(szCL, szModule, ARRAYSIZE(szModule), szVer, ARRAYSIZE(szVer));
  1966. alitem.pszName = NULL;
  1967. alitem.pszPath = NULL;
  1968. alitem.pszModule = NULL;
  1969. alitem.pszVer = NULL;
  1970. Str_SetPtr(&alitem.pszName, lppmitem->lpszDesc);
  1971. Str_SetPtr(&alitem.pszPath, szCL);
  1972. Str_SetPtr(&alitem.pszModule, szModule);
  1973. Str_SetPtr(&alitem.pszVer, szVer);
  1974. DSA_AppendItem(g_hdsaAppList, &alitem);
  1975. }
  1976. DebugMsg(DM_TRACE, TEXT("gc.al_a: %d items"), DSA_GetItemCount(g_hdsaAppList));
  1977. }
  1978. //---------------------------------------------------------------------------
  1979. // Reads an old format Progman Group files and creates a directory containing
  1980. // links that matches the group file.
  1981. BOOL Group_CreateNewFromOld(HWND hwnd, LPCTSTR lpszOldGrpPath, UINT options)
  1982. {
  1983. GROUPDEF grpdef;
  1984. #ifdef UNICODE
  1985. HANDLE fh;
  1986. DWORD dwBytesRead;
  1987. #else
  1988. HFILE fh;
  1989. #endif
  1990. TCHAR szNewGrpPath[MAX_PATH];
  1991. TCHAR szOldGrpTitle[MAXGROUPNAMELEN + 1];
  1992. // LPSTR lpszExt;
  1993. BOOL fStatus = FALSE;
  1994. SHELLEXECUTEINFO sei;
  1995. BOOL fStartup = FALSE;
  1996. if (!ItemList_Create(lpszOldGrpPath))
  1997. return FALSE;
  1998. #ifdef UNICODE
  1999. fh = CreateFile(
  2000. lpszOldGrpPath,
  2001. GENERIC_READ,
  2002. FILE_SHARE_READ,
  2003. NULL,
  2004. OPEN_EXISTING,
  2005. 0,
  2006. NULL
  2007. );
  2008. if (fh == INVALID_HANDLE_VALUE) {
  2009. #else
  2010. fh = _lopen(lpszOldGrpPath, OF_READ | OF_SHARE_DENY_NONE);
  2011. if (fh == HFILE_ERROR) {
  2012. #endif
  2013. DebugMsg(DM_ERROR, TEXT("gc.gcnfo: Unable to open group."));
  2014. goto ProcExit2;
  2015. }
  2016. #ifdef UNICODE
  2017. if ((!ReadFile(fh, &grpdef, SIZEOF(grpdef), &dwBytesRead, NULL)) ||
  2018. (dwBytesRead != SIZEOF(grpdef))) {
  2019. #else
  2020. if (_lread(fh, &grpdef, SIZEOF(grpdef)) != SIZEOF(grpdef)) {
  2021. #endif
  2022. DebugMsg(DM_ERROR, TEXT("gc.gcnfo: header too small."));
  2023. goto ProcExit;
  2024. }
  2025. if (grpdef.cItems > 50) {
  2026. // NB This isn;t fatal so carry on.
  2027. DebugMsg(DM_ERROR, TEXT("gc.gcnfo: Too many items."));
  2028. }
  2029. // Check to make sure there is a name embedded in the
  2030. // .grp file. If not, just use the filename
  2031. if (grpdef.pName==0) {
  2032. LPTSTR lpszFile, lpszExt, lpszDest = szOldGrpTitle;
  2033. lpszFile = PathFindFileName( lpszOldGrpPath );
  2034. lpszExt = PathFindExtension( lpszOldGrpPath );
  2035. for( ;
  2036. lpszFile && lpszExt && (lpszFile != lpszExt);
  2037. *lpszDest++ = *lpszFile++
  2038. );
  2039. *lpszDest = TEXT('\0');
  2040. } else {
  2041. #ifdef UNICODE
  2042. CHAR szAnsiTitle[ MAXGROUPNAMELEN + 1 ];
  2043. SetFilePointer(fh, grpdef.pName, NULL, FILE_BEGIN);
  2044. ReadFile(fh, szAnsiTitle, SIZEOF(szAnsiTitle), &dwBytesRead, NULL);
  2045. MultiByteToWideChar( CP_ACP, 0, szAnsiTitle, -1, szOldGrpTitle, ARRAYSIZE(szOldGrpTitle) );
  2046. #else
  2047. _llseek(fh, grpdef.pName, 0);
  2048. _lread(fh, szOldGrpTitle, SIZEOF(szOldGrpTitle));
  2049. #endif
  2050. }
  2051. // Get the destination dir, use the title from the old group...
  2052. // Special case the startup group.
  2053. if (StartupCmp(szOldGrpTitle)) {
  2054. fStartup = TRUE;
  2055. if (g_fDoingCommonGroups) {
  2056. SHGetSpecialFolderPath(hwnd, szNewGrpPath, CSIDL_COMMON_STARTUP, TRUE);
  2057. } else {
  2058. SHGetSpecialFolderPath(hwnd, szNewGrpPath, CSIDL_STARTUP, TRUE);
  2059. }
  2060. } else {
  2061. if (!Group_GenerateNewGroupPath(hwnd, szOldGrpTitle, szNewGrpPath, lpszOldGrpPath)) {
  2062. DebugMsg(DM_ERROR, TEXT("gc.gcnfo; Unable to create destination directory."));
  2063. goto ProcExit;
  2064. }
  2065. }
  2066. // PathQualify(szNewGrpPath);
  2067. // ResolveDuplicateGroupNames(szNewGrpPath);
  2068. // Go through every item in the old group and make it a link...
  2069. if (!GetAllItemData(fh, grpdef.cItems, grpdef.cbGroup, szOldGrpTitle, szNewGrpPath)) {
  2070. if (options & GC_REPORTERROR)
  2071. MyMessageBox(hwnd, IDS_APPTITLE, IDS_BADOLDGROUP, NULL, MB_OK | MB_ICONEXCLAMATION);
  2072. }
  2073. // Deal with the tags section.
  2074. HandleTags(fh, grpdef.cbGroup);
  2075. // Now we've dealt with the tags we don't need to keep track of
  2076. // busted items so delete them now. From here on we always have
  2077. // valid items.
  2078. DeleteBustedItems();
  2079. // Shorten descs on non-lfn drives.
  2080. if (!IsLFNDrive(szNewGrpPath))
  2081. ShortenDescriptions();
  2082. // Fixup the paths/WD stuff.
  2083. MungePaths();
  2084. // Fix dups.
  2085. ResolveDuplicates(szNewGrpPath);
  2086. // Do we just want a list of the apps or create some links?
  2087. if (options & GC_BUILDLIST)
  2088. AppList_Append();
  2089. else
  2090. CreateLinks(szNewGrpPath, fStartup, grpdef.cItems);
  2091. // Get the cabinet to show the new group.
  2092. if (options & GC_OPENGROUP)
  2093. {
  2094. sei.cbSize = SIZEOF(sei);
  2095. sei.fMask = 0;
  2096. sei.hwnd = hwnd;
  2097. sei.lpVerb = NULL;
  2098. sei.lpFile = szNewGrpPath;
  2099. sei.lpParameters = NULL;
  2100. sei.lpDirectory = NULL;
  2101. sei.lpClass = NULL;
  2102. sei.nShow = SW_SHOWNORMAL;
  2103. sei.hInstApp = g_hinst;
  2104. // ShellExecute(hwnd, NULL, szNewGrpPath, NULL, NULL, SW_SHOWNORMAL);
  2105. ShellExecuteEx(&sei);
  2106. }
  2107. // Everything went OK.
  2108. fStatus = TRUE;
  2109. ProcExit:
  2110. #ifdef UNICODE
  2111. CloseHandle(fh);
  2112. #else
  2113. _lclose(fh);
  2114. #endif
  2115. #ifndef WINNT
  2116. // we only need to call Group_DeleteIfRequired
  2117. // when we are on a Japanese language machine (win95J or win98J). We
  2118. // should have a runtime check for Japanese here.
  2119. // Delete old group file when it is specified in special
  2120. // registry entry. Bug#7259-win95d
  2121. //
  2122. if (fStatus == TRUE)
  2123. {
  2124. // delete it only if the conversion was successful.
  2125. Group_DeleteIfRequired(szOldGrpTitle,lpszOldGrpPath);
  2126. }
  2127. #endif // !WINNT
  2128. ProcExit2:
  2129. ItemList_Destroy();
  2130. return fStatus;
  2131. }
  2132. //---------------------------------------------------------------------------
  2133. // Reads an NT format Progman Group files and creates a directory containing
  2134. // links that matches the group file.
  2135. BOOL Group_CreateNewFromOldNT(HWND hwnd, LPCTSTR lpszOldGrpPath, UINT options)
  2136. {
  2137. NT_GROUPDEF grpdef;
  2138. #ifdef UNICODE
  2139. HANDLE fh;
  2140. DWORD dwBytesRead;
  2141. #else
  2142. HFILE fh;
  2143. #endif
  2144. TCHAR szNewGrpPath[MAX_PATH];
  2145. WCHAR szOldGrpTitleUnicode[MAXGROUPNAMELEN + 1];
  2146. TCHAR szOldGrpTitle[MAXGROUPNAMELEN + 1];
  2147. // LPSTR lpszExt;
  2148. BOOL fStatus = FALSE;
  2149. SHELLEXECUTEINFO sei;
  2150. BOOL fStartup = FALSE;
  2151. if (!ItemList_Create(lpszOldGrpPath))
  2152. return FALSE;
  2153. #ifdef UNICODE
  2154. fh = CreateFile(
  2155. lpszOldGrpPath,
  2156. GENERIC_READ,
  2157. FILE_SHARE_READ,
  2158. NULL,
  2159. OPEN_EXISTING,
  2160. 0,
  2161. NULL
  2162. );
  2163. if (fh == INVALID_HANDLE_VALUE) {
  2164. #else
  2165. fh = _lopen(lpszOldGrpPath, OF_READ | OF_SHARE_DENY_NONE);
  2166. if (fh == HFILE_ERROR) {
  2167. #endif
  2168. DebugMsg(DM_ERROR, TEXT("gc.gcnfont: Unable to open group."));
  2169. goto ProcExit2;
  2170. }
  2171. #ifdef UNICODE
  2172. if (!ReadFile(fh, &grpdef, SIZEOF(grpdef), &dwBytesRead, NULL) ||
  2173. dwBytesRead != SIZEOF(grpdef)) {
  2174. #else
  2175. if (_lread(fh, &grpdef, SIZEOF(grpdef)) != SIZEOF(grpdef)) {
  2176. #endif
  2177. DebugMsg(DM_ERROR, TEXT("gc.gcnfont: header too small."));
  2178. goto ProcExit;
  2179. }
  2180. if (grpdef.cItems > 50) {
  2181. // NB This isn;t fatal so carry on.
  2182. DebugMsg(DM_ERROR, TEXT("gc.gcnfont: Too many items."));
  2183. }
  2184. #ifdef UNICODE
  2185. SetFilePointer(fh, grpdef.pName, NULL, FILE_BEGIN);
  2186. ReadFile(fh, szOldGrpTitleUnicode, SIZEOF(szOldGrpTitleUnicode), &dwBytesRead, NULL);
  2187. #else
  2188. _llseek(fh, grpdef.pName, 0);
  2189. _lread(fh, szOldGrpTitleUnicode, SIZEOF(szOldGrpTitleUnicode));
  2190. #endif
  2191. #ifdef UNICODE
  2192. lstrcpy(szOldGrpTitle, szOldGrpTitleUnicode);
  2193. #else
  2194. WideCharToMultiByte (CP_ACP, 0, szOldGrpTitleUnicode, -1,
  2195. szOldGrpTitle, MAXGROUPNAMELEN+1, NULL, NULL);
  2196. #endif
  2197. // Get the destination dir, use the title from the old group.
  2198. // REVIEW UNDONE - until we get long filenames we'll use the old
  2199. // groups' filename as the basis for the new group instead of it's
  2200. // title.
  2201. // Special case the startup group.
  2202. if (StartupCmp(szOldGrpTitle)) {
  2203. if (g_fDoingCommonGroups) {
  2204. SHGetSpecialFolderPath(hwnd, szNewGrpPath, CSIDL_COMMON_STARTUP, TRUE);
  2205. } else {
  2206. SHGetSpecialFolderPath(hwnd, szNewGrpPath, CSIDL_STARTUP, TRUE);
  2207. }
  2208. } else {
  2209. if (!Group_GenerateNewGroupPath(hwnd, szOldGrpTitle, szNewGrpPath, lpszOldGrpPath)) {
  2210. DebugMsg(DM_ERROR, TEXT("gc.gcnfo; Unable to create destination directory."));
  2211. goto ProcExit;
  2212. }
  2213. }
  2214. // Go through every item in the old group and make it a link...
  2215. if (!GetAllItemDataNT(fh, grpdef.cItems, grpdef.cbGroup, szOldGrpTitle, szNewGrpPath)) {
  2216. if (options & GC_REPORTERROR)
  2217. MyMessageBox(hwnd, IDS_APPTITLE, IDS_BADOLDGROUP, NULL, MB_OK | MB_ICONEXCLAMATION);
  2218. }
  2219. // Deal with the tags section.
  2220. HandleTagsNT(fh, grpdef.cbGroup);
  2221. // Now we've dealt with the tags we don't need to keep track of
  2222. // busted items so delete them now. From here on we always have
  2223. // valid items.
  2224. DeleteBustedItems();
  2225. // Shorten descs on non-lfn drives.
  2226. if (!IsLFNDrive(szNewGrpPath))
  2227. ShortenDescriptions();
  2228. // Fixup the paths/WD stuff.
  2229. MungePaths();
  2230. // Fix dups.
  2231. ResolveDuplicates(szNewGrpPath);
  2232. // Do we just want a list of the apps or create some links?
  2233. if (options & GC_BUILDLIST)
  2234. AppList_Append();
  2235. else
  2236. CreateLinks(szNewGrpPath, fStartup, grpdef.cItems);
  2237. // Get the cabinet to show the new group.
  2238. if (options & GC_OPENGROUP)
  2239. {
  2240. sei.cbSize = SIZEOF(sei);
  2241. sei.fMask = 0;
  2242. sei.hwnd = hwnd;
  2243. sei.lpVerb = NULL;
  2244. sei.lpFile = szNewGrpPath;
  2245. sei.lpParameters = NULL;
  2246. sei.lpDirectory = NULL;
  2247. sei.lpClass = NULL;
  2248. sei.nShow = SW_SHOWNORMAL;
  2249. sei.hInstApp = g_hinst;
  2250. // ShellExecute(hwnd, NULL, szNewGrpPath, NULL, NULL, SW_SHOWNORMAL);
  2251. ShellExecuteEx(&sei);
  2252. }
  2253. // Everything went OK.
  2254. fStatus = TRUE;
  2255. ProcExit:
  2256. #ifdef UNICODE
  2257. CloseHandle(fh);
  2258. #else
  2259. _lclose(fh);
  2260. #endif
  2261. ProcExit2:
  2262. ItemList_Destroy();
  2263. return fStatus;
  2264. }
  2265. //---------------------------------------------------------------------------
  2266. // Record the last write date/time of the given group in the ini file.
  2267. void Group_WriteLastModDateTime(LPCTSTR lpszGroupFile,DWORD dwLowDateTime)
  2268. {
  2269. Reg_SetStruct(g_hkeyGrpConv, c_szGroups, lpszGroupFile, &dwLowDateTime, SIZEOF(dwLowDateTime));
  2270. }
  2271. //---------------------------------------------------------------------------
  2272. // Read the last write date/time of the given group from the ini file.
  2273. DWORD Group_ReadLastModDateTime(LPCTSTR lpszGroupFile)
  2274. {
  2275. DWORD dwDateTime = 0;
  2276. Reg_GetStruct(g_hkeyGrpConv, c_szGroups, lpszGroupFile, &dwDateTime, SIZEOF(dwDateTime));
  2277. return dwDateTime;
  2278. }
  2279. //---------------------------------------------------------------------------
  2280. // Convert the given group to the new format.
  2281. // Returns FALSE if something goes wrong.
  2282. // Returns true if the given group got converted or the user cancelled.
  2283. BOOL Group_Convert(HWND hwnd, LPCTSTR lpszOldGrpFile, UINT options)
  2284. {
  2285. TCHAR szGroupTitle[MAXGROUPNAMELEN + 1]; // PM Groups had a max title len of 30.
  2286. BOOL fStatus;
  2287. WIN32_FIND_DATA fd;
  2288. HANDLE hff;
  2289. UINT nCode;
  2290. UINT iErrorId;
  2291. Log(TEXT("Grp: %s"), lpszOldGrpFile);
  2292. DebugMsg(DM_TRACE, TEXT("gc.gc: Converting group %s"), (LPTSTR) lpszOldGrpFile);
  2293. // Does the group exist?
  2294. if (PathFileExists(lpszOldGrpFile))
  2295. {
  2296. // Group exists - is it valid?
  2297. nCode = Group_ValidOldFormat(lpszOldGrpFile, szGroupTitle);
  2298. switch( nCode )
  2299. {
  2300. case VOF_WINNT:
  2301. case VOF_WIN31:
  2302. // Yes - ask for confirmation.
  2303. if (!(options & GC_PROMPTBEFORECONVERT) ||
  2304. MyMessageBox(hwnd, IDS_APPTITLE, IDS_OKTOCONVERT, szGroupTitle, MB_YESNO) == IDYES)
  2305. {
  2306. // Everything went OK?
  2307. if ( nCode == VOF_WIN31 )
  2308. {
  2309. fStatus = Group_CreateNewFromOld(hwnd,lpszOldGrpFile,
  2310. options);
  2311. }
  2312. else
  2313. {
  2314. fStatus = Group_CreateNewFromOldNT(hwnd,lpszOldGrpFile,
  2315. options);
  2316. }
  2317. if ( fStatus )
  2318. {
  2319. iErrorId = 0;
  2320. }
  2321. else
  2322. {
  2323. // Nope - FU. Warn and exit.
  2324. iErrorId = IDS_CONVERTERROR;
  2325. }
  2326. }
  2327. else
  2328. {
  2329. // User cancelled...
  2330. iErrorId = 0;
  2331. }
  2332. break;
  2333. default:
  2334. case VOF_BAD:
  2335. {
  2336. // Nope, File is invalid.
  2337. // Warn user.
  2338. iErrorId = IDS_NOTGROUPFILE;
  2339. }
  2340. break;
  2341. }
  2342. }
  2343. else
  2344. {
  2345. // Nope, File doesn't even exist.
  2346. iErrorId = IDS_MISSINGFILE;
  2347. }
  2348. if ( iErrorId != 0 )
  2349. {
  2350. if (options & GC_REPORTERROR)
  2351. {
  2352. MyMessageBox(hwnd, IDS_APPTITLE, iErrorId,
  2353. lpszOldGrpFile, MB_OK|MB_ICONEXCLAMATION);
  2354. }
  2355. Log(TEXT("Grp: %s done."), lpszOldGrpFile);
  2356. return FALSE;
  2357. }
  2358. else
  2359. {
  2360. DebugMsg(DM_TRACE, TEXT("gc.gc: Done."));
  2361. Log(TEXT("Grp: %s done."), lpszOldGrpFile);
  2362. return TRUE;
  2363. }
  2364. }
  2365. //---------------------------------------------------------------------------
  2366. // Checks the date/time stamp of the given group against the one in
  2367. // grpconv.ini
  2368. BOOL GroupHasBeenModified(LPCTSTR lpszGroupFile)
  2369. {
  2370. WIN32_FIND_DATA fd;
  2371. HANDLE hff;
  2372. BOOL fModified;
  2373. hff = FindFirstFile(lpszGroupFile, &fd);
  2374. if (hff != INVALID_HANDLE_VALUE)
  2375. {
  2376. if (Group_ReadLastModDateTime(lpszGroupFile) != fd.ftLastWriteTime.dwLowDateTime)
  2377. {
  2378. DebugMsg(DM_TRACE, TEXT("cg.ghbm: Group %s has been modified."), (LPTSTR)lpszGroupFile);
  2379. fModified = TRUE;
  2380. }
  2381. else
  2382. {
  2383. DebugMsg(DM_TRACE, TEXT("cg.ghbm: Group %s has not been modified."), (LPTSTR)lpszGroupFile);
  2384. fModified = FALSE;
  2385. }
  2386. FindClose(hff);
  2387. return fModified;
  2388. }
  2389. else
  2390. {
  2391. // Hmm, file doesn't exist, pretend it's up to date.
  2392. return TRUE;
  2393. }
  2394. }
  2395. //---------------------------------------------------------------------------
  2396. // Converts a group file from its NT registry into a real file on disk. Since
  2397. // the disk format for NT 1.0 files never existed and collided in its usage
  2398. // the GROUP_MAGIC file type, we will convert it from the registry, directly
  2399. // into a GROUP_UNICODE format file. In this way we will always be able to
  2400. // distiguish the NT group files from the Win 3.1 group files.
  2401. BOOL MakeGroupFile( LPTSTR lpFileName, LPTSTR lpGroupName)
  2402. {
  2403. LONG lResult;
  2404. DWORD cbSize;
  2405. HGLOBAL hBuffer;
  2406. HGLOBAL hNew;
  2407. LPBYTE lpBuffer;
  2408. BOOL fOk;
  2409. HANDLE hFile;
  2410. HKEY hkey;
  2411. DWORD cbWrote;
  2412. fOk = FALSE;
  2413. lResult = RegOpenKeyEx(hkeyGroups, lpGroupName, 0,
  2414. KEY_READ, &hkey );
  2415. if ( lResult != ERROR_SUCCESS )
  2416. {
  2417. return FALSE;
  2418. }
  2419. lResult = RegQueryValueEx( hkey, NULL, NULL, NULL, NULL, &cbSize);
  2420. if ( lResult != ERROR_SUCCESS )
  2421. {
  2422. goto CleanupKey;
  2423. }
  2424. hBuffer = GlobalAlloc(GMEM_MOVEABLE,cbSize);
  2425. if ( hBuffer == NULL )
  2426. {
  2427. goto CleanupKey;
  2428. }
  2429. lpBuffer = (LPBYTE)GlobalLock(hBuffer);
  2430. if ( lpBuffer == NULL )
  2431. {
  2432. goto CleanupMem;
  2433. }
  2434. lResult = RegQueryValueEx( hkey, NULL, NULL, NULL,
  2435. lpBuffer, &cbSize );
  2436. if ( lResult != ERROR_SUCCESS )
  2437. {
  2438. goto Cleanup;
  2439. }
  2440. if ( *(DWORD *)lpBuffer == GROUP_MAGIC )
  2441. {
  2442. HGLOBAL hNew;
  2443. cbSize = ConvertToUnicodeGroup( (LPNT_GROUPDEF_A)lpBuffer, &hNew );
  2444. GlobalUnlock( hBuffer );
  2445. GlobalFree( hBuffer );
  2446. hBuffer = hNew;
  2447. lpBuffer = GlobalLock( hBuffer );
  2448. if ( lpBuffer == NULL )
  2449. {
  2450. goto CleanupMem;
  2451. }
  2452. }
  2453. hFile = CreateFile(lpFileName,GENERIC_WRITE,0,NULL,
  2454. CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
  2455. if (hFile != INVALID_HANDLE_VALUE)
  2456. {
  2457. fOk = WriteFile(hFile,lpBuffer,cbSize,&cbWrote,NULL);
  2458. CloseHandle(hFile);
  2459. }
  2460. Cleanup:
  2461. GlobalUnlock(hBuffer);
  2462. CleanupMem:
  2463. GlobalFree(hBuffer);
  2464. CleanupKey:
  2465. RegCloseKey( hkey );
  2466. return fOk;
  2467. }
  2468. #define BIG_STEP 512
  2469. // returns S_OK if allocated a sufficiently large buffer (buffer allocated with LocalAlloc returned)
  2470. // returns E_FAIL if buffer required is larger than BIG_STEP*7 characters (no buffer returned)
  2471. // returns E_OUTOFMEMORY if memory allocation failed (no buffer returned)
  2472. HRESULT GetSufficientlyLargeGroupBuffer(LPTSTR pszIni, LPTSTR* ppszBuffer)
  2473. {
  2474. UINT cchBuffer;
  2475. HRESULT hr = S_FALSE;
  2476. for (cchBuffer = BIG_STEP; (S_FALSE == hr) && (cchBuffer <= BIG_STEP * 7); cchBuffer += BIG_STEP)
  2477. {
  2478. *ppszBuffer = (LPTSTR)LocalAlloc(LPTR, cchBuffer * sizeof(TCHAR));
  2479. if (!*ppszBuffer)
  2480. {
  2481. hr = E_OUTOFMEMORY;
  2482. }
  2483. else
  2484. {
  2485. if ((UINT)GetPrivateProfileString(c_szGroups, NULL, c_szNULL, *ppszBuffer, cchBuffer, pszIni) < (cchBuffer - 5))
  2486. {
  2487. hr = S_OK; // found a big enough buffer, we can stop
  2488. }
  2489. else
  2490. {
  2491. LocalFree((HLOCAL)*ppszBuffer);
  2492. *ppszBuffer = NULL;
  2493. hr = S_FALSE; // continue in the loop
  2494. }
  2495. }
  2496. }
  2497. if (S_FALSE == hr)
  2498. {
  2499. hr = E_FAIL; // make callers using SUCCEEDED happy
  2500. }
  2501. return hr;
  2502. }
  2503. //----------------------------------------------------------------------------
  2504. // Enumerate all the groups or just all the modified groups.
  2505. int Group_Enum(PFNGRPCALLBACK pfncb, BOOL fProgress,
  2506. BOOL fModifiedOnly)
  2507. {
  2508. TCHAR szIniFile[MAX_PATH], szFile[MAX_PATH];
  2509. FILETIME ft;
  2510. UINT uSize;
  2511. LPTSTR pSection, pKey;
  2512. int cGroups = 0;
  2513. HANDLE hFile;
  2514. WIN32_FIND_DATA fd;
  2515. if (!FindProgmanIni(szIniFile))
  2516. return 0;
  2517. if (FAILED(GetSufficientlyLargeGroupBuffer(szIniFile, &pSection)))
  2518. return 0;
  2519. if (fProgress)
  2520. Group_CreateProgressDlg();
  2521. for (pKey = pSection; *pKey; pKey += lstrlen(pKey) + 1)
  2522. {
  2523. GetPrivateProfileString(c_szGroups, pKey, c_szNULL, szFile, ARRAYSIZE(szFile), szIniFile);
  2524. if (szFile[0])
  2525. {
  2526. if (!fModifiedOnly || GroupHasBeenModified(szFile))
  2527. {
  2528. (*pfncb)(szFile);
  2529. cGroups++;
  2530. hFile = FindFirstFile (szFile, &fd);
  2531. if (hFile != INVALID_HANDLE_VALUE) {
  2532. FindClose (hFile);
  2533. Group_WriteLastModDateTime(szFile, fd.ftLastWriteTime.dwLowDateTime);
  2534. }
  2535. }
  2536. }
  2537. }
  2538. // Cabinet uses the date/time of progman.ini as a hint to speed things up
  2539. // so set it here so we won't run automatically again.
  2540. GetSystemTimeAsFileTime(&ft);
  2541. Group_WriteLastModDateTime(szIniFile,ft.dwLowDateTime);
  2542. LocalFree((HLOCAL)pSection);
  2543. if (fProgress)
  2544. Group_DestroyProgressDlg();
  2545. return cGroups;
  2546. }
  2547. //----------------------------------------------------------------------------
  2548. // Enumerate all the NT groups or just all the modified groups.
  2549. int Group_EnumNT(PFNGRPCALLBACK pfncb, BOOL fProgress,
  2550. BOOL fModifiedOnly, HKEY hKeyRoot, LPCTSTR lpKey)
  2551. {
  2552. LONG lResult;
  2553. DWORD dwSubKey = 0;
  2554. TCHAR szGroupName[MAXGROUPNAMELEN+1];
  2555. TCHAR szFileName[MAX_PATH];
  2556. TCHAR szTempFileDir[MAX_PATH];
  2557. TCHAR szTempFileName[MAX_PATH];
  2558. DWORD cchGroupNameLen;
  2559. FILETIME ft;
  2560. BOOL fOk;
  2561. BOOL fDialog = FALSE;
  2562. BOOL fProcess;
  2563. int cGroups = 0;
  2564. //
  2565. // Look for groups in the registry
  2566. //
  2567. lResult = RegOpenKeyEx(hKeyRoot, lpKey, 0,
  2568. KEY_READ, &hkeyGroups );
  2569. if ( lResult != ERROR_SUCCESS )
  2570. {
  2571. return 0;
  2572. }
  2573. while ( TRUE )
  2574. {
  2575. cchGroupNameLen = ARRAYSIZE(szGroupName);
  2576. lResult = RegEnumKeyEx( hkeyGroups, dwSubKey, szGroupName,
  2577. &cchGroupNameLen, NULL, NULL, NULL, &ft );
  2578. szGroupName[MAXGROUPNAMELEN] = TEXT('\0');
  2579. if ( lResult == ERROR_NO_MORE_ITEMS )
  2580. {
  2581. break;
  2582. }
  2583. if ( lResult == ERROR_SUCCESS )
  2584. {
  2585. GetWindowsDirectory(szFileName, ARRAYSIZE(szFileName));
  2586. // Save this dir for use by GetTempFileName below
  2587. lstrcpy(szTempFileDir, szFileName);
  2588. #ifdef WINNT
  2589. GetEnvironmentVariable(TEXT("USERPROFILE"), szTempFileDir, MAX_PATH);
  2590. #endif
  2591. lstrcat(szFileName,TEXT("\\"));
  2592. lstrcat(szFileName,szGroupName);
  2593. lstrcat(szFileName,TEXT(".grp"));
  2594. //
  2595. // If the key has been modified since we last processed it,
  2596. // then time to process it again.
  2597. //
  2598. fProcess = FALSE;
  2599. if (fModifiedOnly)
  2600. {
  2601. if ( Group_ReadLastModDateTime(szFileName) != ft.dwLowDateTime )
  2602. {
  2603. fProcess = TRUE;
  2604. }
  2605. }
  2606. else
  2607. {
  2608. fProcess = TRUE;
  2609. }
  2610. if (fProcess)
  2611. {
  2612. if (GetTempFileName(szTempFileDir,TEXT("grp"),0,szTempFileName) != 0)
  2613. {
  2614. fOk = MakeGroupFile(szTempFileName,szGroupName);
  2615. if ( fOk )
  2616. {
  2617. if (fProgress && !fDialog)
  2618. {
  2619. Group_CreateProgressDlg();
  2620. fDialog = TRUE;
  2621. }
  2622. (*pfncb)(szTempFileName);
  2623. DeleteFile(szTempFileName);
  2624. Group_WriteLastModDateTime(szFileName,ft.dwLowDateTime);
  2625. cGroups++;
  2626. }
  2627. }
  2628. }
  2629. }
  2630. dwSubKey++;
  2631. }
  2632. RegCloseKey( hkeyGroups );
  2633. hkeyGroups = NULL;
  2634. if (fProgress && fDialog)
  2635. Group_DestroyProgressDlg();
  2636. return cGroups;
  2637. }
  2638. //---------------------------------------------------------------------------
  2639. // Find the progman ini from before an upgrade.
  2640. BOOL FindOldProgmanIni(LPTSTR pszPath)
  2641. {
  2642. if (Reg_GetString(HKEY_LOCAL_MACHINE, REGSTR_PATH_SETUP, REGSTR_VAL_OLDWINDIR, pszPath, MAX_PATH*SIZEOF(TCHAR)))
  2643. {
  2644. PathAppend(pszPath, c_szProgmanIni);
  2645. if (PathFileExists(pszPath))
  2646. {
  2647. return TRUE;
  2648. }
  2649. DebugMsg(DM_ERROR, TEXT("Can't find old progman.ini"));
  2650. return FALSE;
  2651. }
  2652. return FALSE;
  2653. }
  2654. //----------------------------------------------------------------------------
  2655. // Enumerate all the old groups.
  2656. void Group_EnumOldGroups(PFNGRPCALLBACK pfncb, BOOL fProgress)
  2657. {
  2658. TCHAR szIniFile[MAX_PATH], szFile[MAX_PATH];
  2659. UINT uSize;
  2660. LPTSTR pSection, pKey;
  2661. if (!FindOldProgmanIni(szIniFile))
  2662. return;
  2663. if (FAILED(GetSufficientlyLargeGroupBuffer(szIniFile, &pSection)))
  2664. return;
  2665. if (fProgress)
  2666. Group_CreateProgressDlg();
  2667. for (pKey = pSection; *pKey; pKey += lstrlen(pKey) + 1)
  2668. {
  2669. GetPrivateProfileString(c_szGroups, pKey, c_szNULL, szFile, ARRAYSIZE(szFile), szIniFile);
  2670. if (szFile[0])
  2671. {
  2672. (*pfncb)(szFile);
  2673. }
  2674. }
  2675. if (fProgress)
  2676. Group_DestroyProgressDlg();
  2677. LocalFree((HLOCAL)pSection);
  2678. }
  2679. //----------------------------------------------------------------------------
  2680. // Given a pidl for a link, extract the appropriate info and append it to
  2681. // the app list.
  2682. void AppList_AppendCurrentItem(LPITEMIDLIST pidlFolder, LPSHELLFOLDER psf,
  2683. LPITEMIDLIST pidlItem, IShellLink *psl, IPersistFile *ppf)
  2684. {
  2685. STRRET str;
  2686. WCHAR wszPath[MAX_PATH];
  2687. TCHAR szName[MAX_PATH];
  2688. TCHAR sz[MAX_PATH];
  2689. TCHAR szPath[MAX_PATH];
  2690. TCHAR szModule[MAX_PATH];
  2691. TCHAR szVer[MAX_PATH];
  2692. ALITEM alitem;
  2693. if (SUCCEEDED(psf->lpVtbl->GetDisplayNameOf(psf, pidlItem, SHGDN_NORMAL, &str)))
  2694. {
  2695. // Get the name.
  2696. StrRetToStrN(szName, ARRAYSIZE(szName), &str, pidlItem);
  2697. DebugMsg(DM_TRACE, TEXT("c.gi_gi: Link %s"), szName);
  2698. // Get the path from the link...
  2699. SHGetPathFromIDList(pidlFolder, sz);
  2700. PathAppend(sz, szName);
  2701. lstrcat(sz, TEXT(".lnk"));
  2702. StrToOleStrN(wszPath, ARRAYSIZE(wszPath), sz, -1);
  2703. ppf->lpVtbl->Load(ppf, wszPath, 0);
  2704. // Copy all the data.
  2705. szPath[0] = TEXT('\0');
  2706. if (SUCCEEDED(psl->lpVtbl->GetPath(psl, szPath, ARRAYSIZE(szPath), NULL, SLGP_SHORTPATH)))
  2707. {
  2708. // Valid CL?
  2709. if (szPath[0])
  2710. {
  2711. GetVersionInfo(szPath, szModule, ARRAYSIZE(szModule), szVer, ARRAYSIZE(szVer));
  2712. alitem.pszName = NULL;
  2713. alitem.pszPath = NULL;
  2714. alitem.pszModule = NULL;
  2715. alitem.pszVer = NULL;
  2716. Str_SetPtr(&alitem.pszName, szName);
  2717. Str_SetPtr(&alitem.pszPath, szPath);
  2718. Str_SetPtr(&alitem.pszModule, szModule);
  2719. Str_SetPtr(&alitem.pszVer, szVer);
  2720. DSA_AppendItem(g_hdsaAppList, &alitem);
  2721. }
  2722. }
  2723. }
  2724. }
  2725. //----------------------------------------------------------------------------
  2726. HRESULT AppList_ShellFolderEnum(LPITEMIDLIST pidlFolder, LPSHELLFOLDER psf)
  2727. {
  2728. HRESULT hres;
  2729. LPENUMIDLIST penum;
  2730. IShellLink *psl;
  2731. LPITEMIDLIST pidlItem;
  2732. UINT celt;
  2733. IPersistFile *ppf;
  2734. DWORD dwAttribs;
  2735. LPSHELLFOLDER psfItem;
  2736. LPITEMIDLIST pidlPath;
  2737. DebugMsg(DM_TRACE, TEXT("gc.al_sfe: Enum..."));
  2738. hres = psf->lpVtbl->EnumObjects(psf, (HWND)NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &penum);
  2739. if (SUCCEEDED(hres))
  2740. {
  2741. hres = ICoCreateInstance(&CLSID_ShellLink, &IID_IShellLink, &psl);
  2742. if (SUCCEEDED(hres))
  2743. {
  2744. psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, &ppf);
  2745. while ((penum->lpVtbl->Next(penum, 1, &pidlItem, &celt) == NOERROR) && (celt == 1))
  2746. {
  2747. dwAttribs = SFGAO_LINK|SFGAO_FOLDER;
  2748. if (SUCCEEDED(psf->lpVtbl->GetAttributesOf(psf, 1, &pidlItem, &dwAttribs)))
  2749. {
  2750. // Is it a folder
  2751. if (dwAttribs & SFGAO_FOLDER)
  2752. {
  2753. // Recurse.
  2754. DebugMsg(DM_TRACE, TEXT("al_sfe: Folder."));
  2755. hres = psf->lpVtbl->BindToObject(psf, pidlItem, NULL, &IID_IShellFolder, &psfItem);
  2756. if (SUCCEEDED(hres))
  2757. {
  2758. pidlPath = ILCombine(pidlFolder, pidlItem);
  2759. if (pidlPath)
  2760. {
  2761. AppList_ShellFolderEnum(pidlPath, psfItem);
  2762. psfItem->lpVtbl->Release(psfItem);
  2763. ILFree(pidlPath);
  2764. }
  2765. }
  2766. }
  2767. else if (dwAttribs & SFGAO_LINK)
  2768. {
  2769. // Regular link, add it to the list.
  2770. DebugMsg(DM_TRACE, TEXT("al_sfe: Link."));
  2771. AppList_AppendCurrentItem(pidlFolder, psf, pidlItem, psl, ppf);
  2772. }
  2773. }
  2774. SHFree(pidlItem);
  2775. }
  2776. ppf->lpVtbl->Release(ppf);
  2777. psl->lpVtbl->Release(psl);
  2778. }
  2779. penum->lpVtbl->Release(penum);
  2780. }
  2781. return hres;
  2782. }
  2783. //----------------------------------------------------------------------------
  2784. void Applist_SpecialFolderEnum(int nFolder)
  2785. {
  2786. HRESULT hres;
  2787. LPITEMIDLIST pidlGroup;
  2788. LPSHELLFOLDER psf, psfDesktop;
  2789. TCHAR sz[MAX_PATH];
  2790. // Get the group info.
  2791. if (SHGetSpecialFolderPath(NULL, sz, nFolder, FALSE))
  2792. {
  2793. pidlGroup = ILCreateFromPath(sz);
  2794. if (pidlGroup)
  2795. {
  2796. if (SUCCEEDED(ICoCreateInstance(&CLSID_ShellDesktop, &IID_IShellFolder, &psfDesktop)))
  2797. {
  2798. hres = psfDesktop->lpVtbl->BindToObject(psfDesktop, pidlGroup, NULL, &IID_IShellFolder, &psf);
  2799. if (SUCCEEDED(hres))
  2800. {
  2801. hres = AppList_ShellFolderEnum(pidlGroup, psf);
  2802. psf->lpVtbl->Release(psf);
  2803. }
  2804. psfDesktop->lpVtbl->Release(psfDesktop);
  2805. }
  2806. else
  2807. {
  2808. DebugMsg(DM_ERROR, TEXT("OneTree: failed to bind to Desktop root"));
  2809. }
  2810. ILFree(pidlGroup);
  2811. }
  2812. else
  2813. {
  2814. DebugMsg(DM_ERROR, TEXT("gc.al_acs: Can't create IDList for path.."));
  2815. }
  2816. }
  2817. else
  2818. {
  2819. DebugMsg(DM_ERROR, TEXT("gc.al_acs: Can't find programs folder."));
  2820. }
  2821. }
  2822. BOOL StartMenuIsProgramsParent(void)
  2823. {
  2824. LPITEMIDLIST pidlStart, pidlProgs;
  2825. BOOL fParent = FALSE;
  2826. if (SHGetSpecialFolderLocation(NULL, CSIDL_STARTMENU, &pidlStart))
  2827. {
  2828. if (SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAMS, &pidlProgs))
  2829. {
  2830. if (ILIsParent(pidlStart, pidlProgs, FALSE))
  2831. fParent = TRUE;
  2832. ILFree(pidlProgs);
  2833. }
  2834. ILFree(pidlStart);
  2835. }
  2836. return fParent;
  2837. }
  2838. //---------------------------------------------------------------------------
  2839. // Return the links in a group.
  2840. void AppList_AddCurrentStuff(void)
  2841. {
  2842. DebugMsg(DM_TRACE, TEXT("gc.al_acs: Enumerating everything..."));
  2843. DebugMsg(DM_TRACE, TEXT("gc.al_acs: Enumerating StartMenu..."));
  2844. Applist_SpecialFolderEnum(CSIDL_STARTMENU);
  2845. if (!StartMenuIsProgramsParent())
  2846. {
  2847. DebugMsg(DM_TRACE, TEXT("gc.al_acs: Enumerating Programs..."));
  2848. Applist_SpecialFolderEnum(CSIDL_PROGRAMS);
  2849. }
  2850. }
  2851. // On NT we plan on converting NT formated group files into folders and links
  2852. // therefore we need the ability of supporting all of the NT group file formats
  2853. /*--------------------------------------------------------------------------*/
  2854. /* */
  2855. /* SIZEOFGroup() - */
  2856. /* */
  2857. /*--------------------------------------------------------------------------*/
  2858. DWORD SizeofGroup(LPNT_GROUPDEF lpgd)
  2859. {
  2860. LPNT_PMTAG lptag;
  2861. DWORD cbSeg;
  2862. DWORD cb;
  2863. cbSeg = (DWORD)GlobalSize(lpgd);
  2864. // The following needs to be verified
  2865. lptag = (LPNT_PMTAG)((LPSTR)lpgd+lpgd->cbGroup);
  2866. if ((DWORD)((PCHAR)lptag - (PCHAR)lpgd +MyDwordAlign(SIZEOF(NT_PMTAG))-MyDwordAlign(SIZEOF(lptag->rgb))+4) <= cbSeg
  2867. && lptag->wID == ID_MAGIC
  2868. && lptag->wItem == (int)0xFFFF
  2869. && lptag->cb == (WORD)(MyDwordAlign(SIZEOF(NT_PMTAG))-MyDwordAlign(SIZEOF(lptag->rgb)) + 4)
  2870. && *(PLONG)lptag->rgb == TAG_MAGIC)
  2871. {
  2872. while ((cb = (DWORD)((PCHAR)lptag - (PCHAR)lpgd + MyDwordAlign(SIZEOF(NT_PMTAG))-MyDwordAlign(SIZEOF(lptag->rgb)))) <= cbSeg)
  2873. {
  2874. if (lptag->wID == ID_LASTTAG)
  2875. return cb;
  2876. (LPSTR)lptag += lptag->cb;
  2877. }
  2878. }
  2879. return lpgd->cbGroup;
  2880. }
  2881. /*--------------------------------------------------------------------------*/
  2882. /* */
  2883. /* FindTag() - */
  2884. /* */
  2885. /*--------------------------------------------------------------------------*/
  2886. LPNT_PMTAG FindTag(LPNT_GROUPDEF lpgd, int item, WORD id)
  2887. {
  2888. LPNT_PMTAG lptag;
  2889. DWORD cbSeg;
  2890. DWORD cb;
  2891. cbSeg = (DWORD)GlobalSize(lpgd);
  2892. lptag = (LPNT_PMTAG)((LPSTR)lpgd+lpgd->cbGroup);
  2893. if ((PCHAR)lptag - (PCHAR)lpgd + MyDwordAlign(SIZEOF(NT_PMTAG))-MyDwordAlign(SIZEOF(lptag->rgb)) + 4 <= cbSeg
  2894. && lptag->wID == ID_MAGIC
  2895. && lptag->wItem == (int)0xFFFF
  2896. && lptag->cb == (WORD)(MyDwordAlign(SIZEOF(NT_PMTAG))-MyDwordAlign(SIZEOF(lptag->rgb)) +4)
  2897. && *(LONG *)lptag->rgb == TAG_MAGIC) {
  2898. while ((cb = (DWORD)((PCHAR)lptag - (PCHAR)lpgd + MyDwordAlign(SIZEOF(NT_PMTAG))-MyDwordAlign(SIZEOF(lptag->rgb)))) <= cbSeg)
  2899. {
  2900. if ((item == lptag->wItem)
  2901. && (id == 0 || id == lptag->wID)) {
  2902. return lptag;
  2903. }
  2904. if (lptag->wID == ID_LASTTAG)
  2905. return NULL;
  2906. (LPSTR)lptag += lptag->cb;
  2907. }
  2908. }
  2909. return NULL;
  2910. }
  2911. /*--------------------------------------------------------------------------*/
  2912. /* */
  2913. /* DeleteTag() - */
  2914. /* */
  2915. /* in: */
  2916. /* hGroup group handle, can be discardable (alwayws shrink object) */
  2917. /* */
  2918. /*--------------------------------------------------------------------------*/
  2919. VOID DeleteTag(HANDLE hGroup, int item, WORD id)
  2920. {
  2921. LPNT_PMTAG lptag;
  2922. LPWSTR lp1, lp2;
  2923. LPWSTR lpend;
  2924. LPNT_GROUPDEF lpgd;
  2925. lpgd = (LPNT_GROUPDEF) GlobalLock(hGroup);
  2926. lptag = FindTag(lpgd,item,id);
  2927. if (lptag == NULL) {
  2928. GlobalUnlock(hGroup);
  2929. return;
  2930. }
  2931. lp1 = (LPWSTR)lptag;
  2932. lp2 = (LPWSTR)((LPSTR)lptag + lptag->cb);
  2933. lpend = (LPWSTR)((LPSTR)lpgd + SizeofGroup(lpgd));
  2934. while (lp2 < lpend) {
  2935. *lp1++ = *lp2++;
  2936. }
  2937. /* always reallocing smaller
  2938. */
  2939. GlobalUnlock(hGroup);
  2940. GlobalReAlloc(hGroup, (DWORD)((LPSTR)lp1 - (LPSTR)lpgd), 0);
  2941. return;
  2942. }
  2943. /*--------------------------------------------------------------------------*/
  2944. /* */
  2945. /* AddTag() - */
  2946. /* */
  2947. /* in: */
  2948. /* h group handle, must not be discardable! */
  2949. /* */
  2950. /* returns: */
  2951. /* 0 failure */
  2952. /* 1 success */
  2953. /*--------------------------------------------------------------------------*/
  2954. INT AddTag(HANDLE h, int item, WORD id, LPWSTR lpbuf, UINT cb)
  2955. {
  2956. LPNT_PMTAG lptag;
  2957. WORD fAddFirst;
  2958. LPNT_GROUPDEF lpgd;
  2959. int cbNew;
  2960. int cbMyLen;
  2961. LPNT_GROUPDEF lpgdOld;
  2962. if (!cb && lpbuf) {
  2963. cb = SIZEOF(WCHAR)*(lstrlenW(lpbuf) + 1);
  2964. }
  2965. cbMyLen = MyDwordAlign(cb);
  2966. if (!lpbuf) {
  2967. cb = 0;
  2968. cbMyLen = 0;
  2969. }
  2970. /*
  2971. * Remove the old version of the tag, if any.
  2972. */
  2973. DeleteTag(h, item, id);
  2974. lpgd = (LPNT_GROUPDEF)GlobalLock(h);
  2975. lptag = FindTag(lpgd, (int)0xFFFF, (WORD)ID_LASTTAG);
  2976. if (!lptag) {
  2977. /*
  2978. * In this case, there are no tags at all, and we have to add
  2979. * the first tag, the interesting tag, and the last tag
  2980. */
  2981. cbNew = 3 * (MyDwordAlign(SIZEOF(NT_PMTAG)) - MyDwordAlign(SIZEOF(lptag->rgb))) + 4 + cbMyLen;
  2982. fAddFirst = TRUE;
  2983. lptag = (LPNT_PMTAG)((LPSTR)lpgd + lpgd->cbGroup);
  2984. } else {
  2985. /*
  2986. * In this case, only the interesting tag needs to be added
  2987. * but we count in the last because the delta is from lptag
  2988. */
  2989. cbNew = 2 * (MyDwordAlign(SIZEOF(NT_PMTAG)) - MyDwordAlign(SIZEOF(lptag->rgb))) + cbMyLen;
  2990. fAddFirst = FALSE;
  2991. }
  2992. /*
  2993. * check for 64K limit
  2994. */
  2995. if ((DWORD_PTR)lptag + cbNew < (DWORD_PTR)lptag) {
  2996. return 0;
  2997. }
  2998. cbNew += (DWORD)((PCHAR)lptag -(PCHAR)lpgd);
  2999. lpgdOld = lpgd;
  3000. GlobalUnlock(h);
  3001. if (!GlobalReAlloc(h, (DWORD)cbNew, GMEM_MOVEABLE)) {
  3002. return 0;
  3003. }
  3004. lpgd = (LPNT_GROUPDEF)GlobalLock(h);
  3005. lptag = (LPNT_PMTAG)((LPSTR)lpgd + ((LPSTR)lptag - (LPSTR)lpgdOld));
  3006. if (fAddFirst) {
  3007. /*
  3008. * Add the first tag
  3009. */
  3010. lptag->wID = ID_MAGIC;
  3011. lptag->wItem = (int)0xFFFF;
  3012. *(LONG *)lptag->rgb = TAG_MAGIC;
  3013. lptag->cb = (WORD)(MyDwordAlign(SIZEOF(NT_PMTAG)) - MyDwordAlign(SIZEOF(lptag->rgb)) + 4);
  3014. (LPSTR)lptag += lptag->cb;
  3015. }
  3016. /*
  3017. * Add the tag
  3018. */
  3019. lptag->wID = id;
  3020. lptag->wItem = item;
  3021. lptag->cb = (WORD)(MyDwordAlign(SIZEOF(NT_PMTAG)) - MyDwordAlign(SIZEOF(lptag->rgb)) + cbMyLen);
  3022. if (lpbuf) {
  3023. memmove(lptag->rgb, lpbuf, (WORD)cb);
  3024. }
  3025. (LPSTR)lptag += lptag->cb;
  3026. /*
  3027. * Add the end tag
  3028. */
  3029. lptag->wID = ID_LASTTAG;
  3030. lptag->wItem = (int)0xFFFF;
  3031. lptag->cb = 0;
  3032. GlobalUnlock(h);
  3033. return 1;
  3034. }
  3035. /*--------------------------------------------------------------------------*/
  3036. /* */
  3037. /* CreateNewGroupFromAnsiGroup() - */
  3038. /* */
  3039. /* This function creates a new, empty group. */
  3040. /* */
  3041. /*--------------------------------------------------------------------------*/
  3042. HANDLE CreateNewGroupFromAnsiGroup(LPNT_GROUPDEF_A lpGroupORI)
  3043. {
  3044. HANDLE hT;
  3045. LPNT_GROUPDEF lpgd;
  3046. int i;
  3047. int cb;
  3048. int cItems; // number of items in 16bit group
  3049. LPSTR pGroupName; // 32bit group name
  3050. LPWSTR pGroupNameUNI = NULL; // 32bit UNICODE group name
  3051. UINT wGroupNameLen; // length of pGroupName DWORD aligned.
  3052. INT cchWideChar = 0; //character count of resultant unicode string
  3053. INT cchMultiByte = 0;
  3054. pGroupName = (LPSTR)PTR(lpGroupORI, lpGroupORI->pName);
  3055. //
  3056. // convert pGroupName to unicode here
  3057. //
  3058. cchMultiByte=MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,pGroupName,
  3059. -1,pGroupNameUNI,cchWideChar) ;
  3060. pGroupNameUNI = LocalAlloc(LPTR,(++cchMultiByte)*SIZEOF(WCHAR)) ;
  3061. if (NULL == pGroupNameUNI)
  3062. goto Exit;
  3063. MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,pGroupName,
  3064. -1,pGroupNameUNI,cchMultiByte) ;
  3065. wGroupNameLen = MyDwordAlign(SIZEOF(WCHAR)*(lstrlenW(pGroupNameUNI) + 1));
  3066. cItems = lpGroupORI->cItems;
  3067. cb = SIZEOF(NT_GROUPDEF) + (cItems * SIZEOF(DWORD)) + wGroupNameLen;
  3068. //
  3069. // In CreateNewGroup before GlobalAlloc.
  3070. //
  3071. hT = GlobalAlloc(GHND, (DWORD)cb);
  3072. if (!hT) {
  3073. LocalFree((HLOCAL)pGroupNameUNI);
  3074. goto Exit;
  3075. }
  3076. lpgd = (LPNT_GROUPDEF)GlobalLock(hT);
  3077. //
  3078. // use the NT 1.0 group settings for what we can.
  3079. //
  3080. lpgd->nCmdShow = lpGroupORI->nCmdShow;
  3081. lpgd->wIconFormat = lpGroupORI->wIconFormat;
  3082. lpgd->cxIcon = lpGroupORI->cxIcon;
  3083. lpgd->cyIcon = lpGroupORI->cyIcon;
  3084. lpgd->ptMin.x = (INT)lpGroupORI->ptMin.x;
  3085. lpgd->ptMin.y = (INT)lpGroupORI->ptMin.y;
  3086. CopyRect(&(lpgd->rcNormal),&(lpGroupORI->rcNormal));
  3087. lpgd->dwMagic = GROUP_UNICODE;
  3088. lpgd->cbGroup = (DWORD)cb;
  3089. lpgd->pName = SIZEOF(NT_GROUPDEF) + cItems * SIZEOF(DWORD);
  3090. lpgd->Reserved1 = (WORD)-1;
  3091. lpgd->Reserved2 = (DWORD)-1;
  3092. lpgd->cItems = (WORD)cItems;
  3093. for (i = 0; i < cItems; i++) {
  3094. lpgd->rgiItems[i] = 0;
  3095. }
  3096. lstrcpyW((LPWSTR)((LPBYTE)lpgd + SIZEOF(NT_GROUPDEF) + cItems * SIZEOF(DWORD)),
  3097. pGroupNameUNI); // lhb tracks
  3098. LocalFree((HLOCAL)pGroupNameUNI);
  3099. GlobalUnlock(hT);
  3100. return(hT);
  3101. Exit:
  3102. return NULL;
  3103. }
  3104. /*--------------------------------------------------------------------------*/
  3105. /* */
  3106. /* AddThing() - */
  3107. /* */
  3108. /* in: */
  3109. /* hGroup group handle, must not be discardable */
  3110. /* lpStuff pointer to data or NULL to init data to zero */
  3111. /* cbStuff count of item (may be 0) if lpStuff is a string */
  3112. /* */
  3113. /* Adds an object to the group segment and returns its offset. Will */
  3114. /* reallocate the segment if necessary. */
  3115. /* */
  3116. /* Handle passed in must not be discardable */
  3117. /* */
  3118. /* returns: */
  3119. /* 0 failure */
  3120. /* > 0 offset to thing in the segment */
  3121. /* */
  3122. /*--------------------------------------------------------------------------*/
  3123. DWORD AddThing(HANDLE hGroup, LPWSTR lpStuff, DWORD cbStuff)
  3124. {
  3125. DWORD cb;
  3126. LPNT_GROUPDEF lpgd;
  3127. DWORD offset;
  3128. LPWSTR lpT;
  3129. DWORD cbStuffSize;
  3130. DWORD cbGroupSize;
  3131. DWORD myOffset;
  3132. if (cbStuff == 0xFFFFFFFF) {
  3133. return 0xFFFFFFFF;
  3134. }
  3135. if (!cbStuff) {
  3136. cbStuff = SIZEOF(WCHAR)*(DWORD)(1 + lstrlenW(lpStuff));
  3137. }
  3138. cbStuffSize = MyDwordAlign((int)cbStuff);
  3139. lpgd = (LPNT_GROUPDEF)GlobalLock(hGroup);
  3140. cb = SizeofGroup(lpgd);
  3141. cbGroupSize = MyDwordAlign((int)cb);
  3142. offset = lpgd->cbGroup;
  3143. myOffset = (DWORD)MyDwordAlign((int)offset);
  3144. GlobalUnlock(hGroup);
  3145. if (!GlobalReAlloc(hGroup,(DWORD)(cbGroupSize + cbStuffSize), GMEM_MOVEABLE))
  3146. return 0;
  3147. lpgd = (LPNT_GROUPDEF)GlobalLock(hGroup);
  3148. /*
  3149. * Slide the tags up
  3150. */
  3151. memmove((LPSTR)lpgd + myOffset + cbStuffSize, (LPSTR)lpgd + myOffset,
  3152. (cbGroupSize - myOffset));
  3153. lpgd->cbGroup += cbStuffSize;
  3154. lpT = (LPWSTR)((LPSTR)lpgd + myOffset);
  3155. if (lpStuff) {
  3156. memcpy(lpT, lpStuff, cbStuff);
  3157. } else {
  3158. /*
  3159. * Zero it
  3160. */
  3161. while (cbStuffSize--) {
  3162. *((LPBYTE)lpT)++ = 0;
  3163. }
  3164. }
  3165. GlobalUnlock(hGroup);
  3166. return myOffset;
  3167. }
  3168. DWORD AddThing_A(HANDLE hGroup, LPSTR lpStuff, WORD cbStuff)
  3169. {
  3170. LPWSTR lpStuffUNI = NULL;
  3171. BOOL bAlloc = FALSE;
  3172. DWORD cb;
  3173. if (cbStuff == 0xFFFF) {
  3174. return 0xFFFF;
  3175. }
  3176. if (!cbStuff) {
  3177. INT cchMultiByte;
  3178. INT cchWideChar = 0;
  3179. bAlloc = TRUE;
  3180. cchMultiByte=MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,lpStuff,
  3181. -1,lpStuffUNI,cchWideChar) ;
  3182. lpStuffUNI = LocalAlloc(LPTR,(++cchMultiByte)*SIZEOF(WCHAR)) ;
  3183. if (lpStuffUNI)
  3184. {
  3185. MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,lpStuff,
  3186. -1,lpStuffUNI,cchMultiByte) ;
  3187. cbStuff = (WORD)SIZEOF(WCHAR)*(1 + lstrlenW(lpStuffUNI)); // lhb tracks
  3188. }
  3189. } else {
  3190. lpStuffUNI = (LPWSTR)lpStuff;
  3191. }
  3192. if (lpStuffUNI)
  3193. {
  3194. cb = AddThing(hGroup, lpStuffUNI, cbStuff);
  3195. if (bAlloc)
  3196. LocalFree(lpStuffUNI);
  3197. }
  3198. else
  3199. {
  3200. cb = 0;
  3201. }
  3202. return(cb);
  3203. }
  3204. /*--------------------------------------------------------------------------*/
  3205. /* */
  3206. /* ConvertToUnicodeGroup() - */
  3207. /* */
  3208. /* returns the size of the new unicode group. */
  3209. /* */
  3210. /*--------------------------------------------------------------------------*/
  3211. int ConvertToUnicodeGroup(LPNT_GROUPDEF_A lpGroupORI, LPHANDLE lphNewGroup)
  3212. {
  3213. HANDLE hNewGroup;
  3214. LPNT_GROUPDEF lpgd;
  3215. LPNT_ITEMDEF lpid;
  3216. LPBYTE lpid_A;
  3217. LPNT_PMTAG lptag_A;
  3218. LPSTR lpTagValue;
  3219. WORD wTagId;
  3220. LPSTR lpT;
  3221. DWORD offset;
  3222. int cb;
  3223. int i;
  3224. INT cchMultiByte;
  3225. INT cchWideChar;
  3226. LPWSTR lpTagValueUNI;
  3227. BOOL bAlloc = FALSE;
  3228. hNewGroup = CreateNewGroupFromAnsiGroup(lpGroupORI);
  3229. if (!hNewGroup) {
  3230. return(0);
  3231. }
  3232. //
  3233. // Add all items to the new formatted group.
  3234. //
  3235. for (i = 0; i < (int)lpGroupORI->cItems; i++) {
  3236. //
  3237. // Get the pointer to the 16bit item
  3238. //
  3239. lpid_A = (LPBYTE)ITEM(lpGroupORI, i);
  3240. if (lpGroupORI->rgiItems[i]) {
  3241. //
  3242. // Create the item.
  3243. //
  3244. offset = AddThing(hNewGroup, NULL, SIZEOF(NT_ITEMDEF));
  3245. if (!offset) {
  3246. DebugMsg(DM_ERROR, TEXT("gc.ctug: AddThing NT_ITEMDEF failed"));
  3247. goto QuitThis;
  3248. }
  3249. lpgd = (LPNT_GROUPDEF)GlobalLock(hNewGroup);
  3250. lpgd->rgiItems[i] = offset;
  3251. lpid = ITEM(lpgd, i);
  3252. //
  3253. // Set the item's position.
  3254. //
  3255. lpid->pt.x = ((LPNT_ITEMDEF_A)lpid_A)->pt.x;
  3256. lpid->pt.y = ((LPNT_ITEMDEF_A)lpid_A)->pt.y;
  3257. //
  3258. // Add the item's Name.
  3259. //
  3260. GlobalUnlock(hNewGroup);
  3261. lpT = (LPSTR)PTR(lpGroupORI,((LPNT_ITEMDEF_A)lpid_A)->pName);
  3262. offset = AddThing_A(hNewGroup, lpT, 0);
  3263. if (!offset) {
  3264. DebugMsg(DM_ERROR, TEXT("gc.ctug: AddThing pName failed"));
  3265. goto PuntCreation;
  3266. }
  3267. lpgd = (LPNT_GROUPDEF)GlobalLock(hNewGroup);
  3268. lpid = ITEM(lpgd, i);
  3269. lpid->pName = offset;
  3270. //
  3271. // Add the item's Command line.
  3272. //
  3273. GlobalUnlock(hNewGroup);
  3274. lpT = (LPSTR)PTR(lpGroupORI, ((LPNT_ITEMDEF_A)lpid_A)->pCommand);
  3275. offset = AddThing_A(hNewGroup, lpT, 0);
  3276. if (!offset) {
  3277. DebugMsg(DM_ERROR, TEXT("gc.ctug: AddThing pCommand failed"));
  3278. goto PuntCreation;
  3279. }
  3280. lpgd = (LPNT_GROUPDEF)GlobalLock(hNewGroup);
  3281. lpid = ITEM(lpgd, i);
  3282. lpid->pCommand = offset;
  3283. //
  3284. // Add the item's Icon path.
  3285. //
  3286. GlobalUnlock(hNewGroup);
  3287. lpT = (LPSTR)PTR(lpGroupORI, ((LPNT_ITEMDEF_A)lpid_A)->pIconPath);
  3288. offset = AddThing_A(hNewGroup, lpT, 0);
  3289. if (!offset) {
  3290. DebugMsg(DM_ERROR, TEXT("gc.ctug: AddThing pIconPath failed"));
  3291. goto PuntCreation;
  3292. }
  3293. lpgd = (LPNT_GROUPDEF)GlobalLock(hNewGroup);
  3294. lpid = ITEM(lpgd, i);
  3295. lpid->pIconPath = offset;
  3296. //
  3297. // Get the item's icon resource using the Icon path and the icon index.
  3298. // And add the item's Icon resource.
  3299. //
  3300. lpid->iIcon = ((LPNT_ITEMDEF_A)lpid_A)->idIcon;
  3301. lpid->cbIconRes = ((LPNT_ITEMDEF_A)lpid_A)->cbIconRes;
  3302. lpid->wIconVer = ((LPNT_ITEMDEF_A)lpid_A)->wIconVer;
  3303. GlobalUnlock(hNewGroup);
  3304. lpT = (LPBYTE)PTR(lpGroupORI, ((LPNT_ITEMDEF_A)lpid_A)->pIconRes);
  3305. offset = AddThing_A(hNewGroup, (LPSTR)lpT, lpid->cbIconRes);
  3306. if (!offset) {
  3307. DebugMsg(DM_ERROR, TEXT("gc.ctug: AddThing pIconRes failed"));
  3308. goto PuntCreation;
  3309. }
  3310. lpgd = (LPNT_GROUPDEF)GlobalLock(hNewGroup);
  3311. lpid = ITEM(lpgd, i);
  3312. lpid->pIconRes = offset;
  3313. GlobalUnlock(hNewGroup);
  3314. }
  3315. }
  3316. /*
  3317. * Copy all the tags to the new group format.
  3318. */
  3319. lptag_A = (LPNT_PMTAG)((LPSTR)lpGroupORI + lpGroupORI->cbGroup); // lhb tracks
  3320. if (lptag_A->wID == ID_MAGIC &&
  3321. lptag_A->wItem == (int)0xFFFF &&
  3322. *(LONG *)lptag_A->rgb == TAG_MAGIC) {
  3323. //
  3324. // This is the first tag id, goto start of item tags.
  3325. //
  3326. (LPBYTE)lptag_A += lptag_A->cb;
  3327. while (lptag_A->wID != ID_LASTTAG) {
  3328. wTagId = lptag_A->wID;
  3329. cb = lptag_A->cb - (3 * SIZEOF(DWORD)); // cb - sizeof tag
  3330. if (wTagId == ID_MINIMIZE) {
  3331. lpTagValueUNI = NULL;
  3332. }
  3333. else {
  3334. lpTagValue = lptag_A->rgb ;
  3335. if (wTagId != ID_HOTKEY) {
  3336. bAlloc = TRUE;
  3337. cchWideChar = 0;
  3338. cchMultiByte=MultiByteToWideChar(CP_ACP,
  3339. MB_PRECOMPOSED,lpTagValue,
  3340. -1,NULL,cchWideChar) ;
  3341. lpTagValueUNI = LocalAlloc(LPTR,(++cchMultiByte)*SIZEOF(WCHAR)) ;
  3342. MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,lpTagValue,
  3343. -1,lpTagValueUNI,cchMultiByte) ;
  3344. cb = SIZEOF(WCHAR)*(lstrlenW(lpTagValueUNI) + 1); // lhb tracks
  3345. }
  3346. else {
  3347. lpTagValueUNI = (LPWSTR)lpTagValue;
  3348. }
  3349. }
  3350. if (! AddTag( hNewGroup,
  3351. lptag_A->wItem, // wItem
  3352. wTagId, // wID
  3353. lpTagValueUNI, // rgb : tag value
  3354. cb
  3355. )) {
  3356. DebugMsg(DM_ERROR, TEXT("gc.ctug: AddTag failed"));
  3357. }
  3358. if (bAlloc && lpTagValueUNI) {
  3359. LocalFree(lpTagValueUNI);
  3360. bAlloc = FALSE;
  3361. }
  3362. (LPBYTE)lptag_A += lptag_A->cb ; // go to next tag
  3363. }
  3364. }
  3365. lpgd = GlobalLock(hNewGroup);
  3366. cb = SizeofGroup(lpgd);
  3367. GlobalUnlock(hNewGroup);
  3368. *lphNewGroup = hNewGroup;
  3369. return(cb);
  3370. PuntCreation:
  3371. QuitThis:
  3372. if (hNewGroup) {
  3373. GlobalFree(hNewGroup);
  3374. }
  3375. return(0);
  3376. }