Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3923 lines
120 KiB

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
#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);
}
#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; 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");
//----------------------------------------------------------------------------
// 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);
}