|
|
//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
#include "grpconv.h"
#include "util.h"
#include "rcids.h"
#include "group.h"
#include "gcinst.h"
#include <port32.h>
#include <regstr.h>
#define INITGUID
#include <initguid.h>
#pragma data_seg(DATASEG_READONLY)
#include <coguid.h>
#include <oleguid.h>
#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); }
#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; i<cItems; i++) { ppmitem = DSA_GetItemPtr(hdsaPMItems, i); // Is the item broken?
if (!ppmitem->lpszDesc || !(*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; i<cItems; i++) { ppmitem = DSA_GetItemPtr(hdsaPMItems, i); // Shorten the descriptions
lstrcpyn(ppmitem->lpszDesc, 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; i<cItems; i++) { szCL[0] = TEXT('\0'); szWD[0] = TEXT('\0'); lppmitem = DSA_GetItemPtr(hdsaPMItems, i);
// Get the current command line.
Str_GetPtr(lppmitem->lpszCL, 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<cItems; i++) { Group_SetProgress(i);
szDesc[0] = TEXT('\0'); szCL[0] = TEXT('\0'); szIconPath[0] = TEXT('\0'); itemdef.iIcon = 0;
if (rgItems[i] == 0) { DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file has empty item definition - skipping.")); goto AddItem; } if (rgItems[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<cItems; i++) { Group_SetProgress(i);
szDesc[0] = TEXT('\0'); szCL[0] = TEXT('\0'); szIconPath[0] = TEXT('\0'); itemdef.iIcon = 0;
if (rgItems[i] == 0) { DebugMsg(DM_TRACE, TEXT("gc.gcnfo: Old group file has empty item definition - skipping.")); goto AddItem; } if (rgItems[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; i<cItems; i++) { // N is our guy, skip it.
if ((UINT)i == n) continue;
pitem = DSA_GetItemPtr(hdsaPMItems, i); Assert(pitem); if (pitem->lpszDesc && *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");
//----------------------------------------------------------------------------
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; }
//----------------------------------------------------------------------------
BOOL Ver_GetStringFileInfo(PVOID pBuf, LPCTSTR pszLangCharSet, LPCTSTR pszStringName, LPTSTR pszValue, int cchValue) { 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, cchValue); return TRUE; } return FALSE; }
//---------------------------------------------------------------------------
void GetVersionInfo(LPTSTR pszPath, LPTSTR pszModule, int cchModule, LPTSTR pszVer, int cchVer) { 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, cchModule); Ver_GetStringFileInfo(pBuf, szCharSet, c_szProductVersion, pszVer, cchVer); } else { // Try the same language as us.
LoadString(g_hinst, IDS_DEFLANGCHARSET, szCharSet, ARRAYSIZE(szCharSet)); Ver_GetStringFileInfo(pBuf, szCharSet, c_szInternalName, pszModule, cchModule); Ver_GetStringFileInfo(pBuf, szCharSet, c_szProductVersion, pszVer, cchVer); }
// Last chance - try English.
if (!*pszModule) Ver_GetStringFileInfo(pBuf, c_szEngLangCharSet, c_szInternalName, pszModule, cchModule); if (!*pszVer) Ver_GetStringFileInfo(pBuf, c_szEngLangCharSet, c_szProductVersion, pszVer, cchVer); } 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 512
// returns S_OK if allocated a sufficiently large buffer (buffer allocated with LocalAlloc returned)
// returns E_FAIL if buffer required is larger than BIG_STEP*7 characters (no buffer returned)
// returns E_OUTOFMEMORY if memory allocation failed (no buffer returned)
HRESULT GetSufficientlyLargeGroupBuffer(LPTSTR pszIni, LPTSTR* ppszBuffer) { UINT cchBuffer; HRESULT hr = S_FALSE; for (cchBuffer = BIG_STEP; (S_FALSE == hr) && (cchBuffer <= BIG_STEP * 7); cchBuffer += BIG_STEP) { *ppszBuffer = (LPTSTR)LocalAlloc(LPTR, cchBuffer * sizeof(TCHAR)); if (!*ppszBuffer) { hr = E_OUTOFMEMORY; } else { if ((UINT)GetPrivateProfileString(c_szGroups, NULL, c_szNULL, *ppszBuffer, cchBuffer, pszIni) < (cchBuffer - 5)) { hr = S_OK; // found a big enough buffer, we can stop
} else { LocalFree((HLOCAL)*ppszBuffer); *ppszBuffer = NULL; hr = S_FALSE; // continue in the loop
} } }
if (S_FALSE == hr) { hr = E_FAIL; // make callers using SUCCEEDED happy
} return hr; }
//----------------------------------------------------------------------------
// 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;
if (FAILED(GetSufficientlyLargeGroupBuffer(szIniFile, &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;
if (FAILED(GetSufficientlyLargeGroupBuffer(szIniFile, &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, ARRAYSIZE(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); }
|