//--------------------------------------------------------------------------- // //--------------------------------------------------------------------------- #include "grpconv.h" #include "util.h" #include "rcids.h" #include "group.h" #include "gcinst.h" #include #include #define INITGUID #include #pragma data_seg(DATASEG_READONLY) #include #include #pragma data_seg() #ifdef DEBUG extern UINT GC_TRACE; #endif //--------------------------------------------------------------------------- // Exported. const TCHAR c_szMapGroups[] = TEXT("MapGroups"); #ifndef WINNT const TCHAR c_szDelGroups[] = TEXT("DelGroups"); #endif //--------------------------------------------------------------------------- // Global to this file only; static const TCHAR c_szGrpConv[] = TEXT("Grpconv"); static const TCHAR c_szLastModDateTime[] = TEXT("LastModDateTime"); static const TCHAR c_szRegistry[] = TEXT("Registry"); static const TCHAR c_szDefaultUser[] = TEXT("DefaultUser"); static const TCHAR c_szGrpConvData[] = TEXT("compat.csv"); static const TCHAR c_szProgmanStartup[] = TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Program Manager\\Settings\\Startup"); static const TCHAR c_szDotPif[] = TEXT(".pif"); // New group stuff HDSA hdsaPMItems; // current group HDSA g_hdsaAppList; HKEY hkeyGroups = NULL; BOOL g_fDoingCommonGroups = FALSE; #pragma pack(1) typedef struct tagRECTS { short left; short top; short right; short bottom; } RECTS; typedef struct { LPTSTR lpszDesc; LPTSTR lpszCL; LPTSTR lpszWD; LPTSTR lpszIconPath; WORD wiIcon; WORD wHotKey; int nShowCmd; #ifdef WINNT BOOL bSepVdm; #endif } PMITEM, *PPMITEM, *LPPMITEM; // Old Progman stuff. #define GROUP_MAGIC 0x43434D50L // 'PMCC' #define GROUP_UNICODE 0x43554D50L // 'PMUC' /* * Win 3.1 .GRP file formats (ITEMDEF for items, GROUPDEF for groups) */ typedef struct { POINTS pt; WORD iIcon; WORD cbHeader; WORD cbANDPlane; WORD cbXORPlane; WORD pHeader; WORD pANDPlane; WORD pXORPlane; WORD pName; WORD pCommand; WORD pIconPath; } ITEMDEF, *PITEMDEF, *LPITEMDEF; typedef struct { DWORD dwMagic; WORD wCheckSum; WORD cbGroup; WORD nCmdShow; RECTS rcNormal; POINTS ptMin; WORD pName; WORD cxIcon; WORD cyIcon; WORD wIconFormat; WORD wReserved; WORD cItems; } GROUPDEF, *PGROUPDEF, *LPGROUPDEF; typedef struct { WORD wID; WORD wItem; WORD cb; } PMTAG, *PPMTAG, *LPPMTAG; // Thank God the tag stuff never really caught on. #define TAG_MAGIC GROUP_MAGIC #define ID_MAINTAIN 0x8000 #define ID_MAGIC 0x8000 #define ID_WRITERVERSION 0x8001 #define ID_APPLICATIONDIR 0x8101 #define ID_HOTKEY 0x8102 #define ID_MINIMIZE 0x8103 #ifdef WINNT #define ID_NEWVDM 0x8104 #endif #define ID_LASTTAG 0xFFFF /* * NT 3.1 Ansi .GRP File format structures */ typedef struct tagGROUPDEF_A { DWORD dwMagic; /* magical bytes 'PMCC' */ WORD wCheckSum; /* adjust this for zero sum of file */ WORD cbGroup; /* length of group segment */ RECT rcNormal; /* rectangle of normal window */ POINT ptMin; /* point of icon */ WORD nCmdShow; /* min, max, or normal state */ WORD pName; /* name of group */ /* these four change interpretation */ WORD cxIcon; /* width of icons */ WORD cyIcon; /* hieght of icons */ WORD wIconFormat; /* planes and BPP in icons */ WORD wReserved; /* This word is no longer used. */ WORD cItems; /* number of items in group */ WORD rgiItems[1]; /* array of ITEMDEF offsets */ } NT_GROUPDEF_A, *PNT_GROUPDEF_A; typedef NT_GROUPDEF_A *LPNT_GROUPDEF_A; typedef struct tagITEMDEF_A { POINT pt; /* location of item icon in group */ WORD idIcon; /* id of item icon */ WORD wIconVer; /* icon version */ WORD cbIconRes; /* size of icon resource */ WORD indexIcon; /* index of item icon */ WORD dummy2; /* - not used anymore */ WORD pIconRes; /* offset of icon resource */ WORD dummy3; /* - not used anymore */ WORD pName; /* offset of name string */ WORD pCommand; /* offset of command string */ WORD pIconPath; /* offset of icon path */ } NT_ITEMDEF_A, *PNT_ITEMDEF_A; typedef NT_ITEMDEF_A *LPNT_ITEMDEF_A; /* * NT 3.1a Unicode .GRP File format structures */ typedef struct tagGROUPDEF { DWORD dwMagic; /* magical bytes 'PMCC' */ DWORD cbGroup; /* length of group segment */ RECT rcNormal; /* rectangle of normal window */ POINT ptMin; /* point of icon */ WORD wCheckSum; /* adjust this for zero sum of file */ WORD nCmdShow; /* min, max, or normal state */ DWORD pName; /* name of group */ /* these four change interpretation */ WORD cxIcon; /* width of icons */ WORD cyIcon; /* hieght of icons */ WORD wIconFormat; /* planes and BPP in icons */ WORD wReserved; /* This word is no longer used. */ WORD cItems; /* number of items in group */ WORD Reserved1; DWORD Reserved2; DWORD rgiItems[1]; /* array of ITEMDEF offsets */ } NT_GROUPDEF, *PNT_GROUPDEF; typedef NT_GROUPDEF *LPNT_GROUPDEF; typedef struct tagITEMDEF { POINT pt; /* location of item icon in group */ WORD iIcon; /* id of item icon */ WORD wIconVer; /* icon version */ WORD cbIconRes; /* size of icon resource */ WORD wIconIndex; /* index of the item icon (not the same as the id) */ DWORD pIconRes; /* offset of icon resource */ DWORD pName; /* offset of name string */ DWORD pCommand; /* offset of command string */ DWORD pIconPath; /* offset of icon path */ } NT_ITEMDEF, *PNT_ITEMDEF; typedef NT_ITEMDEF *LPNT_ITEMDEF; typedef struct _tag { WORD wID; // tag identifier WORD dummy1; // need this for alignment! int wItem; // (unde the covers 32 bit point!)item the tag belongs to WORD cb; // size of record, including id and count WORD dummy2; // need this for alignment! BYTE rgb[1]; } NT_PMTAG, * LPNT_PMTAG; /* the pointers in the above structures are short pointers relative to the * beginning of the segments. This macro converts the short pointer into * a long pointer including the proper segment/selector value. It assumes * that its argument is an lvalue somewhere in a group segment, for example, * PTR(lpgd->pName) returns a pointer to the group name, but k=lpgd->pName; * PTR(k) is obviously wrong as it will use either SS or DS for its segment, * depending on the storage class of k. */ #define PTR(base, offset) (LPBYTE)((PBYTE)base + offset) /* PTR2 is used for those cases where a variable already contains an offset * (The "case that doesn't work", above) */ #define PTR2(lp,offset) ((LPBYTE)MAKELONG(offset,HIWORD(lp))) /* this macro is used to retrieve the i-th item in the group segment. Note * that this pointer will NOT be NULL for an unused slot. */ #define ITEM(lpgd,i) ((LPNT_ITEMDEF)PTR(lpgd, lpgd->rgiItems[i])) /* Keeping things starting on aligned boundaries allows faster access on * most platforms. */ #define MyDwordAlign(size) (((size) + 3) & ~3) #pragma pack() #define CFree(a) if(a) Free(a) //--------------------------------------------------------------------------- #define Stream_Write(ps, pv, cb) SUCCEEDED((ps)->lpVtbl->Write(ps, pv, cb, NULL)) #define Stream_Close(ps) (void)(ps)->lpVtbl->Release(ps) #define VOF_BAD 0 #define VOF_WIN31 1 #define VOF_WINNT 2 int ConvertToUnicodeGroup(LPNT_GROUPDEF_A lpGroupORI, LPHANDLE lphNewGroup); //--------------------------------------------------------------------------- // Init the group stuff BOOL ItemList_Create(LPCTSTR lpszGroup) { if (!hdsaPMItems) hdsaPMItems = DSA_Create(SIZEOF(PMITEM), 16); if (hdsaPMItems) return TRUE; DebugMsg(DM_ERROR, TEXT("cg.gi: Unable to init.")); return FALSE; } //--------------------------------------------------------------------------- // Tidyup. void ItemList_Destroy(void) { int i; int cItems; LPPMITEM lppmitem; // Clean up the items. cItems = DSA_GetItemCount(hdsaPMItems); for(i=0; i < cItems; i++) { lppmitem = DSA_GetItemPtr(hdsaPMItems, 0); // Nuke the strings. CFree(lppmitem->lpszDesc); CFree(lppmitem->lpszCL); CFree(lppmitem->lpszWD); CFree(lppmitem->lpszIconPath); // Nuke the structure. DSA_DeleteItem(hdsaPMItems, 0); } DSA_Destroy(hdsaPMItems); hdsaPMItems = NULL; } //--------------------------------------------------------------------------- // Returns TRUE if the file smells like an old PM group, the title of the // group is returned in lpszTitle which must be at least 32 chars big. // REVIEW - Is it worth checking the checksum? UINT Group_ValidOldFormat(LPCTSTR lpszOldGroup, LPTSTR lpszTitle) { #ifdef UNICODE HANDLE fh; DWORD dwBytesRead; #else HFILE fh; #endif UINT nCode; GROUPDEF grpdef; // Find and open the group file. #ifdef UNICODE fh = CreateFile( lpszOldGroup, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if (fh != INVALID_HANDLE_VALUE) #else fh = _lopen(lpszOldGroup, OF_READ | OF_SHARE_DENY_NONE); if (fh != HFILE_ERROR) #endif { // Get the definition. #ifdef UNICODE ReadFile(fh, &grpdef, SIZEOF(grpdef), &dwBytesRead, NULL); #else _lread(fh, &grpdef, SIZEOF(grpdef)); #endif // Does it have the right magic bytes?. switch( grpdef.dwMagic ) { case GROUP_UNICODE: { NT_GROUPDEF nt_grpdef; #ifdef UNICODE SetFilePointer(fh, 0, NULL, FILE_BEGIN); ReadFile(fh, &nt_grpdef, SIZEOF(nt_grpdef), &dwBytesRead, NULL); #else _llseek(fh, 0, 0); // Back to the start _lread(fh, &nt_grpdef, SIZEOF(nt_grpdef)); #endif // Yep, Get it's size.. // Is it at least as big as the header says it is? #ifdef UNICODE if ( nt_grpdef.cbGroup <= (DWORD)SetFilePointer(fh, 0L, NULL, FILE_END)) #else if ( nt_grpdef.cbGroup <= (DWORD)_llseek(fh, 0L, 2)) #endif { WCHAR wchGroupName[MAXGROUPNAMELEN+1]; // Yep, probably valid. // Get its title. #ifdef UNICODE SetFilePointer(fh, nt_grpdef.pName, 0, FILE_BEGIN); ReadFile(fh, wchGroupName, SIZEOF(wchGroupName), &dwBytesRead, NULL); lstrcpy(lpszTitle, wchGroupName); #else _llseek(fh, nt_grpdef.pName, 0); _lread(fh,wchGroupName, SIZEOF(wchGroupName)); WideCharToMultiByte (CP_ACP, 0, wchGroupName, -1, lpszTitle, MAXGROUPNAMELEN+1, NULL, NULL); #endif nCode = VOF_WINNT; } else { // No. Too small. DebugMsg(DM_TRACE, TEXT("gc.gvof: File has invalid size.")); nCode = VOF_BAD; } } break; case GROUP_MAGIC: { CHAR chGroupName[MAXGROUPNAMELEN+1]; // Yep, Get it's size.. // Is it at least as big as the header says it is? #ifdef UNICODE if (grpdef.cbGroup <= (WORD) SetFilePointer(fh, 0L, NULL, FILE_END)) #else if (grpdef.cbGroup <= (WORD) _llseek(fh, 0L, 2)) #endif { // Check to make sure there is a name embedded in the // .grp file. If not, just use the filename if (grpdef.pName==0) { LPTSTR lpszFile, lpszExt, lpszDest = lpszTitle; lpszFile = PathFindFileName( lpszOldGroup ); lpszExt = PathFindExtension( lpszOldGroup ); for( ; lpszFile && lpszExt && (lpszFile != lpszExt); *lpszDest++ = *lpszFile++ ); *lpszDest = TEXT('\0'); } else { // Yep, probably valid. // Get it's title. #ifdef UNICODE SetFilePointer(fh, grpdef.pName, NULL, FILE_BEGIN); ReadFile(fh, chGroupName, MAXGROUPNAMELEN+1, &dwBytesRead, NULL); MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, chGroupName, -1, lpszTitle, MAXGROUPNAMELEN+1 ) ; #else _llseek(fh, grpdef.pName, 0); _lread(fh, lpszTitle, MAXGROUPNAMELEN+1); #endif } nCode = VOF_WIN31; } else { // No. Too small. DebugMsg(DM_TRACE, TEXT("gc.gvof: File has invalid size.")); nCode = VOF_BAD; } break; } default: // No, the magic bytes are wrong. DebugMsg(DM_TRACE, TEXT("gc.gvof: File has invalid magic bytes.")); nCode = VOF_BAD; break; } #ifdef UNICODE CloseHandle(fh); #else _lclose(fh); #endif } else { // No. Can't even read the file. DebugMsg(DM_TRACE, TEXT("gc.gvof: File is unreadble.")); nCode = VOF_BAD; } return nCode; } BOOL _IsValidFileNameChar(TBYTE ch, UINT flags) { switch (ch) { case TEXT('\\'): // path separator return flags & PRICF_ALLOWSLASH; case TEXT(';'): // terminator case TEXT(','): // terminator case TEXT('|'): // pipe case TEXT('>'): // redir case TEXT('<'): // redir case TEXT('"'): // quote case TEXT('?'): // wc we only do wilds here because they're case TEXT('*'): // wc legal for qualifypath case TEXT(':'): // drive colon case TEXT('/'): // path sep return FALSE; } // Can not be a control char... return ch >= TEXT(' '); } void PathRemoveIllegalChars(LPTSTR pszPath, int iGroupName, UINT flags) { LPTSTR pszT = pszPath + iGroupName; // Map all of the strange characters out of the name for both LFn and not // machines while (*pszT) { if (!_IsValidFileNameChar(*pszT, flags)) *pszT = TEXT('_'); // Don't Allow invalid chars in names pszT = CharNext(pszT); } } //--------------------------------------------------------------------------- // We want certain groups to end up in a new location eg Games is now // Applications\Games. void MapGroupTitle(LPCTSTR lpszOld, LPTSTR lpszNew, UINT cchNew) { // Is there a mapping? if (!Reg_GetString(g_hkeyGrpConv, c_szMapGroups, lpszOld, lpszNew, cchNew*sizeof(TCHAR))) { // Nope, just use the given name. lstrcpyn(lpszNew, lpszOld, cchNew); } DebugMsg(DM_TRACE, TEXT("gc.mgr: From %s to %s"), lpszOld, lpszNew); } #ifndef WINNT BOOL Group_DeleteIfRequired(LPCTSTR lpszOldGrpTitle, LPCTSTR lpszOldGrpFile) { BOOL fRet; HKEY hkeyNew; TCHAR szIniFile[MAX_PATH], szFile[MAX_PATH]; if (Reg_GetString(g_hkeyGrpConv, c_szDelGroups, lpszOldGrpTitle, NULL, 0)) { Win32DeleteFile(lpszOldGrpFile); DebugMsg(DM_TRACE, TEXT("gc.mgr: old group %s has been deleted"), lpszOldGrpTitle); // Remove old Group entry from registry.. // if (RegOpenKey(g_hkeyGrpConv, c_szGroups, &hkeyNew) == ERROR_SUCCESS) { if (RegDeleteValue(hkeyNew, lpszOldGrpFile) == ERROR_SUCCESS) { fRet = TRUE; } RegCloseKey(hkeyNew); } // Remove old Group entry from progman.ini if (FindProgmanIni(szIniFile)) { UINT uSize; LPTSTR pSection, pKey; for (uSize = 1024; uSize < 1024 * 8; uSize += 1024) { pSection = (PSTR)LocalAlloc(LPTR, uSize); if (!pSection) break; if ((UINT)GetPrivateProfileString(c_szGroups, NULL, c_szNULL, pSection, uSize / sizeof(pSection[0]), szIniFile) < uSize - 5) break; LocalFree((HLOCAL)pSection); pSection = NULL; fRet = FALSE; } if (pSection) { for (pKey = pSection; *pKey; pKey += lstrlen(pKey) + 1) { GetPrivateProfileString(c_szGroups, pKey, c_szNULL, szFile, ARRAYSIZE(szFile), szIniFile); if (lstrcmpi(lpszOldGrpFile, szFile) == 0) { WritePrivateProfileString(c_szGroups, pKey, NULL, szIniFile); break; } } LocalFree((HLOCAL)pSection); pSection = NULL; } } } else { fRet = FALSE; } return fRet; } #endif // !WINNT #undef PathRemoveExtension //--------------------------------------------------------------------------- void PathRemoveExtension(LPTSTR pszPath) { LPTSTR pExt = PathFindExtension(pszPath); if (*pExt) { Assert(*pExt == TEXT('.')); *pExt = 0; // null out the "." } } //--------------------------------------------------------------------------- // Given a path to an old group, create and return a path to where the new // group will be. BOOL Group_GenerateNewGroupPath(HWND hwnd, LPCTSTR lpszOldGrpTitle, LPTSTR lpszNewGrpPath, LPCTSTR pszOldGrpPath) { int iLen; TCHAR szGrpTitle[MAX_PATH]; TCHAR szOldGrpTitle[32]; // Get the location for all the special shell folders. if (g_fDoingCommonGroups) SHGetSpecialFolderPath(hwnd, lpszNewGrpPath, CSIDL_COMMON_PROGRAMS, TRUE); else SHGetSpecialFolderPath(hwnd, lpszNewGrpPath, CSIDL_PROGRAMS, TRUE); if (IsLFNDrive(lpszNewGrpPath)) { // Fix it a bit. lstrcpyn(szOldGrpTitle, lpszOldGrpTitle, ARRAYSIZE(szOldGrpTitle)); PathRemoveIllegalChars(szOldGrpTitle, 0, PRICF_NORMAL); // Munge the names so that things move to the new locations. MapGroupTitle(szOldGrpTitle, szGrpTitle, ARRAYSIZE(szGrpTitle)); // Stick on the new group name. PathAddBackslash(lpszNewGrpPath); iLen = lstrlen(lpszNewGrpPath); // NB Don't use PathAppend() - very bad if there's a colons in the title. lstrcpyn(lpszNewGrpPath+iLen, szGrpTitle, MAX_PATH-iLen); PathRemoveIllegalChars(lpszNewGrpPath, iLen, PRICF_ALLOWSLASH); } else { // Just use the old group file name - this will make sure the group // names remain unique. PathAppend(lpszNewGrpPath, PathFindFileName(pszOldGrpPath)); PathRemoveExtension(lpszNewGrpPath); } if (!PathFileExists(lpszNewGrpPath)) { // Folder doesn't exist. // return Win32CreateDirectory(lpszNewGrpPath, NULL); return (SHCreateDirectory(hwnd, lpszNewGrpPath) == 0); } // Folder already exists. return TRUE; } //--------------------------------------------------------------------------- // Returns true if the offsets given in the item def are valid-ish. BOOL CheckItemDef(LPITEMDEF lpitemdef, WORD cbGroup) { if (lpitemdef->pHeader < cbGroup && lpitemdef->pANDPlane < cbGroup && lpitemdef->pXORPlane < cbGroup && lpitemdef->pName < cbGroup && lpitemdef->pCommand < cbGroup && lpitemdef->pIconPath < cbGroup && lpitemdef->pHeader && lpitemdef->pXORPlane && lpitemdef->pCommand) return TRUE; else { return FALSE; } } //--------------------------------------------------------------------------- // Returns true if the offsets given in the item def are valid-ish. BOOL CheckItemDefNT(LPNT_ITEMDEF lpitemdef, DWORD cbGroup) { if (lpitemdef->pName < cbGroup && lpitemdef->pCommand < cbGroup && lpitemdef->pIconPath < cbGroup && lpitemdef->pCommand) return TRUE; else { return FALSE; } } //--------------------------------------------------------------------------- // Read the tags info from the given file handle from the given offset. #ifdef UNICODE void HandleTags(HANDLE fh, WORD oTags) #else void HandleTags(int fh, WORD oTags) #endif { LONG cbGroupReal; PMTAG pmtag; BOOL fTags = TRUE; TCHAR szText[MAX_PATH]; BOOL fFirstTag = FALSE; LPPMITEM lppmitem; WORD wHotKey; #ifdef UNICODE DWORD dwBytesRead; #endif DebugMsg(DM_TRACE, TEXT("cg.ht: Reading tags.")); #ifdef UNICODE cbGroupReal = SetFilePointer(fh, 0, NULL, FILE_END); #else cbGroupReal = (WORD) _llseek(fh, 0L, 2); #endif if (cbGroupReal <= (LONG) oTags) { // No tags in this file. return; } // Get to the tags section. #ifdef UNICODE SetFilePointer(fh, oTags, NULL, FILE_BEGIN); #else _llseek(fh, oTags, 0); #endif while (fTags) { #ifdef UNICODE if (!ReadFile(fh, &pmtag, SIZEOF(pmtag), &dwBytesRead, NULL) || dwBytesRead == 0) { fTags = FALSE; break; } #else fTags = _lread(fh, &pmtag, SIZEOF(pmtag)); #endif switch (pmtag.wID) { case ID_MAGIC: { // DebugMsg(DM_TRACE, "gc.ht: First tag found."); fFirstTag = TRUE; #ifdef UNICODE SetFilePointer(fh, pmtag.cb - SIZEOF(PMTAG), NULL, FILE_CURRENT); #else _llseek(fh, pmtag.cb - SIZEOF(PMTAG), 1); #endif break; } case ID_LASTTAG: { // DebugMsg(DM_TRACE, "gc.ht: Last tag found."); fTags = FALSE; break; } case ID_APPLICATIONDIR: { // DebugMsg(DM_TRACE, "gc.ht: App dir %s found for %d.", (LPSTR) szText, pmtag.wItem); fgets(szText, ARRAYSIZE(szText), fh); lppmitem = DSA_GetItemPtr(hdsaPMItems, pmtag.wItem); if (lppmitem) { Str_SetPtr(&lppmitem->lpszCL, szText); } #ifdef DEBUG else { DebugMsg(DM_ERROR, TEXT("gc.ht: Item is invalid.")); } #endif break; } case ID_HOTKEY: { // DebugMsg(DM_TRACE, "gc.ht: Hotkey found for %d.", pmtag.wItem); #ifdef UNICODE ReadFile(fh, &wHotKey, SIZEOF(wHotKey), &dwBytesRead, NULL); #else _lread(fh, &wHotKey, SIZEOF(wHotKey)); #endif lppmitem = DSA_GetItemPtr(hdsaPMItems, pmtag.wItem); if (lppmitem) { lppmitem->wHotKey = wHotKey; } #ifdef DEBUG else { DebugMsg(DM_ERROR, TEXT("gc.ht: Item is invalid.")); } #endif break; } case ID_MINIMIZE: { // DebugMsg(DM_TRACE, "gc.ht: Minimise flag found for %d.", pmtag.wItem); lppmitem = DSA_GetItemPtr(hdsaPMItems, pmtag.wItem); if (lppmitem) { lppmitem->nShowCmd = SW_SHOWMINNOACTIVE; } #ifdef DEBUG else { DebugMsg(DM_ERROR, TEXT("gc.ht: Item is invalid.")); } #endif // Skip to the next tag. #ifdef UNICODE SetFilePointer(fh, pmtag.cb - SIZEOF(PMTAG), NULL, FILE_CURRENT); #else _llseek(fh, pmtag.cb - SIZEOF(PMTAG), 1); #endif break; } #ifdef WINNT case ID_NEWVDM: { // DebugMsg(DM_TRACE, "gc.ht: Separate VDM flag found for %d.", pmtag.wItem ); lppmitem = DSA_GetItemPtr(hdsaPMItems, pmtag.wItem); if (lppmitem) { lppmitem->bSepVdm = TRUE; } #ifdef DEBUG else { DebugMsg(DM_ERROR, TEXT("gc.ht: Item is invalid.")); } #endif // Skip to the next tag. #ifdef UNICODE SetFilePointer(fh, pmtag.cb - SIZEOF(PMTAG), NULL, FILE_CURRENT); #else _llseek(fh, pmtag.cb - SIZEOF(PMTAG), 1); #endif break; } #endif default: { // We've found something we don't understand but we haven't // found the first tag yet - probably a bust file. if (!fFirstTag) { DebugMsg(DM_TRACE, TEXT("gc.ht: No initial tag found - tags section is corrupt.")); fTags = FALSE; } else { // Some unknown tag. if (pmtag.cb < SIZEOF(PMTAG)) { // Can't continue! DebugMsg(DM_TRACE, TEXT("gc.ht: Tag has invalid size - ignoring remaining tags.")); fTags = FALSE; } else { // Just ignore its data and continue. #ifdef UNICODE SetFilePointer(fh, pmtag.cb - SIZEOF(PMTAG), NULL, FILE_CURRENT); #else _llseek(fh, pmtag.cb - SIZEOF(PMTAG), 1); #endif } } break; } } } } //--------------------------------------------------------------------------- // Read the tags info from the given file handle from the given offset. #ifdef UNICODE void HandleTagsNT(HANDLE fh, DWORD oTags) #else void HandleTagsNT(int fh, DWORD oTags) #endif { DWORD cbGroupReal; DWORD dwPosition; NT_PMTAG pmtag; BOOL fTags = TRUE; WCHAR wszTemp[MAX_PATH]; TCHAR szText[MAX_PATH]; BOOL fFirstTag = FALSE; LPPMITEM lppmitem; WORD wHotKey; #ifdef UNICODE DWORD dwBytesRead; #endif DebugMsg(DM_TRACE, TEXT("cg.ht: Reading tags.")); #ifdef UNICODE cbGroupReal = SetFilePointer(fh, 0, NULL, FILE_END); #else cbGroupReal = _llseek(fh, 0L, 2); #endif if (cbGroupReal <= oTags) { // No tags in this file. return; } // Get to the tags section. dwPosition = oTags; while (fTags) { #ifdef UNICODE SetFilePointer(fh, dwPosition, NULL, FILE_BEGIN); if (!ReadFile(fh, &pmtag, SIZEOF(pmtag), &dwBytesRead, NULL) || dwBytesRead == 0) { fTags = FALSE; break; } #else _llseek(fh,dwPosition,0); fTags = _lread(fh, &pmtag, SIZEOF(pmtag)); #endif switch (pmtag.wID) { case ID_MAGIC: { // DebugMsg(DM_TRACE, "gc.ht: First tag found."); fFirstTag = TRUE; dwPosition += pmtag.cb; break; } case ID_LASTTAG: { // DebugMsg(DM_TRACE, "gc.ht: Last tag found."); fTags = FALSE; break; } case ID_APPLICATIONDIR: { #ifdef UNICODE SetFilePointer(fh, dwPosition+FIELD_OFFSET(NT_PMTAG,rgb[0]), NULL, FILE_BEGIN); ReadFile(fh, wszTemp, SIZEOF(wszTemp), &dwBytesRead, NULL); lstrcpy(szText, wszTemp); #else _llseek(fh,dwPosition+FIELD_OFFSET(NT_PMTAG,rgb[0]),0); _lread(fh,wszTemp,SIZEOF(wszTemp)); WideCharToMultiByte (CP_ACP, 0, wszTemp, -1, szText, ARRAYSIZE(szText), NULL, NULL); #endif // DebugMsg(DM_TRACE, "gc.ht: App dir %s found for %d.", (LPSTR) szText, pmtag.wItem); lppmitem = DSA_GetItemPtr(hdsaPMItems, pmtag.wItem); if (lppmitem) { Str_SetPtr(&lppmitem->lpszCL, szText); } #ifdef DEBUG else { DebugMsg(DM_ERROR, TEXT("gc.ht: Item is invalid.")); } #endif dwPosition += pmtag.cb; break; } case ID_HOTKEY: { // DebugMsg(DM_TRACE, "gc.ht: Hotkey found for %d.", pmtag.wItem); #ifdef UNICODE ReadFile(fh, &wHotKey, SIZEOF(wHotKey), &dwBytesRead, NULL); #else _lread(fh, &wHotKey, SIZEOF(wHotKey)); #endif lppmitem = DSA_GetItemPtr(hdsaPMItems, pmtag.wItem); if (lppmitem) { lppmitem->wHotKey = wHotKey; } #ifdef DEBUG else { DebugMsg(DM_ERROR, TEXT("gc.ht: Item is invalid.")); } #endif dwPosition += pmtag.cb; break; } case ID_MINIMIZE: { // DebugMsg(DM_TRACE, "gc.ht: Minimise flag found for %d.", pmtag.wItem); lppmitem = DSA_GetItemPtr(hdsaPMItems, pmtag.wItem); if (lppmitem) { lppmitem->nShowCmd = SW_SHOWMINNOACTIVE; } #ifdef DEBUG else { DebugMsg(DM_ERROR, TEXT("gc.ht: Item is invalid.")); } #endif // Skip to the next tag. dwPosition += pmtag.cb; break; } #ifdef WINNT case ID_NEWVDM: { // DebugMsg(DM_TRACE, "gc.ht: Separate VDM flag found for %d.", pmtag.wItem ); lppmitem = DSA_GetItemPtr(hdsaPMItems, pmtag.wItem); if (lppmitem) { lppmitem->bSepVdm = TRUE; } #ifdef DEBUG else { DebugMsg(DM_ERROR, TEXT("gc.ht: Item is invalid.")); } #endif // Skip to the next tag. dwPosition += pmtag.cb; break; } #endif default: { // We've found something we don't understand but we haven't // found the first tag yet - probably a bust file. if (!fFirstTag) { DebugMsg(DM_TRACE, TEXT("gc.ht: No initial tag found - tags section is corrupt.")); fTags = FALSE; } else { // Some unknown tag. if (pmtag.cb < SIZEOF(PMTAG)) { // Can't continue! DebugMsg(DM_TRACE, TEXT("gc.ht: Tag has invalid size - ignoring remaining tags.")); fTags = FALSE; } else { // Just ignore its data and continue. dwPosition += pmtag.cb; } } break; } } } } //--------------------------------------------------------------------------- void DeleteBustedItems(void) { int i, cItems; LPPMITEM ppmitem; cItems = DSA_GetItemCount(hdsaPMItems); for (i=0; ilpszDesc || !(*ppmitem->lpszDesc)) { // Yep, delete it. DSA_DeleteItem(hdsaPMItems, i); cItems--; i--; } } } //--------------------------------------------------------------------------- void ShortenDescriptions(void) { int i, cItems; LPPMITEM ppmitem; cItems = DSA_GetItemCount(hdsaPMItems); for (i=0; ilpszDesc, ppmitem->lpszDesc, 9); } } //--------------------------------------------------------------------------- // Kinda like PathFindFileName() but handles things like c:\foo\ differently // to match progmans code. LPTSTR WINAPI _PathFindFileName(LPCTSTR pPath) { LPCTSTR pT; for (pT = pPath; *pPath; pPath = CharNext(pPath)) { if ((pPath[0] == TEXT('\\') || pPath[0] == TEXT(':')) && (pPath[1] != TEXT('\\'))) pT = pPath + 1; } return (LPTSTR)pT; // const -> non const } //--------------------------------------------------------------------------- // Take a 3.1 format WD and exe and convert them to the new style. // NB Old style was WD+exename and exepath - new style is exepath+exename and // WD. void MungePaths(void) { LPTSTR lpszFileName; // Ptr to filename part (plus params). LPTSTR lpszParams; // Ptr to first char of params. TCHAR szCL[MAX_PATH]; TCHAR szWD[MAX_PATH]; int i, cItems; LPPMITEM lppmitem; cItems = DSA_GetItemCount(hdsaPMItems); for (i=0; ilpszCL, szCL, ARRAYSIZE(szCL)); // Get the current working dir. Str_GetPtr(lppmitem->lpszWD, szWD, ARRAYSIZE(szWD)); #ifdef OLDWAY // Find the filename part... // Params will confuse PFFN. lpszParams = PathGetArgs(szWD); if (*lpszParams) { // Chop them off. // NB Previous char is a space by definition. *(lpszParams-1) = TEXT('\0'); lpszFileName = _PathFindFileName(szWD); // Put them back *(lpszParams-1) = TEXT(' '); } else { // No params. lpszFileName = PathFindFileName(szWD); } // Copy this onto the exe path. lstrcat((LPTSTR) szCL, lpszFileName); // Remove it from the end of the WD. *lpszFileName = TEXT('\0'); // For anything but things like c:\ remove the last slash. if (!PathIsRoot(szWD)) { *(lpszFileName-1) = TEXT('\0'); } #else lpszFileName = szWD; if (*lpszFileName == TEXT('"')) { while (lpszFileName) { lpszFileName = StrChr(lpszFileName+1,TEXT('"')); if (!lpszFileName) { // // The directory is not in quotes and since the command // path starts with a quote, there is no working directory. // lpszFileName = szWD; break; } if (*(lpszFileName+1) == TEXT('\\')) { // // The working directory is in quotes. // lpszFileName++; break; } } } else { // // if there's a working directory, it is not in quotes // Copy up until the last \ preceding any quote, space, or the end // LPTSTR lpEnd = lpszFileName; while (*lpszFileName && *lpszFileName != TEXT('"') && *lpszFileName != TEXT(' ')) { if ((*lpszFileName == TEXT('\\') || *lpszFileName == TEXT(':')) && *(lpszFileName+1) != TEXT('\\')) lpEnd = lpszFileName; lpszFileName = CharNext(lpszFileName); } lpszFileName = lpEnd; } // // If the split is at the beginning, // then there is no working dir // if (lpszFileName == szWD) { lstrcat(szCL, szWD); szWD[0] = TEXT('\0'); } else { lstrcat(szCL, lpszFileName+1); *(lpszFileName+1) = TEXT('\0'); // Split it. // // Remove quotes from the working dir NOW. // if (szWD[0] == TEXT('"')) { LPTSTR lpTemp; for (lpTemp = szWD+1; *lpTemp && *lpTemp != TEXT('"'); lpTemp++) *(lpTemp-1) = *lpTemp; if (*lpTemp == TEXT('"')) { *(lpTemp-1) = TEXT('\0'); } } // For anything but things like c:\ remove the last slash. if (!PathIsRoot(szWD)) { *lpszFileName = TEXT('\0'); } } #endif // Replace the data. Str_SetPtr(&lppmitem->lpszCL, szCL); Str_SetPtr(&lppmitem->lpszWD, szWD); // DebugMsg(DM_TRACE, "gc.mp: Exe %s, WD %s", (LPSTR)szCL, (LPSTR)szWD); } } //--------------------------------------------------------------------------- // Set all the fields of the given pmitem to clear; void PMItem_Clear(LPPMITEM lppmitem) { lppmitem->lpszDesc = NULL; lppmitem->lpszCL = NULL; lppmitem->lpszWD = NULL; lppmitem->lpszIconPath = NULL; lppmitem->wiIcon = 0; lppmitem->wHotKey = 0; lppmitem->nShowCmd = SW_SHOWNORMAL; #ifdef WINNT lppmitem->bSepVdm = FALSE; #endif } //--------------------------------------------------------------------------- // Read the item data from the file and add it to the list. // Returns TRUE if everything went perfectly. #ifdef UNICODE BOOL GetAllItemData(HANDLE fh, WORD cItems, WORD cbGroup, LPTSTR lpszOldGrpTitle, LPTSTR lpszNewGrpPath) #else BOOL GetAllItemData(HFILE fh, WORD cItems, WORD cbGroup, LPTSTR lpszOldGrpTitle, LPTSTR lpszNewGrpPath) #endif { UINT cbItemArray; WORD *rgItems; UINT i, iItem; TCHAR szDesc[CCHSZNORMAL]; TCHAR szCL[CCHSZNORMAL]; TCHAR szIconPath[CCHSZNORMAL]; ITEMDEF itemdef; BOOL fOK = TRUE; UINT cbRead; PMITEM pmitem; #ifdef UNICODE DWORD dwBytesRead; #endif // Read in the old item table... iItem = 0; cbItemArray = cItems * SIZEOF(*rgItems); rgItems = (WORD *)LocalAlloc(LPTR, cbItemArray); if (!rgItems) { DebugMsg(DM_ERROR, TEXT("gc.gcnfo: Out of memory.")); return FALSE; } #ifdef UNICODE SetFilePointer(fh, SIZEOF(GROUPDEF), NULL, FILE_BEGIN); ReadFile(fh, rgItems, cbItemArray, &dwBytesRead, NULL); #else _llseek(fh, SIZEOF(GROUPDEF), 0); _lread(fh, rgItems, cbItemArray); #endif // Show progress in two stages, first reading then writing. Group_SetProgressNameAndRange(lpszNewGrpPath, (cItems*2)-1); // Read in the items. // NB Don't just skip busted items since the tag data contains // indices to items and that includes busted ones. Just use // an empty description to indicate that the link is invalid. for (i=0; i cbGroup) { DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (item entry in invalid part of file) - skipping item.")); fOK = FALSE; goto AddItem; } #ifdef UNICODE SetFilePointer(fh, rgItems[i], NULL, FILE_BEGIN); ReadFile(fh, &itemdef, SIZEOF(itemdef), &cbRead, NULL); #else _llseek(fh, rgItems[i], 0); cbRead = _lread(fh, &itemdef, SIZEOF(itemdef)); #endif if (cbRead != SIZEOF(itemdef)) { DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (invalid definition) - skipping item %d."), i); fOK = FALSE; goto AddItem; } if (!CheckItemDef(&itemdef, cbGroup)) { DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (invalid item field) - skipping item %d."), i); fOK = FALSE; goto AddItem; } #ifdef UNICODE SetFilePointer(fh, itemdef.pName, NULL, FILE_BEGIN); #else _llseek(fh, itemdef.pName, 0); #endif fgets(szDesc, SIZEOF(szDesc), fh); if (!*szDesc) { DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (empty name) - skipping item %d."), i); fOK = FALSE; goto AddItem; } #ifdef UNICODE SetFilePointer(fh, itemdef.pCommand, NULL, FILE_BEGIN); #else _llseek(fh, itemdef.pCommand, 0); #endif fgets(szCL, SIZEOF(szCL), fh); // We hit this case with links to c:\ (rare, very rare). #if 0 if (!*szCL) { DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (empty command line) - skipping item %d."), i); // We use a null description to signal a problem with this item. szDesc[0] = TEXT('\0'); fOK = FALSE; goto AddItem; } #endif if (itemdef.pIconPath!=0xFFFF) { #ifdef UNICODE SetFilePointer(fh, itemdef.pIconPath, NULL, FILE_BEGIN); #else _llseek(fh, itemdef.pIconPath, 0); #endif fgets(szIconPath, SIZEOF(szIconPath), fh); } else { szIconPath[ 0 ] = TEXT('\0'); } if (!*szIconPath) { // NB Do nothing. Empty icon paths are legal - associated apps where the associated // app is missing will have an empty icon path. } // NB Forget about the icon data. // DebugMsg(DM_TRACE, "gc.gcnfo: Found item %s.", (LPSTR) szDesc); // Store away the data.... // NB We load the old commands line into the working dir field because // only the leaf is the command, the rest is the WD. Once we've been // through the tags section we can sort out the mess. AddItem: PMItem_Clear(&pmitem); #ifdef DEBUG DebugMsg(GC_TRACE, TEXT("gc.gaid: Desc %s"), (LPTSTR) szDesc); DebugMsg(GC_TRACE, TEXT(" WD: %s"), (LPTSTR) szCL); DebugMsg(GC_TRACE, TEXT(" IP: %s(%d)"), (LPTSTR) szIconPath, itemdef.iIcon); #endif // Don't store anything for items with invalid descriptions. if (*szDesc) { // Remove illegal chars. PathRemoveIllegalChars(szDesc, 0, PRICF_NORMAL); Str_SetPtr(&pmitem.lpszDesc, szDesc); Str_SetPtr(&pmitem.lpszWD, szCL); Str_SetPtr(&pmitem.lpszIconPath, szIconPath); pmitem.wiIcon = itemdef.iIcon; } DSA_InsertItem(hdsaPMItems, iItem, &pmitem); iItem++; } LocalFree((HLOCAL)rgItems); return fOK; } //----------------------------------------------------------------------------- // Functions to try to find out which icon was appropriate given the NT icon // identifier number (the identifier for the RT_ICON resource only). //----------------------------------------------------------------------------- typedef struct _enumstruct { UINT iIndex; BOOL fFound; WORD wIconRTIconID; } ENUMSTRUCT, *LPENUMSTRUCT; BOOL EnumIconFunc( HMODULE hMod, LPCTSTR lpType, LPTSTR lpName, LPARAM lParam ) { HANDLE h; PBYTE p; int id; LPENUMSTRUCT lpes = (LPENUMSTRUCT)lParam; if (!lpName) return TRUE; h = FindResource(hMod, lpName, lpType); if (!h) return TRUE; h = LoadResource(hMod, h); p = LockResource(h); id = LookupIconIdFromDirectory(p, TRUE); UnlockResource(h); FreeResource(h); if (id == lpes->wIconRTIconID) { lpes->fFound = TRUE; return FALSE; } lpes->iIndex++; return TRUE; } WORD FindAppropriateIcon( LPTSTR lpszFileName, WORD wIconRTIconID ) { HINSTANCE hInst; TCHAR szExe[MAX_PATH]; WORD wIcon = wIconRTIconID; ENUMSTRUCT es; int olderror; hInst = FindExecutable(lpszFileName,NULL,szExe); if ( hInst <= (HINSTANCE)HINSTANCE_ERROR ) { return 0; } olderror = SetErrorMode(SEM_FAILCRITICALERRORS); hInst = LoadLibraryEx(szExe,NULL, DONT_RESOLVE_DLL_REFERENCES); SetErrorMode(olderror); if ( hInst <= (HINSTANCE)HINSTANCE_ERROR ) { return 0; } es.iIndex = 0; es.fFound = FALSE; es.wIconRTIconID = wIconRTIconID; EnumResourceNames( hInst, RT_GROUP_ICON, EnumIconFunc, (LPARAM)&es ); FreeLibrary( hInst ); if (es.fFound) { return (WORD)es.iIndex; } else { return 0; } } //--------------------------------------------------------------------------- // Read the item data from the file and add it to the list. // Returns TRUE if everything went perfectly. #ifdef UNICODE BOOL GetAllItemDataNT(HANDLE fh, WORD cItems, DWORD cbGroup, LPTSTR lpszOldGrpTitle, LPTSTR lpszNewGrpPath) #else BOOL GetAllItemDataNT(HFILE fh, WORD cItems, DWORD cbGroup, LPTSTR lpszOldGrpTitle, LPTSTR lpszNewGrpPath) #endif { UINT cbItemArray; DWORD *rgItems; UINT i, iItem; WCHAR wszTemp[CCHSZNORMAL]; TCHAR szDesc[CCHSZNORMAL]; TCHAR szCL[CCHSZNORMAL]; TCHAR szIconPath[CCHSZNORMAL]; NT_ITEMDEF itemdef; BOOL fOK = TRUE; #ifdef UNICODE DWORD cbRead; #else UINT cbRead; #endif PMITEM pmitem; // Read in the old item table... iItem = 0; cbItemArray = cItems * SIZEOF(*rgItems); rgItems = (DWORD *)LocalAlloc(LPTR, cbItemArray); if (!rgItems) { DebugMsg(DM_ERROR, TEXT("gc.gcnfo: Out of memory.")); return FALSE; } #ifdef UNICODE SetFilePointer(fh, FIELD_OFFSET(NT_GROUPDEF,rgiItems[0]), NULL, FILE_BEGIN); ReadFile(fh, rgItems, cbItemArray, &cbRead, NULL); #else _llseek(fh, FIELD_OFFSET(NT_GROUPDEF,rgiItems[0]), 0); _lread(fh, rgItems, cbItemArray); #endif // Show progress in two stages, first reading then writing. Group_SetProgressNameAndRange(lpszNewGrpPath, (cItems*2)-1); // Read in the items. // NB Don't just skip busted items since the tag data contains // indices to items and that includes busted ones. Just use // an empty description to indicate that the link is invalid. for (i=0; i cbGroup) { DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (item entry in invalid part of file) - skipping item.")); fOK = FALSE; goto AddItem; } #ifdef UNICODE SetFilePointer(fh, rgItems[i], NULL, FILE_BEGIN); ReadFile(fh, &itemdef, SIZEOF(itemdef), &cbRead, NULL); #else _llseek(fh, rgItems[i], 0); cbRead = _lread(fh, &itemdef, SIZEOF(itemdef)); #endif if (cbRead != SIZEOF(itemdef)) { DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (invalid definition) - skipping item %d."), i); fOK = FALSE; goto AddItem; } if (!CheckItemDefNT(&itemdef, cbGroup)) { DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (invalid item field) - skipping item %d."), i); fOK = FALSE; goto AddItem; } #ifdef UNICODE SetFilePointer(fh, itemdef.pName, NULL, FILE_BEGIN); ReadFile(fh, wszTemp, SIZEOF(wszTemp), &cbRead, NULL); #else _llseek(fh, itemdef.pName, 0); _lread(fh, wszTemp, SIZEOF(wszTemp)); // There will be a NUL somewhere #endif if (!*wszTemp) { DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (empty name) - skipping item %d."), i); fOK = FALSE; goto AddItem; } #ifdef UNICODE lstrcpy(szDesc, wszTemp); #else WideCharToMultiByte (CP_ACP, 0, wszTemp, -1, szDesc, ARRAYSIZE(szDesc), NULL, NULL); #endif #ifdef UNICODE SetFilePointer(fh, itemdef.pCommand, NULL, FILE_BEGIN); ReadFile(fh, &wszTemp, SIZEOF(wszTemp), &cbRead, NULL); #else _llseek(fh, itemdef.pCommand, 0); _lread(fh, wszTemp, SIZEOF(wszTemp)); #endif if (!*wszTemp) { DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file busted (empty command line) - skipping item %d."), i); // We use a null description to signal a problem with this item. szDesc[0] = TEXT('\0'); fOK = FALSE; goto AddItem; } #ifdef UNICODE lstrcpy(szCL, wszTemp); #else WideCharToMultiByte (CP_ACP, 0, wszTemp, -1, szCL, ARRAYSIZE(szCL), NULL, NULL); #endif #ifdef UNICODE SetFilePointer(fh, itemdef.pIconPath, NULL, FILE_BEGIN); ReadFile(fh, wszTemp, SIZEOF(wszTemp), &cbRead, NULL); #else _llseek(fh, itemdef.pIconPath, 0); _lread(fh, wszTemp, SIZEOF(wszTemp)); #endif if (!*wszTemp) { // NB Do nothing. Empty icon paths are legal - associated apps where the associated // app is missing will have an empty icon path. } #ifdef UNICODE lstrcpy(szIconPath, wszTemp); #else WideCharToMultiByte (CP_ACP, 0, wszTemp, -1, szIconPath, ARRAYSIZE(szIconPath), NULL, NULL); #endif // NB Forget about the icon data. // DebugMsg(DM_TRACE, "gc.gcnfo: Found item %s.", (LPSTR) szDesc); // Store away the data.... // NB We load the old commands line into the working dir field because // only the leaf is the command, the rest is the WD. Once we've been // through the tags section we can sort out the mess. AddItem: PMItem_Clear(&pmitem); #ifdef DEBUG DebugMsg(GC_TRACE, TEXT("gc.gaid: Desc %s"), (LPTSTR) szDesc); DebugMsg(GC_TRACE, TEXT(" WD: %s"), (LPTSTR) szCL); DebugMsg(GC_TRACE, TEXT(" IP: %s(%d)"), (LPTSTR) szIconPath, itemdef.iIcon); #endif // Don't store anything for items with invalid descriptions. if (*szDesc) { WORD wIconIndex; // Remove illegal chars. PathRemoveIllegalChars(szDesc, 0, PRICF_NORMAL); Str_SetPtr(&pmitem.lpszDesc, szDesc); Str_SetPtr(&pmitem.lpszWD, szCL); Str_SetPtr(&pmitem.lpszIconPath, szIconPath); wIconIndex = itemdef.wIconIndex; if ( wIconIndex == 0 ) { WORD wIcon; HICON hIcon; if ( *szIconPath == TEXT('\0') ) { FindExecutable(szCL,NULL,szIconPath); } if ( *szIconPath != TEXT('\0') ) { wIconIndex = FindAppropriateIcon( szIconPath, itemdef.iIcon); } } pmitem.wiIcon = wIconIndex; } DSA_InsertItem(hdsaPMItems, iItem, &pmitem); iItem++; } LocalFree((HLOCAL)rgItems); return fOK; } //--------------------------------------------------------------------------- // Create the links in the given dest dir. void CreateLinks(LPCTSTR lpszNewGrpPath, BOOL fStartup, INT cItemsStart) { int i, cItems; TCHAR szLinkName[MAX_PATH]; TCHAR szBuffer[MAX_PATH]; // we make this 3*MAX_PATH so that DARWIN and LOGO3 callers can pass their extra information TCHAR szExpBuff[3*MAX_PATH]; WCHAR wszPath[MAX_PATH]; LPTSTR lpszArgs; LPCTSTR dirs[2]; IShellLink *psl; LPTSTR pszExt; if (SUCCEEDED(ICoCreateInstance(&CLSID_ShellLink, &IID_IShellLink, &psl))) { IPersistFile *ppf; psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, &ppf); cItems = DSA_GetItemCount(hdsaPMItems); for (i = 0; i < cItems; i++) { LPPMITEM lppmitem = DSA_GetItemPtr(hdsaPMItems, i); // We show the progress in 2 halves. Group_SetProgress(cItemsStart+(i*cItemsStart/cItems)); // command line and args. // if this command line points to net drives we should add // the UNC mapping to the link Str_GetPtr(lppmitem->lpszCL, szBuffer, ARRAYSIZE(szBuffer)); // Spaces at the begining of the CL will confuse us. PathRemoveBlanks(szBuffer); lpszArgs = PathGetArgs(szBuffer); if (*lpszArgs) *(lpszArgs-1) = TEXT('\0'); // NB Special case, remove all links to Progman[.exe] from the // Startup Group. A lot of people put it there to give it a hotkey. // We want to be able to delete it regardless of its name ie we // can't just use setup.ini to do the work. if (fStartup) { if ((lstrcmpi(c_szProgmanExe, PathFindFileName(szBuffer)) == 0) || (lstrcmpi(c_szProgman, PathFindFileName(szBuffer)) == 0)) continue; } psl->lpVtbl->SetArguments(psl, lpszArgs); // // Remove quotes from the command file name NOW. // if (szBuffer[0] == TEXT('"')) { LPTSTR lpTemp; for (lpTemp = szBuffer+1; *lpTemp && *lpTemp != TEXT('"'); lpTemp++) *(lpTemp-1) = *lpTemp; if (*lpTemp == TEXT('"')) { *(lpTemp-1) = TEXT('\0'); } } // working directory // NB Progman assumed an empty WD meant use the windows // directory but we want to change this so to be // backwards compatable we'll fill in missing WD's here. if (!lppmitem->lpszWD || !*lppmitem->lpszWD) { // NB For links to pif's we don't fill in a default WD // so we'll pick it up from pif itself. This fixes a // problem upgrading some Compaq Deskpro's. pszExt = PathFindExtension(szBuffer); if (lstrcmpi(pszExt, c_szDotPif) == 0) { psl->lpVtbl->SetWorkingDirectory(psl, c_szNULL); } else { #ifdef WINNT // Avoid setting to %windir%, under NT we want to change to the users home directory. psl->lpVtbl->SetWorkingDirectory( psl, TEXT("%HOMEDRIVE%%HOMEPATH%") ); #else // Not a pif. Set the WD to be that of the windows dir. psl->lpVtbl->SetWorkingDirectory(psl, TEXT("%windir%")); #endif } } else { psl->lpVtbl->SetWorkingDirectory(psl, lppmitem->lpszWD); } // icon location // REVIEW, do we want to unqualify the icon path if possible? also, // if the icon path is the same as the command line we don't need it if (lppmitem->wiIcon != 0 || lstrcmpi(lppmitem->lpszIconPath, szBuffer) != 0) { // Remove args. lpszArgs = PathGetArgs(lppmitem->lpszIconPath); if (*lpszArgs) *(lpszArgs-1) = TEXT('\0'); psl->lpVtbl->SetIconLocation(psl, lppmitem->lpszIconPath, lppmitem->wiIcon); } else { psl->lpVtbl->SetIconLocation(psl, NULL, 0); } // hotkey psl->lpVtbl->SetHotkey(psl, lppmitem->wHotKey); // show command psl->lpVtbl->SetShowCmd(psl, lppmitem->nShowCmd); // Description. Currently pifmgr is the only guy // that cares about the description and they use // it to overide the default pif description. psl->lpVtbl->SetDescription(psl, lppmitem->lpszDesc); // // NOTE it is very important to set filename *last* // because if this is a group item to another link // (either .lnk or .pif) we want the link properties // to override the ones we just set. // // qualify path to subject (szBuffer) dirs[0] = lppmitem->lpszWD; dirs[1] = NULL; // Try expanding szBuffer ExpandEnvironmentStrings( szBuffer, szExpBuff, MAX_PATH ); szExpBuff[ MAX_PATH-1 ] = TEXT('\0'); if (!PathResolve(szExpBuff, dirs, PRF_TRYPROGRAMEXTENSIONS)) { // Just assume the expanded thing was a-ok... ExpandEnvironmentStrings(szBuffer, szExpBuff, MAX_PATH); szExpBuff[ MAX_PATH-1 ] = TEXT('\0'); } // all we need to call is setpath, it takes care of creating the // pidl for us. psl->lpVtbl->SetPath( psl, szBuffer ); #ifdef WINNT { IShellLinkDataList* psldl; if (SUCCEEDED(psl->lpVtbl->QueryInterface(psl, &IID_IShellLinkDataList, (LPVOID)&psldl))) { DWORD dwFlags; if (SUCCEEDED(psldl->lpVtbl->GetFlags(psldl, &dwFlags))) { if (lppmitem->bSepVdm) dwFlags |= SLDF_RUN_IN_SEPARATE; else dwFlags &= (~SLDF_RUN_IN_SEPARATE); psldl->lpVtbl->SetFlags(psldl, dwFlags); } psldl->lpVtbl->Release(psldl); } } #endif // over write the link if it already exists PathCombine(szLinkName, lpszNewGrpPath, lppmitem->lpszDesc); lstrcat(szLinkName, TEXT(".lnk")); PathQualify(szLinkName); // OLE string. StrToOleStrN(wszPath, ARRAYSIZE(wszPath), szLinkName, -1); ppf->lpVtbl->Save(ppf, wszPath, TRUE); } ppf->lpVtbl->Release(ppf); psl->lpVtbl->Release(psl); } } //---------------------------------------------------------------------------- // Returns TRUE if the specified group title is that of the startup group. BOOL StartupCmp(LPTSTR szGrp) { static TCHAR szOldStartupGrp[MAX_PATH]; TCHAR szNewStartupPath[MAX_PATH]; if (!*szOldStartupGrp) { // Was it over-ridden in progman ini? GetPrivateProfileString(c_szSettings, c_szStartup, c_szNULL, szOldStartupGrp, ARRAYSIZE(szOldStartupGrp), c_szProgmanIni); if (!*szOldStartupGrp) { LONG lResult; DWORD cbSize; // No, try reading it from the NT registry cbSize = MAX_PATH; lResult = RegQueryValue(HKEY_CURRENT_USER, c_szProgmanStartup, szOldStartupGrp, &cbSize ); // Potential porblem with Kana Start if ( lResult != ERROR_SUCCESS ) { // No, use the default name. LoadString(g_hinst, IDS_STARTUP, szOldStartupGrp, ARRAYSIZE(szOldStartupGrp)); } } if (*szOldStartupGrp) { // Yes, use the over-riding name by updating the registry. SHGetSpecialFolderPath(NULL, szNewStartupPath, CSIDL_PROGRAMS, FALSE); PathAddBackslash(szNewStartupPath); lstrcat(szNewStartupPath, szOldStartupGrp); DebugMsg(DM_TRACE, TEXT("gc.sc: Non-default Startup path is %s."), szNewStartupPath); Reg_SetString(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER_SHELLFOLDERS, c_szStartup, szNewStartupPath); } } // Does it match? if (*szOldStartupGrp && (lstrcmpi(szGrp, szOldStartupGrp) == 0)) return TRUE; else return FALSE; } //--------------------------------------------------------------------------- BOOL CALLBACK IsDescUnique(LPCTSTR lpsz, UINT n) { int i, cItems; LPPMITEM pitem; // DebugMsg(DM_TRACE, "gc.idu: Checking uniqueness of %s.", lpsz); cItems = DSA_GetItemCount(hdsaPMItems); for (i=0; ilpszDesc && *pitem->lpszDesc && (lstrcmpi(pitem->lpszDesc, lpsz) == 0)) { // DebugMsg(DM_TRACE, "gc.idu: Not Unique."); return FALSE; } } // Yep. can't find it, must be unique. // DebugMsg(DM_TRACE, "gc.idu: Unique."); return TRUE; } //--------------------------------------------------------------------------- // If there are two or more items with the same link name then change them so // that they are unique. void ResolveDuplicates(LPCTSTR pszNewGrpPath) { LPPMITEM pitem; int i, cItems; TCHAR szNew[MAX_PATH]; BOOL fLFN; int cchSpace; DebugMsg(DM_TRACE, TEXT("gc.rd: Fixing dups...")); // How much room is there for adding the #xx stuff? cchSpace = (ARRAYSIZE(szNew)-lstrlen(pszNewGrpPath))-2; if (cchSpace > 0) { // LFN's or no? fLFN = IsLFNDrive(pszNewGrpPath); if (!fLFN && cchSpace > 8) cchSpace = 8; // Fix dups cItems = DSA_GetItemCount(hdsaPMItems); for (i=0; i<(cItems-1); i++) { pitem = DSA_GetItemPtr(hdsaPMItems, i); Assert(pitem); YetAnotherMakeUniqueName(szNew, cchSpace, pitem->lpszDesc, IsDescUnique, i, fLFN); // Did we get a new name? if (lstrcmp(szNew, pitem->lpszDesc) != 0) { // Yep. DebugMsg(DM_TRACE, TEXT("gc.rd: %s to %s"), pitem->lpszDesc, szNew); Str_SetPtr(&pitem->lpszDesc, szNew); } } } DebugMsg(DM_TRACE, TEXT("gc.rd: Done.")); } //--------------------------------------------------------------------------- typedef struct { LPTSTR pszName; LPTSTR pszPath; LPTSTR pszModule; LPTSTR pszVer; } ALITEM; typedef ALITEM *PALITEM; //--------------------------------------------------------------------------- // Record the total list of apps in a DSA. void AppList_WriteFile(void) { int i, cItems; PALITEM palitem; TCHAR szBetaID[MAX_PATH]; TCHAR szLine[4*MAX_PATH]; HANDLE hFile; DWORD cbWritten; Assert(g_hdsaAppList); cItems = DSA_GetItemCount(g_hdsaAppList); if (cItems) { // Get the beta ID. szBetaID[0] = TEXT('\0'); Reg_GetString(HKEY_LOCAL_MACHINE, c_szRegistry, c_szDefaultUser, szBetaID, SIZEOF(szBetaID)); // Ick - Hard coded file name and in the current dir! hFile = CreateFile(c_szGrpConvData, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { for (i=0; i < cItems; i++) { palitem = DSA_GetItemPtr(g_hdsaAppList, i); wsprintf(szLine, TEXT("%s,\"%s\",\"%s\",\"%s\",\"%s\",,,\r\n"), szBetaID, palitem->pszName, palitem->pszPath, palitem->pszModule, palitem->pszVer); DebugMsg(DM_TRACE,TEXT("gc.al_wf: %s"), szLine); WriteFile(hFile, szLine, lstrlen(szLine)*SIZEOF(TCHAR), &cbWritten, NULL); } CloseHandle(hFile); } else { DebugMsg(DM_ERROR, TEXT("gc.al_wf: Can't write file.")); } } else { DebugMsg(DM_TRACE, TEXT("gc.al_wf: Empty app list. Nothing to write.")); } } //--------------------------------------------------------------------------- //#define DSA_AppendItem(hdsa, pitem) DSA_InsertItem(hdsa, 0x7fff, pitem) //--------------------------------------------------------------------------- static TCHAR const c_szTranslation[] = TEXT("\\VarFileInfo\\Translation"); static TCHAR const c_szStringFileInfo[] = TEXT("\\StringFileInfo\\"); static TCHAR const c_szEngLangCharSet[] = TEXT("040904e4"); static TCHAR const c_szSlash[] = TEXT("\\"); static TCHAR const c_szInternalName[] = TEXT("InternalName"); static TCHAR const c_szProductVersion[] = TEXT("ProductVersion"); //---------------------------------------------------------------------------- // Semi-decent wrappers around the not very good ver apis. BOOL Ver_GetDefaultCharSet(const PVOID pBuf, LPTSTR pszLangCharSet, int cbLangCharSet) { LPWORD pTransTable; DWORD cb; Assert(pszLangCharSet); Assert(cbLangCharSet > 8); if (VerQueryValue(pBuf, (LPTSTR)c_szTranslation, &pTransTable, &cb)) { wsprintf(pszLangCharSet, TEXT("%04X%04X"), *pTransTable, *(pTransTable+1)); return TRUE; } return FALSE; } //---------------------------------------------------------------------------- // Semi-decent wrappers around the not very good ver apis. BOOL Ver_GetStringFileInfo(PVOID pBuf, LPCTSTR pszLangCharSet, LPCTSTR pszStringName, LPTSTR pszValue, int cbValue) { TCHAR szSubBlock[MAX_PATH]; LPTSTR pszBuf; DWORD cbBuf; lstrcpy(szSubBlock, c_szStringFileInfo); lstrcat(szSubBlock, pszLangCharSet); lstrcat(szSubBlock, c_szSlash); lstrcat(szSubBlock, pszStringName); if (VerQueryValue(pBuf, szSubBlock, &pszBuf, &cbBuf)) { lstrcpyn(pszValue, pszBuf, cbValue); return TRUE; } return FALSE; } //--------------------------------------------------------------------------- void GetVersionInfo(LPTSTR pszPath, LPTSTR pszModule, int cbModule, LPTSTR pszVer, int cbVer) { DWORD cbBuf; LPVOID pBuf; TCHAR szCharSet[MAX_PATH]; DWORD dwWasteOfAnAuto; Assert(pszModule); Assert(pszVer); pszModule[0] = TEXT('\0'); pszVer[0] = TEXT('\0'); cbBuf = GetFileVersionInfoSize(pszPath, &dwWasteOfAnAuto); if (cbBuf) { pBuf = SHAlloc(cbBuf); if (pBuf) { if (GetFileVersionInfo(pszPath, 0, cbBuf, pBuf)) { // Try the default language from the translation tables. if (Ver_GetDefaultCharSet(pBuf, szCharSet, ARRAYSIZE(szCharSet))) { Ver_GetStringFileInfo(pBuf, szCharSet, c_szInternalName, pszModule, cbModule); Ver_GetStringFileInfo(pBuf, szCharSet, c_szProductVersion, pszVer, cbVer); } else { // Try the same language as us. LoadString(g_hinst, IDS_DEFLANGCHARSET, szCharSet, ARRAYSIZE(szCharSet)); Ver_GetStringFileInfo(pBuf, szCharSet, c_szInternalName, pszModule, cbModule); Ver_GetStringFileInfo(pBuf, szCharSet, c_szProductVersion, pszVer, cbVer); } // Last chance - try English. if (!*pszModule) Ver_GetStringFileInfo(pBuf, c_szEngLangCharSet, c_szInternalName, pszModule, cbModule); if (!*pszVer) Ver_GetStringFileInfo(pBuf, c_szEngLangCharSet, c_szProductVersion, pszVer, cbVer); } else { DebugMsg(DM_TRACE, TEXT("gc.gvi: Can't get version info.")); } SHFree(pBuf); } else { DebugMsg(DM_TRACE, TEXT("gc.gvi: Can't allocate version info buffer.")); } } else { DebugMsg(DM_TRACE, TEXT("gc.gvi: No version info.")); } } //--------------------------------------------------------------------------- // Record the total list of apps in a DSA. BOOL AppList_Create(void) { Assert(!g_hdsaAppList); g_hdsaAppList = DSA_Create(SIZEOF(ALITEM), 0); if (g_hdsaAppList) { return TRUE; } else { DebugMsg(DM_ERROR, TEXT("gc.al_c: Can't create app list.")); return FALSE; } } //--------------------------------------------------------------------------- // Record the total list of apps in a DSA. void AppList_Destroy(void) { int i, cItems; PALITEM palitem; Assert(g_hdsaAppList); cItems = DSA_GetItemCount(g_hdsaAppList); for (i=0; i < cItems; i++) { palitem = DSA_GetItemPtr(g_hdsaAppList, i); if (palitem->pszName) SHFree(palitem->pszName); if (palitem->pszPath) SHFree(palitem->pszPath); if (palitem->pszModule) SHFree(palitem->pszModule); if (palitem->pszVer) SHFree(palitem->pszVer); } DSA_Destroy(g_hdsaAppList); g_hdsaAppList = NULL; } //--------------------------------------------------------------------------- // Record the total list of apps in a DSA. void AppList_Append(void) { int i, cItems; // char szName[MAX_PATH]; // char szPath[MAX_PATH]; TCHAR szModule[MAX_PATH]; TCHAR szVer[MAX_PATH]; TCHAR szCL[MAX_PATH]; LPTSTR lpszArgs; LPCTSTR dirs[2]; ALITEM alitem; Assert(g_hdsaAppList); cItems = DSA_GetItemCount(hdsaPMItems); for (i = 0; i < cItems; i++) { LPPMITEM lppmitem = DSA_GetItemPtr(hdsaPMItems, i); // We show the progress in 2 halves. Group_SetProgress(cItems+i); // Command line and args. Str_GetPtr(lppmitem->lpszCL, szCL, ARRAYSIZE(szCL)); lpszArgs = PathGetArgs(szCL); if (*lpszArgs) *(lpszArgs-1) = TEXT('\0'); dirs[0] = lppmitem->lpszWD; dirs[1] = NULL; PathResolve(szCL, dirs, PRF_TRYPROGRAMEXTENSIONS); // Version info. GetVersionInfo(szCL, szModule, ARRAYSIZE(szModule), szVer, ARRAYSIZE(szVer)); alitem.pszName = NULL; alitem.pszPath = NULL; alitem.pszModule = NULL; alitem.pszVer = NULL; Str_SetPtr(&alitem.pszName, lppmitem->lpszDesc); Str_SetPtr(&alitem.pszPath, szCL); Str_SetPtr(&alitem.pszModule, szModule); Str_SetPtr(&alitem.pszVer, szVer); DSA_AppendItem(g_hdsaAppList, &alitem); } DebugMsg(DM_TRACE, TEXT("gc.al_a: %d items"), DSA_GetItemCount(g_hdsaAppList)); } //--------------------------------------------------------------------------- // Reads an old format Progman Group files and creates a directory containing // links that matches the group file. BOOL Group_CreateNewFromOld(HWND hwnd, LPCTSTR lpszOldGrpPath, UINT options) { GROUPDEF grpdef; #ifdef UNICODE HANDLE fh; DWORD dwBytesRead; #else HFILE fh; #endif TCHAR szNewGrpPath[MAX_PATH]; TCHAR szOldGrpTitle[MAXGROUPNAMELEN + 1]; // LPSTR lpszExt; BOOL fStatus = FALSE; SHELLEXECUTEINFO sei; BOOL fStartup = FALSE; if (!ItemList_Create(lpszOldGrpPath)) return FALSE; #ifdef UNICODE fh = CreateFile( lpszOldGrpPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if (fh == INVALID_HANDLE_VALUE) { #else fh = _lopen(lpszOldGrpPath, OF_READ | OF_SHARE_DENY_NONE); if (fh == HFILE_ERROR) { #endif DebugMsg(DM_ERROR, TEXT("gc.gcnfo: Unable to open group.")); goto ProcExit2; } #ifdef UNICODE if ((!ReadFile(fh, &grpdef, SIZEOF(grpdef), &dwBytesRead, NULL)) || (dwBytesRead != SIZEOF(grpdef))) { #else if (_lread(fh, &grpdef, SIZEOF(grpdef)) != SIZEOF(grpdef)) { #endif DebugMsg(DM_ERROR, TEXT("gc.gcnfo: header too small.")); goto ProcExit; } if (grpdef.cItems > 50) { // NB This isn;t fatal so carry on. DebugMsg(DM_ERROR, TEXT("gc.gcnfo: Too many items.")); } // Check to make sure there is a name embedded in the // .grp file. If not, just use the filename if (grpdef.pName==0) { LPTSTR lpszFile, lpszExt, lpszDest = szOldGrpTitle; lpszFile = PathFindFileName( lpszOldGrpPath ); lpszExt = PathFindExtension( lpszOldGrpPath ); for( ; lpszFile && lpszExt && (lpszFile != lpszExt); *lpszDest++ = *lpszFile++ ); *lpszDest = TEXT('\0'); } else { #ifdef UNICODE CHAR szAnsiTitle[ MAXGROUPNAMELEN + 1 ]; SetFilePointer(fh, grpdef.pName, NULL, FILE_BEGIN); ReadFile(fh, szAnsiTitle, SIZEOF(szAnsiTitle), &dwBytesRead, NULL); MultiByteToWideChar( CP_ACP, 0, szAnsiTitle, -1, szOldGrpTitle, ARRAYSIZE(szOldGrpTitle) ); #else _llseek(fh, grpdef.pName, 0); _lread(fh, szOldGrpTitle, SIZEOF(szOldGrpTitle)); #endif } // Get the destination dir, use the title from the old group... // Special case the startup group. if (StartupCmp(szOldGrpTitle)) { fStartup = TRUE; if (g_fDoingCommonGroups) { SHGetSpecialFolderPath(hwnd, szNewGrpPath, CSIDL_COMMON_STARTUP, TRUE); } else { SHGetSpecialFolderPath(hwnd, szNewGrpPath, CSIDL_STARTUP, TRUE); } } else { if (!Group_GenerateNewGroupPath(hwnd, szOldGrpTitle, szNewGrpPath, lpszOldGrpPath)) { DebugMsg(DM_ERROR, TEXT("gc.gcnfo; Unable to create destination directory.")); goto ProcExit; } } // PathQualify(szNewGrpPath); // ResolveDuplicateGroupNames(szNewGrpPath); // Go through every item in the old group and make it a link... if (!GetAllItemData(fh, grpdef.cItems, grpdef.cbGroup, szOldGrpTitle, szNewGrpPath)) { if (options & GC_REPORTERROR) MyMessageBox(hwnd, IDS_APPTITLE, IDS_BADOLDGROUP, NULL, MB_OK | MB_ICONEXCLAMATION); } // Deal with the tags section. HandleTags(fh, grpdef.cbGroup); // Now we've dealt with the tags we don't need to keep track of // busted items so delete them now. From here on we always have // valid items. DeleteBustedItems(); // Shorten descs on non-lfn drives. if (!IsLFNDrive(szNewGrpPath)) ShortenDescriptions(); // Fixup the paths/WD stuff. MungePaths(); // Fix dups. ResolveDuplicates(szNewGrpPath); // Do we just want a list of the apps or create some links? if (options & GC_BUILDLIST) AppList_Append(); else CreateLinks(szNewGrpPath, fStartup, grpdef.cItems); // Get the cabinet to show the new group. if (options & GC_OPENGROUP) { sei.cbSize = SIZEOF(sei); sei.fMask = 0; sei.hwnd = hwnd; sei.lpVerb = NULL; sei.lpFile = szNewGrpPath; sei.lpParameters = NULL; sei.lpDirectory = NULL; sei.lpClass = NULL; sei.nShow = SW_SHOWNORMAL; sei.hInstApp = g_hinst; // ShellExecute(hwnd, NULL, szNewGrpPath, NULL, NULL, SW_SHOWNORMAL); ShellExecuteEx(&sei); } // Everything went OK. fStatus = TRUE; ProcExit: #ifdef UNICODE CloseHandle(fh); #else _lclose(fh); #endif #ifndef WINNT // we only need to call Group_DeleteIfRequired // when we are on a Japanese language machine (win95J or win98J). We // should have a runtime check for Japanese here. // Delete old group file when it is specified in special // registry entry. Bug#7259-win95d // if (fStatus == TRUE) { // delete it only if the conversion was successful. Group_DeleteIfRequired(szOldGrpTitle,lpszOldGrpPath); } #endif // !WINNT ProcExit2: ItemList_Destroy(); return fStatus; } //--------------------------------------------------------------------------- // Reads an NT format Progman Group files and creates a directory containing // links that matches the group file. BOOL Group_CreateNewFromOldNT(HWND hwnd, LPCTSTR lpszOldGrpPath, UINT options) { NT_GROUPDEF grpdef; #ifdef UNICODE HANDLE fh; DWORD dwBytesRead; #else HFILE fh; #endif TCHAR szNewGrpPath[MAX_PATH]; WCHAR szOldGrpTitleUnicode[MAXGROUPNAMELEN + 1]; TCHAR szOldGrpTitle[MAXGROUPNAMELEN + 1]; // LPSTR lpszExt; BOOL fStatus = FALSE; SHELLEXECUTEINFO sei; BOOL fStartup = FALSE; if (!ItemList_Create(lpszOldGrpPath)) return FALSE; #ifdef UNICODE fh = CreateFile( lpszOldGrpPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if (fh == INVALID_HANDLE_VALUE) { #else fh = _lopen(lpszOldGrpPath, OF_READ | OF_SHARE_DENY_NONE); if (fh == HFILE_ERROR) { #endif DebugMsg(DM_ERROR, TEXT("gc.gcnfont: Unable to open group.")); goto ProcExit2; } #ifdef UNICODE if (!ReadFile(fh, &grpdef, SIZEOF(grpdef), &dwBytesRead, NULL) || dwBytesRead != SIZEOF(grpdef)) { #else if (_lread(fh, &grpdef, SIZEOF(grpdef)) != SIZEOF(grpdef)) { #endif DebugMsg(DM_ERROR, TEXT("gc.gcnfont: header too small.")); goto ProcExit; } if (grpdef.cItems > 50) { // NB This isn;t fatal so carry on. DebugMsg(DM_ERROR, TEXT("gc.gcnfont: Too many items.")); } #ifdef UNICODE SetFilePointer(fh, grpdef.pName, NULL, FILE_BEGIN); ReadFile(fh, szOldGrpTitleUnicode, SIZEOF(szOldGrpTitleUnicode), &dwBytesRead, NULL); #else _llseek(fh, grpdef.pName, 0); _lread(fh, szOldGrpTitleUnicode, SIZEOF(szOldGrpTitleUnicode)); #endif #ifdef UNICODE lstrcpy(szOldGrpTitle, szOldGrpTitleUnicode); #else WideCharToMultiByte (CP_ACP, 0, szOldGrpTitleUnicode, -1, szOldGrpTitle, MAXGROUPNAMELEN+1, NULL, NULL); #endif // Get the destination dir, use the title from the old group. // REVIEW UNDONE - until we get long filenames we'll use the old // groups' filename as the basis for the new group instead of it's // title. // Special case the startup group. if (StartupCmp(szOldGrpTitle)) { if (g_fDoingCommonGroups) { SHGetSpecialFolderPath(hwnd, szNewGrpPath, CSIDL_COMMON_STARTUP, TRUE); } else { SHGetSpecialFolderPath(hwnd, szNewGrpPath, CSIDL_STARTUP, TRUE); } } else { if (!Group_GenerateNewGroupPath(hwnd, szOldGrpTitle, szNewGrpPath, lpszOldGrpPath)) { DebugMsg(DM_ERROR, TEXT("gc.gcnfo; Unable to create destination directory.")); goto ProcExit; } } // Go through every item in the old group and make it a link... if (!GetAllItemDataNT(fh, grpdef.cItems, grpdef.cbGroup, szOldGrpTitle, szNewGrpPath)) { if (options & GC_REPORTERROR) MyMessageBox(hwnd, IDS_APPTITLE, IDS_BADOLDGROUP, NULL, MB_OK | MB_ICONEXCLAMATION); } // Deal with the tags section. HandleTagsNT(fh, grpdef.cbGroup); // Now we've dealt with the tags we don't need to keep track of // busted items so delete them now. From here on we always have // valid items. DeleteBustedItems(); // Shorten descs on non-lfn drives. if (!IsLFNDrive(szNewGrpPath)) ShortenDescriptions(); // Fixup the paths/WD stuff. MungePaths(); // Fix dups. ResolveDuplicates(szNewGrpPath); // Do we just want a list of the apps or create some links? if (options & GC_BUILDLIST) AppList_Append(); else CreateLinks(szNewGrpPath, fStartup, grpdef.cItems); // Get the cabinet to show the new group. if (options & GC_OPENGROUP) { sei.cbSize = SIZEOF(sei); sei.fMask = 0; sei.hwnd = hwnd; sei.lpVerb = NULL; sei.lpFile = szNewGrpPath; sei.lpParameters = NULL; sei.lpDirectory = NULL; sei.lpClass = NULL; sei.nShow = SW_SHOWNORMAL; sei.hInstApp = g_hinst; // ShellExecute(hwnd, NULL, szNewGrpPath, NULL, NULL, SW_SHOWNORMAL); ShellExecuteEx(&sei); } // Everything went OK. fStatus = TRUE; ProcExit: #ifdef UNICODE CloseHandle(fh); #else _lclose(fh); #endif ProcExit2: ItemList_Destroy(); return fStatus; } //--------------------------------------------------------------------------- // Record the last write date/time of the given group in the ini file. void Group_WriteLastModDateTime(LPCTSTR lpszGroupFile,DWORD dwLowDateTime) { Reg_SetStruct(g_hkeyGrpConv, c_szGroups, lpszGroupFile, &dwLowDateTime, SIZEOF(dwLowDateTime)); } //--------------------------------------------------------------------------- // Read the last write date/time of the given group from the ini file. DWORD Group_ReadLastModDateTime(LPCTSTR lpszGroupFile) { DWORD dwDateTime = 0; Reg_GetStruct(g_hkeyGrpConv, c_szGroups, lpszGroupFile, &dwDateTime, SIZEOF(dwDateTime)); return dwDateTime; } //--------------------------------------------------------------------------- // Convert the given group to the new format. // Returns FALSE if something goes wrong. // Returns true if the given group got converted or the user cancelled. BOOL Group_Convert(HWND hwnd, LPCTSTR lpszOldGrpFile, UINT options) { TCHAR szGroupTitle[MAXGROUPNAMELEN + 1]; // PM Groups had a max title len of 30. BOOL fStatus; WIN32_FIND_DATA fd; HANDLE hff; UINT nCode; UINT iErrorId; Log(TEXT("Grp: %s"), lpszOldGrpFile); DebugMsg(DM_TRACE, TEXT("gc.gc: Converting group %s"), (LPTSTR) lpszOldGrpFile); // Does the group exist? if (PathFileExists(lpszOldGrpFile)) { // Group exists - is it valid? nCode = Group_ValidOldFormat(lpszOldGrpFile, szGroupTitle); switch( nCode ) { case VOF_WINNT: case VOF_WIN31: // Yes - ask for confirmation. if (!(options & GC_PROMPTBEFORECONVERT) || MyMessageBox(hwnd, IDS_APPTITLE, IDS_OKTOCONVERT, szGroupTitle, MB_YESNO) == IDYES) { // Everything went OK? if ( nCode == VOF_WIN31 ) { fStatus = Group_CreateNewFromOld(hwnd,lpszOldGrpFile, options); } else { fStatus = Group_CreateNewFromOldNT(hwnd,lpszOldGrpFile, options); } if ( fStatus ) { iErrorId = 0; } else { // Nope - FU. Warn and exit. iErrorId = IDS_CONVERTERROR; } } else { // User cancelled... iErrorId = 0; } break; default: case VOF_BAD: { // Nope, File is invalid. // Warn user. iErrorId = IDS_NOTGROUPFILE; } break; } } else { // Nope, File doesn't even exist. iErrorId = IDS_MISSINGFILE; } if ( iErrorId != 0 ) { if (options & GC_REPORTERROR) { MyMessageBox(hwnd, IDS_APPTITLE, iErrorId, lpszOldGrpFile, MB_OK|MB_ICONEXCLAMATION); } Log(TEXT("Grp: %s done."), lpszOldGrpFile); return FALSE; } else { DebugMsg(DM_TRACE, TEXT("gc.gc: Done.")); Log(TEXT("Grp: %s done."), lpszOldGrpFile); return TRUE; } } //--------------------------------------------------------------------------- // Checks the date/time stamp of the given group against the one in // grpconv.ini BOOL GroupHasBeenModified(LPCTSTR lpszGroupFile) { WIN32_FIND_DATA fd; HANDLE hff; BOOL fModified; hff = FindFirstFile(lpszGroupFile, &fd); if (hff != INVALID_HANDLE_VALUE) { if (Group_ReadLastModDateTime(lpszGroupFile) != fd.ftLastWriteTime.dwLowDateTime) { DebugMsg(DM_TRACE, TEXT("cg.ghbm: Group %s has been modified."), (LPTSTR)lpszGroupFile); fModified = TRUE; } else { DebugMsg(DM_TRACE, TEXT("cg.ghbm: Group %s has not been modified."), (LPTSTR)lpszGroupFile); fModified = FALSE; } FindClose(hff); return fModified; } else { // Hmm, file doesn't exist, pretend it's up to date. return TRUE; } } //--------------------------------------------------------------------------- // Converts a group file from its NT registry into a real file on disk. Since // the disk format for NT 1.0 files never existed and collided in its usage // the GROUP_MAGIC file type, we will convert it from the registry, directly // into a GROUP_UNICODE format file. In this way we will always be able to // distiguish the NT group files from the Win 3.1 group files. BOOL MakeGroupFile( LPTSTR lpFileName, LPTSTR lpGroupName) { LONG lResult; DWORD cbSize; HGLOBAL hBuffer; HGLOBAL hNew; LPBYTE lpBuffer; BOOL fOk; HANDLE hFile; HKEY hkey; DWORD cbWrote; fOk = FALSE; lResult = RegOpenKeyEx(hkeyGroups, lpGroupName, 0, KEY_READ, &hkey ); if ( lResult != ERROR_SUCCESS ) { return FALSE; } lResult = RegQueryValueEx( hkey, NULL, NULL, NULL, NULL, &cbSize); if ( lResult != ERROR_SUCCESS ) { goto CleanupKey; } hBuffer = GlobalAlloc(GMEM_MOVEABLE,cbSize); if ( hBuffer == NULL ) { goto CleanupKey; } lpBuffer = (LPBYTE)GlobalLock(hBuffer); if ( lpBuffer == NULL ) { goto CleanupMem; } lResult = RegQueryValueEx( hkey, NULL, NULL, NULL, lpBuffer, &cbSize ); if ( lResult != ERROR_SUCCESS ) { goto Cleanup; } if ( *(DWORD *)lpBuffer == GROUP_MAGIC ) { HGLOBAL hNew; cbSize = ConvertToUnicodeGroup( (LPNT_GROUPDEF_A)lpBuffer, &hNew ); GlobalUnlock( hBuffer ); GlobalFree( hBuffer ); hBuffer = hNew; lpBuffer = GlobalLock( hBuffer ); if ( lpBuffer == NULL ) { goto CleanupMem; } } hFile = CreateFile(lpFileName,GENERIC_WRITE,0,NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); if (hFile != INVALID_HANDLE_VALUE) { fOk = WriteFile(hFile,lpBuffer,cbSize,&cbWrote,NULL); CloseHandle(hFile); } Cleanup: GlobalUnlock(hBuffer); CleanupMem: GlobalFree(hBuffer); CleanupKey: RegCloseKey( hkey ); return fOk; } #define BIG_STEP 1024 //---------------------------------------------------------------------------- // Enumerate all the groups or just all the modified groups. int Group_Enum(PFNGRPCALLBACK pfncb, BOOL fProgress, BOOL fModifiedOnly) { TCHAR szIniFile[MAX_PATH], szFile[MAX_PATH]; FILETIME ft; UINT uSize; LPTSTR pSection, pKey; int cGroups = 0; HANDLE hFile; WIN32_FIND_DATA fd; if (!FindProgmanIni(szIniFile)) return 0; for (uSize = BIG_STEP; uSize < BIG_STEP * 8; uSize += BIG_STEP) { pSection = (LPTSTR)LocalAlloc(LPTR, uSize); if (!pSection) return 0; if ((UINT)GetPrivateProfileString(c_szGroups, NULL, c_szNULL, pSection, uSize / sizeof(pSection[0]), szIniFile) < uSize - 5) break; LocalFree((HLOCAL)pSection); pSection = NULL; } if (!pSection) return 0; if (fProgress) Group_CreateProgressDlg(); for (pKey = pSection; *pKey; pKey += lstrlen(pKey) + 1) { GetPrivateProfileString(c_szGroups, pKey, c_szNULL, szFile, ARRAYSIZE(szFile), szIniFile); if (szFile[0]) { if (!fModifiedOnly || GroupHasBeenModified(szFile)) { (*pfncb)(szFile); cGroups++; hFile = FindFirstFile (szFile, &fd); if (hFile != INVALID_HANDLE_VALUE) { FindClose (hFile); Group_WriteLastModDateTime(szFile, fd.ftLastWriteTime.dwLowDateTime); } } } } // Cabinet uses the date/time of progman.ini as a hint to speed things up // so set it here so we won't run automatically again. GetSystemTimeAsFileTime(&ft); Group_WriteLastModDateTime(szIniFile,ft.dwLowDateTime); LocalFree((HLOCAL)pSection); if (fProgress) Group_DestroyProgressDlg(); return cGroups; } //---------------------------------------------------------------------------- // Enumerate all the NT groups or just all the modified groups. int Group_EnumNT(PFNGRPCALLBACK pfncb, BOOL fProgress, BOOL fModifiedOnly, HKEY hKeyRoot, LPCTSTR lpKey) { LONG lResult; DWORD dwSubKey = 0; TCHAR szGroupName[MAXGROUPNAMELEN+1]; TCHAR szFileName[MAX_PATH]; TCHAR szTempFileDir[MAX_PATH]; TCHAR szTempFileName[MAX_PATH]; DWORD cchGroupNameLen; FILETIME ft; BOOL fOk; BOOL fDialog = FALSE; BOOL fProcess; int cGroups = 0; // // Look for groups in the registry // lResult = RegOpenKeyEx(hKeyRoot, lpKey, 0, KEY_READ, &hkeyGroups ); if ( lResult != ERROR_SUCCESS ) { return 0; } while ( TRUE ) { cchGroupNameLen = ARRAYSIZE(szGroupName); lResult = RegEnumKeyEx( hkeyGroups, dwSubKey, szGroupName, &cchGroupNameLen, NULL, NULL, NULL, &ft ); szGroupName[MAXGROUPNAMELEN] = TEXT('\0'); if ( lResult == ERROR_NO_MORE_ITEMS ) { break; } if ( lResult == ERROR_SUCCESS ) { GetWindowsDirectory(szFileName, ARRAYSIZE(szFileName)); // Save this dir for use by GetTempFileName below lstrcpy(szTempFileDir, szFileName); #ifdef WINNT GetEnvironmentVariable(TEXT("USERPROFILE"), szTempFileDir, MAX_PATH); #endif lstrcat(szFileName,TEXT("\\")); lstrcat(szFileName,szGroupName); lstrcat(szFileName,TEXT(".grp")); // // If the key has been modified since we last processed it, // then time to process it again. // fProcess = FALSE; if (fModifiedOnly) { if ( Group_ReadLastModDateTime(szFileName) != ft.dwLowDateTime ) { fProcess = TRUE; } } else { fProcess = TRUE; } if (fProcess) { if (GetTempFileName(szTempFileDir,TEXT("grp"),0,szTempFileName) != 0) { fOk = MakeGroupFile(szTempFileName,szGroupName); if ( fOk ) { if (fProgress && !fDialog) { Group_CreateProgressDlg(); fDialog = TRUE; } (*pfncb)(szTempFileName); DeleteFile(szTempFileName); Group_WriteLastModDateTime(szFileName,ft.dwLowDateTime); cGroups++; } } } } dwSubKey++; } RegCloseKey( hkeyGroups ); hkeyGroups = NULL; if (fProgress && fDialog) Group_DestroyProgressDlg(); return cGroups; } //--------------------------------------------------------------------------- // Find the progman ini from before an upgrade. BOOL FindOldProgmanIni(LPTSTR pszPath) { if (Reg_GetString(HKEY_LOCAL_MACHINE, REGSTR_PATH_SETUP, REGSTR_VAL_OLDWINDIR, pszPath, MAX_PATH*SIZEOF(TCHAR))) { PathAppend(pszPath, c_szProgmanIni); if (PathFileExists(pszPath)) { return TRUE; } DebugMsg(DM_ERROR, TEXT("Can't find old progman.ini")); return FALSE; } return FALSE; } //---------------------------------------------------------------------------- // Enumerate all the old groups. void Group_EnumOldGroups(PFNGRPCALLBACK pfncb, BOOL fProgress) { TCHAR szIniFile[MAX_PATH], szFile[MAX_PATH]; UINT uSize; LPTSTR pSection, pKey; if (!FindOldProgmanIni(szIniFile)) return; for (uSize = BIG_STEP; uSize < BIG_STEP * 8; uSize += BIG_STEP) { pSection = (LPTSTR)LocalAlloc(LPTR, uSize); if (!pSection) return; if ((UINT)GetPrivateProfileString(c_szGroups, NULL, c_szNULL, pSection, uSize / sizeof(pSection[0]), szIniFile) < uSize - 5) break; LocalFree((HLOCAL)pSection); pSection = NULL; } if (!pSection) return; if (fProgress) Group_CreateProgressDlg(); for (pKey = pSection; *pKey; pKey += lstrlen(pKey) + 1) { GetPrivateProfileString(c_szGroups, pKey, c_szNULL, szFile, ARRAYSIZE(szFile), szIniFile); if (szFile[0]) { (*pfncb)(szFile); } } if (fProgress) Group_DestroyProgressDlg(); LocalFree((HLOCAL)pSection); } //---------------------------------------------------------------------------- // Given a pidl for a link, extract the appropriate info and append it to // the app list. void AppList_AppendCurrentItem(LPITEMIDLIST pidlFolder, LPSHELLFOLDER psf, LPITEMIDLIST pidlItem, IShellLink *psl, IPersistFile *ppf) { STRRET str; WCHAR wszPath[MAX_PATH]; TCHAR szName[MAX_PATH]; TCHAR sz[MAX_PATH]; TCHAR szPath[MAX_PATH]; TCHAR szModule[MAX_PATH]; TCHAR szVer[MAX_PATH]; ALITEM alitem; if (SUCCEEDED(psf->lpVtbl->GetDisplayNameOf(psf, pidlItem, SHGDN_NORMAL, &str))) { // Get the name. StrRetToStrN(szName, ARRAYSIZE(szName), &str, pidlItem); DebugMsg(DM_TRACE, TEXT("c.gi_gi: Link %s"), szName); // Get the path from the link... SHGetPathFromIDList(pidlFolder, sz); PathAppend(sz, szName); lstrcat(sz, TEXT(".lnk")); StrToOleStrN(wszPath, ARRAYSIZE(wszPath), sz, -1); ppf->lpVtbl->Load(ppf, wszPath, 0); // Copy all the data. szPath[0] = TEXT('\0'); if (SUCCEEDED(psl->lpVtbl->GetPath(psl, szPath, ARRAYSIZE(szPath), NULL, SLGP_SHORTPATH))) { // Valid CL? if (szPath[0]) { GetVersionInfo(szPath, szModule, ARRAYSIZE(szModule), szVer, sizeof(szVer)); alitem.pszName = NULL; alitem.pszPath = NULL; alitem.pszModule = NULL; alitem.pszVer = NULL; Str_SetPtr(&alitem.pszName, szName); Str_SetPtr(&alitem.pszPath, szPath); Str_SetPtr(&alitem.pszModule, szModule); Str_SetPtr(&alitem.pszVer, szVer); DSA_AppendItem(g_hdsaAppList, &alitem); } } } } //---------------------------------------------------------------------------- HRESULT AppList_ShellFolderEnum(LPITEMIDLIST pidlFolder, LPSHELLFOLDER psf) { HRESULT hres; LPENUMIDLIST penum; IShellLink *psl; LPITEMIDLIST pidlItem; UINT celt; IPersistFile *ppf; DWORD dwAttribs; LPSHELLFOLDER psfItem; LPITEMIDLIST pidlPath; DebugMsg(DM_TRACE, TEXT("gc.al_sfe: Enum...")); hres = psf->lpVtbl->EnumObjects(psf, (HWND)NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &penum); if (SUCCEEDED(hres)) { hres = ICoCreateInstance(&CLSID_ShellLink, &IID_IShellLink, &psl); if (SUCCEEDED(hres)) { psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, &ppf); while ((penum->lpVtbl->Next(penum, 1, &pidlItem, &celt) == NOERROR) && (celt == 1)) { dwAttribs = SFGAO_LINK|SFGAO_FOLDER; if (SUCCEEDED(psf->lpVtbl->GetAttributesOf(psf, 1, &pidlItem, &dwAttribs))) { // Is it a folder if (dwAttribs & SFGAO_FOLDER) { // Recurse. DebugMsg(DM_TRACE, TEXT("al_sfe: Folder.")); hres = psf->lpVtbl->BindToObject(psf, pidlItem, NULL, &IID_IShellFolder, &psfItem); if (SUCCEEDED(hres)) { pidlPath = ILCombine(pidlFolder, pidlItem); if (pidlPath) { AppList_ShellFolderEnum(pidlPath, psfItem); psfItem->lpVtbl->Release(psfItem); ILFree(pidlPath); } } } else if (dwAttribs & SFGAO_LINK) { // Regular link, add it to the list. DebugMsg(DM_TRACE, TEXT("al_sfe: Link.")); AppList_AppendCurrentItem(pidlFolder, psf, pidlItem, psl, ppf); } } SHFree(pidlItem); } ppf->lpVtbl->Release(ppf); psl->lpVtbl->Release(psl); } penum->lpVtbl->Release(penum); } return hres; } //---------------------------------------------------------------------------- void Applist_SpecialFolderEnum(int nFolder) { HRESULT hres; LPITEMIDLIST pidlGroup; LPSHELLFOLDER psf, psfDesktop; TCHAR sz[MAX_PATH]; // Get the group info. if (SHGetSpecialFolderPath(NULL, sz, nFolder, FALSE)) { pidlGroup = ILCreateFromPath(sz); if (pidlGroup) { if (SUCCEEDED(ICoCreateInstance(&CLSID_ShellDesktop, &IID_IShellFolder, &psfDesktop))) { hres = psfDesktop->lpVtbl->BindToObject(psfDesktop, pidlGroup, NULL, &IID_IShellFolder, &psf); if (SUCCEEDED(hres)) { hres = AppList_ShellFolderEnum(pidlGroup, psf); psf->lpVtbl->Release(psf); } psfDesktop->lpVtbl->Release(psfDesktop); } else { DebugMsg(DM_ERROR, TEXT("OneTree: failed to bind to Desktop root")); } ILFree(pidlGroup); } else { DebugMsg(DM_ERROR, TEXT("gc.al_acs: Can't create IDList for path..")); } } else { DebugMsg(DM_ERROR, TEXT("gc.al_acs: Can't find programs folder.")); } } BOOL StartMenuIsProgramsParent(void) { LPITEMIDLIST pidlStart, pidlProgs; BOOL fParent = FALSE; if (SHGetSpecialFolderLocation(NULL, CSIDL_STARTMENU, &pidlStart)) { if (SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAMS, &pidlProgs)) { if (ILIsParent(pidlStart, pidlProgs, FALSE)) fParent = TRUE; ILFree(pidlProgs); } ILFree(pidlStart); } return fParent; } //--------------------------------------------------------------------------- // Return the links in a group. void AppList_AddCurrentStuff(void) { DebugMsg(DM_TRACE, TEXT("gc.al_acs: Enumerating everything...")); DebugMsg(DM_TRACE, TEXT("gc.al_acs: Enumerating StartMenu...")); Applist_SpecialFolderEnum(CSIDL_STARTMENU); if (!StartMenuIsProgramsParent()) { DebugMsg(DM_TRACE, TEXT("gc.al_acs: Enumerating Programs...")); Applist_SpecialFolderEnum(CSIDL_PROGRAMS); } } // On NT we plan on converting NT formated group files into folders and links // therefore we need the ability of supporting all of the NT group file formats /*--------------------------------------------------------------------------*/ /* */ /* SIZEOFGroup() - */ /* */ /*--------------------------------------------------------------------------*/ DWORD SizeofGroup(LPNT_GROUPDEF lpgd) { LPNT_PMTAG lptag; DWORD cbSeg; DWORD cb; cbSeg = (DWORD)GlobalSize(lpgd); // The following needs to be verified lptag = (LPNT_PMTAG)((LPSTR)lpgd+lpgd->cbGroup); if ((DWORD)((PCHAR)lptag - (PCHAR)lpgd +MyDwordAlign(SIZEOF(NT_PMTAG))-MyDwordAlign(SIZEOF(lptag->rgb))+4) <= cbSeg && lptag->wID == ID_MAGIC && lptag->wItem == (int)0xFFFF && lptag->cb == (WORD)(MyDwordAlign(SIZEOF(NT_PMTAG))-MyDwordAlign(SIZEOF(lptag->rgb)) + 4) && *(PLONG)lptag->rgb == TAG_MAGIC) { while ((cb = (DWORD)((PCHAR)lptag - (PCHAR)lpgd + MyDwordAlign(SIZEOF(NT_PMTAG))-MyDwordAlign(SIZEOF(lptag->rgb)))) <= cbSeg) { if (lptag->wID == ID_LASTTAG) return cb; (LPSTR)lptag += lptag->cb; } } return lpgd->cbGroup; } /*--------------------------------------------------------------------------*/ /* */ /* FindTag() - */ /* */ /*--------------------------------------------------------------------------*/ LPNT_PMTAG FindTag(LPNT_GROUPDEF lpgd, int item, WORD id) { LPNT_PMTAG lptag; DWORD cbSeg; DWORD cb; cbSeg = (DWORD)GlobalSize(lpgd); lptag = (LPNT_PMTAG)((LPSTR)lpgd+lpgd->cbGroup); if ((PCHAR)lptag - (PCHAR)lpgd + MyDwordAlign(SIZEOF(NT_PMTAG))-MyDwordAlign(SIZEOF(lptag->rgb)) + 4 <= cbSeg && lptag->wID == ID_MAGIC && lptag->wItem == (int)0xFFFF && lptag->cb == (WORD)(MyDwordAlign(SIZEOF(NT_PMTAG))-MyDwordAlign(SIZEOF(lptag->rgb)) +4) && *(LONG *)lptag->rgb == TAG_MAGIC) { while ((cb = (DWORD)((PCHAR)lptag - (PCHAR)lpgd + MyDwordAlign(SIZEOF(NT_PMTAG))-MyDwordAlign(SIZEOF(lptag->rgb)))) <= cbSeg) { if ((item == lptag->wItem) && (id == 0 || id == lptag->wID)) { return lptag; } if (lptag->wID == ID_LASTTAG) return NULL; (LPSTR)lptag += lptag->cb; } } return NULL; } /*--------------------------------------------------------------------------*/ /* */ /* DeleteTag() - */ /* */ /* in: */ /* hGroup group handle, can be discardable (alwayws shrink object) */ /* */ /*--------------------------------------------------------------------------*/ VOID DeleteTag(HANDLE hGroup, int item, WORD id) { LPNT_PMTAG lptag; LPWSTR lp1, lp2; LPWSTR lpend; LPNT_GROUPDEF lpgd; lpgd = (LPNT_GROUPDEF) GlobalLock(hGroup); lptag = FindTag(lpgd,item,id); if (lptag == NULL) { GlobalUnlock(hGroup); return; } lp1 = (LPWSTR)lptag; lp2 = (LPWSTR)((LPSTR)lptag + lptag->cb); lpend = (LPWSTR)((LPSTR)lpgd + SizeofGroup(lpgd)); while (lp2 < lpend) { *lp1++ = *lp2++; } /* always reallocing smaller */ GlobalUnlock(hGroup); GlobalReAlloc(hGroup, (DWORD)((LPSTR)lp1 - (LPSTR)lpgd), 0); return; } /*--------------------------------------------------------------------------*/ /* */ /* AddTag() - */ /* */ /* in: */ /* h group handle, must not be discardable! */ /* */ /* returns: */ /* 0 failure */ /* 1 success */ /*--------------------------------------------------------------------------*/ INT AddTag(HANDLE h, int item, WORD id, LPWSTR lpbuf, UINT cb) { LPNT_PMTAG lptag; WORD fAddFirst; LPNT_GROUPDEF lpgd; int cbNew; int cbMyLen; LPNT_GROUPDEF lpgdOld; if (!cb && lpbuf) { cb = SIZEOF(WCHAR)*(lstrlenW(lpbuf) + 1); } cbMyLen = MyDwordAlign(cb); if (!lpbuf) { cb = 0; cbMyLen = 0; } /* * Remove the old version of the tag, if any. */ DeleteTag(h, item, id); lpgd = (LPNT_GROUPDEF)GlobalLock(h); lptag = FindTag(lpgd, (int)0xFFFF, (WORD)ID_LASTTAG); if (!lptag) { /* * In this case, there are no tags at all, and we have to add * the first tag, the interesting tag, and the last tag */ cbNew = 3 * (MyDwordAlign(SIZEOF(NT_PMTAG)) - MyDwordAlign(SIZEOF(lptag->rgb))) + 4 + cbMyLen; fAddFirst = TRUE; lptag = (LPNT_PMTAG)((LPSTR)lpgd + lpgd->cbGroup); } else { /* * In this case, only the interesting tag needs to be added * but we count in the last because the delta is from lptag */ cbNew = 2 * (MyDwordAlign(SIZEOF(NT_PMTAG)) - MyDwordAlign(SIZEOF(lptag->rgb))) + cbMyLen; fAddFirst = FALSE; } /* * check for 64K limit */ if ((DWORD_PTR)lptag + cbNew < (DWORD_PTR)lptag) { return 0; } cbNew += (DWORD)((PCHAR)lptag -(PCHAR)lpgd); lpgdOld = lpgd; GlobalUnlock(h); if (!GlobalReAlloc(h, (DWORD)cbNew, GMEM_MOVEABLE)) { return 0; } lpgd = (LPNT_GROUPDEF)GlobalLock(h); lptag = (LPNT_PMTAG)((LPSTR)lpgd + ((LPSTR)lptag - (LPSTR)lpgdOld)); if (fAddFirst) { /* * Add the first tag */ lptag->wID = ID_MAGIC; lptag->wItem = (int)0xFFFF; *(LONG *)lptag->rgb = TAG_MAGIC; lptag->cb = (WORD)(MyDwordAlign(SIZEOF(NT_PMTAG)) - MyDwordAlign(SIZEOF(lptag->rgb)) + 4); (LPSTR)lptag += lptag->cb; } /* * Add the tag */ lptag->wID = id; lptag->wItem = item; lptag->cb = (WORD)(MyDwordAlign(SIZEOF(NT_PMTAG)) - MyDwordAlign(SIZEOF(lptag->rgb)) + cbMyLen); if (lpbuf) { memmove(lptag->rgb, lpbuf, (WORD)cb); } (LPSTR)lptag += lptag->cb; /* * Add the end tag */ lptag->wID = ID_LASTTAG; lptag->wItem = (int)0xFFFF; lptag->cb = 0; GlobalUnlock(h); return 1; } /*--------------------------------------------------------------------------*/ /* */ /* CreateNewGroupFromAnsiGroup() - */ /* */ /* This function creates a new, empty group. */ /* */ /*--------------------------------------------------------------------------*/ HANDLE CreateNewGroupFromAnsiGroup(LPNT_GROUPDEF_A lpGroupORI) { HANDLE hT; LPNT_GROUPDEF lpgd; int i; int cb; int cItems; // number of items in 16bit group LPSTR pGroupName; // 32bit group name LPWSTR pGroupNameUNI = NULL; // 32bit UNICODE group name UINT wGroupNameLen; // length of pGroupName DWORD aligned. INT cchWideChar = 0; //character count of resultant unicode string INT cchMultiByte = 0; pGroupName = (LPSTR)PTR(lpGroupORI, lpGroupORI->pName); // // convert pGroupName to unicode here // cchMultiByte=MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,pGroupName, -1,pGroupNameUNI,cchWideChar) ; pGroupNameUNI = LocalAlloc(LPTR,(++cchMultiByte)*SIZEOF(WCHAR)) ; if (NULL == pGroupNameUNI) goto Exit; MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,pGroupName, -1,pGroupNameUNI,cchMultiByte) ; wGroupNameLen = MyDwordAlign(SIZEOF(WCHAR)*(lstrlenW(pGroupNameUNI) + 1)); cItems = lpGroupORI->cItems; cb = SIZEOF(NT_GROUPDEF) + (cItems * SIZEOF(DWORD)) + wGroupNameLen; // // In CreateNewGroup before GlobalAlloc. // hT = GlobalAlloc(GHND, (DWORD)cb); if (!hT) { LocalFree((HLOCAL)pGroupNameUNI); goto Exit; } lpgd = (LPNT_GROUPDEF)GlobalLock(hT); // // use the NT 1.0 group settings for what we can. // lpgd->nCmdShow = lpGroupORI->nCmdShow; lpgd->wIconFormat = lpGroupORI->wIconFormat; lpgd->cxIcon = lpGroupORI->cxIcon; lpgd->cyIcon = lpGroupORI->cyIcon; lpgd->ptMin.x = (INT)lpGroupORI->ptMin.x; lpgd->ptMin.y = (INT)lpGroupORI->ptMin.y; CopyRect(&(lpgd->rcNormal),&(lpGroupORI->rcNormal)); lpgd->dwMagic = GROUP_UNICODE; lpgd->cbGroup = (DWORD)cb; lpgd->pName = SIZEOF(NT_GROUPDEF) + cItems * SIZEOF(DWORD); lpgd->Reserved1 = (WORD)-1; lpgd->Reserved2 = (DWORD)-1; lpgd->cItems = (WORD)cItems; for (i = 0; i < cItems; i++) { lpgd->rgiItems[i] = 0; } lstrcpyW((LPWSTR)((LPBYTE)lpgd + SIZEOF(NT_GROUPDEF) + cItems * SIZEOF(DWORD)), pGroupNameUNI); // lhb tracks LocalFree((HLOCAL)pGroupNameUNI); GlobalUnlock(hT); return(hT); Exit: return NULL; } /*--------------------------------------------------------------------------*/ /* */ /* AddThing() - */ /* */ /* in: */ /* hGroup group handle, must not be discardable */ /* lpStuff pointer to data or NULL to init data to zero */ /* cbStuff count of item (may be 0) if lpStuff is a string */ /* */ /* Adds an object to the group segment and returns its offset. Will */ /* reallocate the segment if necessary. */ /* */ /* Handle passed in must not be discardable */ /* */ /* returns: */ /* 0 failure */ /* > 0 offset to thing in the segment */ /* */ /*--------------------------------------------------------------------------*/ DWORD AddThing(HANDLE hGroup, LPWSTR lpStuff, DWORD cbStuff) { DWORD cb; LPNT_GROUPDEF lpgd; DWORD offset; LPWSTR lpT; DWORD cbStuffSize; DWORD cbGroupSize; DWORD myOffset; if (cbStuff == 0xFFFFFFFF) { return 0xFFFFFFFF; } if (!cbStuff) { cbStuff = SIZEOF(WCHAR)*(DWORD)(1 + lstrlenW(lpStuff)); } cbStuffSize = MyDwordAlign((int)cbStuff); lpgd = (LPNT_GROUPDEF)GlobalLock(hGroup); cb = SizeofGroup(lpgd); cbGroupSize = MyDwordAlign((int)cb); offset = lpgd->cbGroup; myOffset = (DWORD)MyDwordAlign((int)offset); GlobalUnlock(hGroup); if (!GlobalReAlloc(hGroup,(DWORD)(cbGroupSize + cbStuffSize), GMEM_MOVEABLE)) return 0; lpgd = (LPNT_GROUPDEF)GlobalLock(hGroup); /* * Slide the tags up */ memmove((LPSTR)lpgd + myOffset + cbStuffSize, (LPSTR)lpgd + myOffset, (cbGroupSize - myOffset)); lpgd->cbGroup += cbStuffSize; lpT = (LPWSTR)((LPSTR)lpgd + myOffset); if (lpStuff) { memcpy(lpT, lpStuff, cbStuff); } else { /* * Zero it */ while (cbStuffSize--) { *((LPBYTE)lpT)++ = 0; } } GlobalUnlock(hGroup); return myOffset; } DWORD AddThing_A(HANDLE hGroup, LPSTR lpStuff, WORD cbStuff) { LPWSTR lpStuffUNI = NULL; BOOL bAlloc = FALSE; DWORD cb; if (cbStuff == 0xFFFF) { return 0xFFFF; } if (!cbStuff) { INT cchMultiByte; INT cchWideChar = 0; bAlloc = TRUE; cchMultiByte=MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,lpStuff, -1,lpStuffUNI,cchWideChar) ; lpStuffUNI = LocalAlloc(LPTR,(++cchMultiByte)*SIZEOF(WCHAR)) ; if (lpStuffUNI) { MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,lpStuff, -1,lpStuffUNI,cchMultiByte) ; cbStuff = (WORD)SIZEOF(WCHAR)*(1 + lstrlenW(lpStuffUNI)); // lhb tracks } } else { lpStuffUNI = (LPWSTR)lpStuff; } if (lpStuffUNI) { cb = AddThing(hGroup, lpStuffUNI, cbStuff); if (bAlloc) LocalFree(lpStuffUNI); } else { cb = 0; } return(cb); } /*--------------------------------------------------------------------------*/ /* */ /* ConvertToUnicodeGroup() - */ /* */ /* returns the size of the new unicode group. */ /* */ /*--------------------------------------------------------------------------*/ int ConvertToUnicodeGroup(LPNT_GROUPDEF_A lpGroupORI, LPHANDLE lphNewGroup) { HANDLE hNewGroup; LPNT_GROUPDEF lpgd; LPNT_ITEMDEF lpid; LPBYTE lpid_A; LPNT_PMTAG lptag_A; LPSTR lpTagValue; WORD wTagId; LPSTR lpT; DWORD offset; int cb; int i; INT cchMultiByte; INT cchWideChar; LPWSTR lpTagValueUNI; BOOL bAlloc = FALSE; hNewGroup = CreateNewGroupFromAnsiGroup(lpGroupORI); if (!hNewGroup) { return(0); } // // Add all items to the new formatted group. // for (i = 0; i < (int)lpGroupORI->cItems; i++) { // // Get the pointer to the 16bit item // lpid_A = (LPBYTE)ITEM(lpGroupORI, i); if (lpGroupORI->rgiItems[i]) { // // Create the item. // offset = AddThing(hNewGroup, NULL, SIZEOF(NT_ITEMDEF)); if (!offset) { DebugMsg(DM_ERROR, TEXT("gc.ctug: AddThing NT_ITEMDEF failed")); goto QuitThis; } lpgd = (LPNT_GROUPDEF)GlobalLock(hNewGroup); lpgd->rgiItems[i] = offset; lpid = ITEM(lpgd, i); // // Set the item's position. // lpid->pt.x = ((LPNT_ITEMDEF_A)lpid_A)->pt.x; lpid->pt.y = ((LPNT_ITEMDEF_A)lpid_A)->pt.y; // // Add the item's Name. // GlobalUnlock(hNewGroup); lpT = (LPSTR)PTR(lpGroupORI,((LPNT_ITEMDEF_A)lpid_A)->pName); offset = AddThing_A(hNewGroup, lpT, 0); if (!offset) { DebugMsg(DM_ERROR, TEXT("gc.ctug: AddThing pName failed")); goto PuntCreation; } lpgd = (LPNT_GROUPDEF)GlobalLock(hNewGroup); lpid = ITEM(lpgd, i); lpid->pName = offset; // // Add the item's Command line. // GlobalUnlock(hNewGroup); lpT = (LPSTR)PTR(lpGroupORI, ((LPNT_ITEMDEF_A)lpid_A)->pCommand); offset = AddThing_A(hNewGroup, lpT, 0); if (!offset) { DebugMsg(DM_ERROR, TEXT("gc.ctug: AddThing pCommand failed")); goto PuntCreation; } lpgd = (LPNT_GROUPDEF)GlobalLock(hNewGroup); lpid = ITEM(lpgd, i); lpid->pCommand = offset; // // Add the item's Icon path. // GlobalUnlock(hNewGroup); lpT = (LPSTR)PTR(lpGroupORI, ((LPNT_ITEMDEF_A)lpid_A)->pIconPath); offset = AddThing_A(hNewGroup, lpT, 0); if (!offset) { DebugMsg(DM_ERROR, TEXT("gc.ctug: AddThing pIconPath failed")); goto PuntCreation; } lpgd = (LPNT_GROUPDEF)GlobalLock(hNewGroup); lpid = ITEM(lpgd, i); lpid->pIconPath = offset; // // Get the item's icon resource using the Icon path and the icon index. // And add the item's Icon resource. // lpid->iIcon = ((LPNT_ITEMDEF_A)lpid_A)->idIcon; lpid->cbIconRes = ((LPNT_ITEMDEF_A)lpid_A)->cbIconRes; lpid->wIconVer = ((LPNT_ITEMDEF_A)lpid_A)->wIconVer; GlobalUnlock(hNewGroup); lpT = (LPBYTE)PTR(lpGroupORI, ((LPNT_ITEMDEF_A)lpid_A)->pIconRes); offset = AddThing_A(hNewGroup, (LPSTR)lpT, lpid->cbIconRes); if (!offset) { DebugMsg(DM_ERROR, TEXT("gc.ctug: AddThing pIconRes failed")); goto PuntCreation; } lpgd = (LPNT_GROUPDEF)GlobalLock(hNewGroup); lpid = ITEM(lpgd, i); lpid->pIconRes = offset; GlobalUnlock(hNewGroup); } } /* * Copy all the tags to the new group format. */ lptag_A = (LPNT_PMTAG)((LPSTR)lpGroupORI + lpGroupORI->cbGroup); // lhb tracks if (lptag_A->wID == ID_MAGIC && lptag_A->wItem == (int)0xFFFF && *(LONG *)lptag_A->rgb == TAG_MAGIC) { // // This is the first tag id, goto start of item tags. // (LPBYTE)lptag_A += lptag_A->cb; while (lptag_A->wID != ID_LASTTAG) { wTagId = lptag_A->wID; cb = lptag_A->cb - (3 * SIZEOF(DWORD)); // cb - sizeof tag if (wTagId == ID_MINIMIZE) { lpTagValueUNI = NULL; } else { lpTagValue = lptag_A->rgb ; if (wTagId != ID_HOTKEY) { bAlloc = TRUE; cchWideChar = 0; cchMultiByte=MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,lpTagValue, -1,NULL,cchWideChar) ; lpTagValueUNI = LocalAlloc(LPTR,(++cchMultiByte)*SIZEOF(WCHAR)) ; MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,lpTagValue, -1,lpTagValueUNI,cchMultiByte) ; cb = SIZEOF(WCHAR)*(lstrlenW(lpTagValueUNI) + 1); // lhb tracks } else { lpTagValueUNI = (LPWSTR)lpTagValue; } } if (! AddTag( hNewGroup, lptag_A->wItem, // wItem wTagId, // wID lpTagValueUNI, // rgb : tag value cb )) { DebugMsg(DM_ERROR, TEXT("gc.ctug: AddTag failed")); } if (bAlloc && lpTagValueUNI) { LocalFree(lpTagValueUNI); bAlloc = FALSE; } (LPBYTE)lptag_A += lptag_A->cb ; // go to next tag } } lpgd = GlobalLock(hNewGroup); cb = SizeofGroup(lpgd); GlobalUnlock(hNewGroup); *lphNewGroup = hNewGroup; return(cb); PuntCreation: QuitThis: if (hNewGroup) { GlobalFree(hNewGroup); } return(0); }