group.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 123k
Category:

Windows Kernel

Development Platform:

Visual C++

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