You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3866 lines
122 KiB
3866 lines
122 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);
|
|
}
|
|
|
|
#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);
|
|
}
|