|
|
//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
#include "grpconv.h"
#include "gcinst.h"
#include "util.h"
#include <shellp.h>
#include <trayp.h>
#include <regstr.h>
#include <shguidp.h>
#include <windowsx.h>
#include "rcids.h"
#include "group.h"
#ifdef WINNT
// NT is unicode so use a larger buffer since sizeof(TCHAR) is > than on win95
#define BUFSIZES 40960
#else
// on win95 GetPrivateProfileSection has a 32767 char limit, so
// we make this a bit smaller
#define BUFSIZES 20480
#endif // WINNT
// Define checkbox states for listview
#define LVIS_GCNOCHECK 0x1000
#define LVIS_GCCHECK 0x2000
#define HSZNULL 0
#define HCONVNULL 0
#define HCONVLISTNULL 0
#define DDETIMEOUT 20*1000
extern UINT GC_TRACE; extern const TCHAR c_szMapGroups[]; BOOL g_fDoProgmanDde = FALSE; BOOL g_fInitDDE = FALSE;
#define CH_COLON TEXT(':')
//---------------------------------------------------------------------------
// Global to this file only...
static const TCHAR c_szGrpConvInf[] = TEXT("setup.ini"); static const TCHAR c_szGrpConvInfOld[] = TEXT("setup.old"); static const TCHAR c_szExitProgman[] = TEXT("[ExitProgman(1)]"); static const TCHAR c_szAppProgman[] = TEXT("AppProgman"); static const TCHAR c_szEnableDDE[] = TEXT("EnableDDE"); static const TCHAR c_szProgmanOnly[] = TEXT("progman.only"); static const TCHAR c_szProgmanGroups[] = TEXT("progman.groups"); static const TCHAR c_szDesktopGroups[] = TEXT("desktop.groups"); static const TCHAR c_szStartupGroups[] = TEXT("startup.groups"); static const TCHAR c_szSendToGroups[] = TEXT("sendto.groups"); static const TCHAR c_szRecentDocsGroups[] = TEXT("recentdocs.groups");
//---------------------------------------------------------------------------
const TCHAR c_szProgmanIni[] = TEXT("progman.ini"); const TCHAR c_szStartup[] = TEXT("Startup"); const TCHAR c_szProgmanExe[] = TEXT("progman.exe"); const TCHAR c_szProgman[] = TEXT("Progman");
// NB This must match the one in cabinet.
static const TCHAR c_szRUCabinet[] = TEXT("[ConfirmCabinetID]");
typedef struct { DWORD dwInst; HCONVLIST hcl; HCONV hconv; BOOL fStartedProgman; } PMDDE, *PPMDDE;
//
// This function grovles HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\ShellFolders
// and creates a DPA with strings of all the speial folders
//
BOOL CreateSpecialFolderDPA(HDPA* phdpaSF) { HKEY hkSP; TCHAR szValueName[MAX_PATH]; DWORD cbValueName; DWORD cbData; DWORD dwIndex = 0; LONG lRet = ERROR_SUCCESS;
// we should only ever be called once to populate the dpa
if (*phdpaSF != NULL) return FALSE;
if (RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"), 0, KEY_QUERY_VALUE, &hkSP) != ERROR_SUCCESS) { // couldnt open the key, so bail
return FALSE; }
*phdpaSF = DPA_Create(4);
do { cbValueName = ARRAYSIZE(szValueName);
lRet = RegEnumValue(hkSP, dwIndex, szValueName, &cbValueName, NULL, NULL, NULL, &cbData); if (lRet == ERROR_SUCCESS) { LPTSTR pszValueData = LocalAlloc(LPTR, cbData);
if (!pszValueData) break; if (RegQueryValueEx(hkSP, szValueName, NULL, NULL, (LPBYTE)pszValueData, &cbData) == ERROR_SUCCESS) { DPA_AppendPtr(*phdpaSF, pszValueData); } }
dwIndex++;
} while (lRet != ERROR_NO_MORE_ITEMS);
return TRUE; }
//
// SafeRemoveDirectory checks to make sure that we arent removing a "special"
// folder. On win95 when we remove the last shortcut from the %windir%\desktop folder,
// we go and remove that as well. This causes the shell to hang among other bad things.
//
BOOL SafeRemoveDirectory(LPCTSTR pszDir) { static HDPA hdpaSF = NULL; int iMax; int iIndex;
if (!hdpaSF && !CreateSpecialFolderDPA(&hdpaSF)) { // if we cant read the special folders, error on the
// side of caution
return FALSE; }
iMax = DPA_GetPtrCount(hdpaSF);
for (iIndex = 0; iIndex < iMax; iIndex++) { LPTSTR pszSpecialFolder = DPA_GetPtr(hdpaSF, iIndex);
if (!pszSpecialFolder) continue;
if (lstrcmpi(pszDir, pszSpecialFolder) == 0) return FALSE; } // no special folders matched, so its ok to delete it
return Win32RemoveDirectory(pszDir); }
//---------------------------------------------------------------------------
void Progman_ReplaceItem(PPMDDE ppmdde, LPCTSTR szName, LPCTSTR pszCL, LPCTSTR szArgs, LPCTSTR szIP, int iIcon, LPCTSTR szWD) { TCHAR szBuf[512];
if (g_fDoProgmanDde) { wsprintf(szBuf, TEXT("[ReplaceItem(\"%s\")]"), szName); DdeClientTransaction((LPBYTE)szBuf, (lstrlen(szBuf)+1)*SIZEOF(TCHAR), ppmdde->hconv, HSZNULL, 0, XTYP_EXECUTE, DDETIMEOUT, NULL);
wsprintf(szBuf, TEXT("[AddItem(\"%s %s\",\"%s\",%s,%d,-1,-1,%s)]"), pszCL, szArgs, szName, szIP, iIcon, szWD); DdeClientTransaction((LPBYTE)szBuf, (lstrlen(szBuf)+1)*SIZEOF(TCHAR), ppmdde->hconv, HSZNULL, 0, XTYP_EXECUTE, DDETIMEOUT, NULL); } }
//---------------------------------------------------------------------------
void Progman_DeleteItem(PPMDDE ppmdde, LPCTSTR szName) { // NB Progman only support 256 char commands.
TCHAR szBuf[256];
if (g_fDoProgmanDde) { wsprintf(szBuf, TEXT("[DeleteItem(%s)]"), szName); DdeClientTransaction((LPBYTE)szBuf, (lstrlen(szBuf)+1)*SIZEOF(TCHAR), ppmdde->hconv, HSZNULL, 0, XTYP_EXECUTE, DDETIMEOUT, NULL); } }
//---------------------------------------------------------------------------
void Reg_SetMapGroupEntry(LPCTSTR pszOld, LPCTSTR pszNew) { Reg_SetString(g_hkeyGrpConv, c_szMapGroups, pszOld, pszNew); DebugMsg(DM_TRACE, TEXT("gc.r_cmge: From %s to %s"), pszOld, pszNew); }
//---------------------------------------------------------------------------
void GetProperGroupName(LPCTSTR pszGroupPath, LPTSTR pszGroup, int cchGroup) { LPTSTR pszGroupName; // Progman only supports a single level hierachy so...
pszGroupName = PathFindFileName(pszGroupPath);
// NB If we do have a group within a group then we should add a
// MapGroup entry to the registry so running GrpConv in the
// future won't cause groups to get duplicated.
if (lstrcmpi(pszGroupName, pszGroupPath) != 0) { Reg_SetMapGroupEntry(pszGroupName, pszGroupPath); } // A missing group name implies use a default.
if (!pszGroupName || !*pszGroupName) { LoadString(g_hinst, IDS_PROGRAMS, pszGroup, cchGroup); } else { lstrcpyn(pszGroup, pszGroupName, cchGroup); } }
//---------------------------------------------------------------------------
BOOL Progman_CreateGroup(PPMDDE ppmdde, LPCTSTR pszGroupPath) { // NB Progman only support 256 char commands.
TCHAR szBuf[256]; TCHAR szGroup[MAX_PATH]; HDDEDATA hdata;
GetProperGroupName(pszGroupPath, szGroup, ARRAYSIZE(szGroup)); if (g_fDoProgmanDde) { wsprintf(szBuf, TEXT("[CreateGroup(%s)]"), szGroup); hdata = DdeClientTransaction((LPBYTE)szBuf, (lstrlen(szBuf)+1)*SIZEOF(TCHAR), ppmdde->hconv, HSZNULL, 0, XTYP_EXECUTE, DDETIMEOUT, NULL); Assert(hdata); } else return FALSE; return hdata ? TRUE : FALSE; }
//---------------------------------------------------------------------------
BOOL Progman_ShowGroup(PPMDDE ppmdde, LPCTSTR pszGroupPath) { // NB Progman only support 256 char commands.
TCHAR szBuf[256]; TCHAR szGroup[MAX_PATH]; HDDEDATA hdata; GetProperGroupName(pszGroupPath, szGroup, ARRAYSIZE(szGroup));
if (g_fDoProgmanDde) { wsprintf(szBuf, TEXT("[ShowGroup(%s, %d)]"), szGroup, SW_SHOWNORMAL); hdata = DdeClientTransaction((LPBYTE)szBuf, (lstrlen(szBuf)+1)*SIZEOF(TCHAR), ppmdde->hconv, HSZNULL, 0, XTYP_EXECUTE, DDETIMEOUT, NULL); Assert(hdata); } else return FALSE; return hdata ? TRUE : FALSE; }
// Given a string that potentially could be "::{GUID}:DATA::....::{GUID}:DATA::Path",
// return the pointer to the path. This starts after the last double-colon sequence.
// (Darwin and Logo3 uses this format.)
LPTSTR FindPathSection(LPCTSTR pszPath) { LPCTSTR psz = pszPath; LPCTSTR pszFirstColon = NULL; LPCTSTR pszDblColon = NULL;
// Find the last double-colon sequence
while (*psz) { if (*psz == CH_COLON) { // Was the previous character a colon too?
if (pszFirstColon) { // Yes; remember that position
pszDblColon = pszFirstColon; pszFirstColon = NULL; } else { // No; remember this as a potential for being the first colon
// in a double-colon sequence
pszFirstColon = psz; } } else pszFirstColon = NULL;
psz = CharNext(psz); }
if (pszDblColon) return (LPTSTR)pszDblColon+2; // skip the double-colon
return (LPTSTR)pszPath; }
#define BG_DELETE_EMPTY 0x0001
#define BG_PROG_GRP_CREATED 0x0002
#define BG_PROG_GRP_SHOWN 0x0004
#define BG_SEND_TO_GRP 0x0008
#define BG_LFN 0x0010
#define BG_RECENT_DOCS 0x0020
#define BG_SET_PROGRESS_TEXT 0x0040
#define BG_FORCE_DESKTOP 0x0080
#define BG_FORCE_STARTUP 0x0100
#define BG_FORCE_RECENT 0x0200
#define BG_FORCE_SENDTO 0x0400
//---------------------------------------------------------------------------
void BuildGroup(LPCTSTR lpszIniFileName, LPCTSTR lpszSection, LPCTSTR lpszGroupName, PPMDDE ppmdde, BOOL fUpdFolder, DWORD dwFlags) { // Data associated with readining in section.
HGLOBAL hg; LPTSTR lpBuf; // Pointer to buffer to read section into
int cb; LPTSTR pszLine; IShellLink *psl; TCHAR szName[MAX_PATH]; TCHAR szCL[3*MAX_PATH]; // we make this 3*MAX_PATH so that DARWIN and LOGO3 callers can pass the extra information
TCHAR szIP[2*MAX_PATH]; TCHAR szArgs[2*MAX_PATH]; TCHAR szGroupFolder[MAX_PATH]; TCHAR szSpecialGrp[32]; WCHAR wszPath[2*MAX_PATH]; TCHAR szWD[2*MAX_PATH]; TCHAR szDesc[3*MAX_PATH]; TCHAR szNum[8]; // Should never exceed this!
LPTSTR lpszArgs; TCHAR szCLPathPart[3*MAX_PATH]; // this 3*MAX_PATH because we use them to twiddle with szCL
TCHAR szCLSpecialPart[3*MAX_PATH]; // this 3*MAX_PATH because we use them to twiddle with szCL
int iLen; int iIcon; LPTSTR pszExt; // DWORD dwFlags = BG_DELETE_EMPTY;
// BOOL fDeleteEmpty = TRUE;
// BOOL fProgGrpCreated = FALSE;
// BOOL fProgGrpShown = FALSE;
// BOOL fSendToGrp = FALSE;
// BOOL fLFN;
Log(TEXT("Setup.Ini: %s"), lpszGroupName); DebugMsg(GC_TRACE, TEXT("gc.bg: Rebuilding %s"), (LPTSTR) lpszGroupName);
// Special case [SendTo] section name - this stuff doesn't
// need to be added to progman.
LoadString(g_hinst, IDS_SENDTO, szSpecialGrp, ARRAYSIZE(szSpecialGrp)); if ((dwFlags & BG_FORCE_SENDTO) || (lstrcmpi(lpszSection, szSpecialGrp) == 0)) { DebugMsg(GC_TRACE, TEXT("gc.bg: SendTo section - no Progman group")); // fSendToGrp = TRUE;
dwFlags |= BG_SEND_TO_GRP; }
// Now lets read in the section for the group from the ini file
// First allocate a buffer to read the section into
hg = GlobalAlloc(GPTR, BUFSIZES); // Should never exceed 64K?
if (hg) { lpBuf = GlobalLock(hg);
// Special case the startup group.
LoadString(g_hinst, IDS_STARTUP, szSpecialGrp, ARRAYSIZE(szSpecialGrp)); // Is this the startup group?
szGroupFolder[0] = TEXT('\0'); if ((dwFlags & BG_FORCE_STARTUP) || (lstrcmpi(szSpecialGrp, lpszGroupName) == 0)) { DebugMsg(DM_TRACE, TEXT("gc.bg: Startup group...")); // Yep, Try to get the new location.
Reg_GetString(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER_SHELLFOLDERS, c_szStartup, szGroupFolder, SIZEOF(szGroupFolder)); // fDeleteEmpty = FALSE;
dwFlags &= ~BG_DELETE_EMPTY; } // Is this the desktop folder?
LoadString(g_hinst, IDS_DESKTOP, szSpecialGrp, ARRAYSIZE(szSpecialGrp)); if ((dwFlags & BG_FORCE_RECENT) || (lstrcmp(szSpecialGrp, PathFindFileName(lpszGroupName)) == 0)) { DebugMsg(DM_TRACE, TEXT("gc.bg: Desktop group...")); // fDeleteEmpty = FALSE;
dwFlags &= ~BG_DELETE_EMPTY; }
// Special case the recent folder.
LoadString(g_hinst, IDS_RECENT, szSpecialGrp, ARRAYSIZE(szSpecialGrp)); if (lstrcmp(szSpecialGrp, lpszGroupName) == 0) { DebugMsg(DM_TRACE, TEXT("gc.bg: Recent group...")); dwFlags |= BG_RECENT_DOCS; dwFlags &= ~BG_DELETE_EMPTY; } if (SUCCEEDED(ICoCreateInstance(&CLSID_ShellLink, &IID_IShellLink, &psl))) { IPersistFile *ppf; psl->lpVtbl->QueryInterface(psl, &IID_IPersistFile, &ppf);
// now Read in the secint into our buffer
cb = GetPrivateProfileSection(lpszSection, lpBuf, BUFSIZES/SIZEOF(TCHAR), lpszIniFileName);
if (cb > 0) { pszLine = lpBuf;
// Create the folder...
// Use a generic name until we get items to add so we
// don't stick group names like "AT&T" in users faces
// when all we're trying to do is delete items from them.
Group_SetProgressNameAndRange((LPCTSTR)-1, cb);
// Did we fill in the szGroupFolder yet?
if (!*szGroupFolder) { // some people pass us a fully qualified path for lpszGroupName (eg c:\foo\bar or \\pyrex\user\foo)
// if that is the case, then use the path they specify
if ((PathGetDriveNumber((LPTSTR)lpszGroupName) != -1) || PathIsUNC((LPTSTR)lpszGroupName)) { lstrcpy(szGroupFolder, lpszGroupName); iLen = 2; // let PathRemoveIllegalChars validate the whole string after "c:" or "\\"
} else { // non-fully qualified groupname, so just construct it under startmenu\programs
SHGetSpecialFolderPath(NULL, szGroupFolder, CSIDL_PROGRAMS, TRUE);
iLen = lstrlen(szGroupFolder); PathAppend(szGroupFolder, lpszGroupName); }
PathRemoveIllegalChars(szGroupFolder, iLen, PRICF_ALLOWSLASH); // This should take care of mapping it if machine does not support LFNs.
PathQualify(szGroupFolder); } else { DebugMsg(DM_TRACE, TEXT("gc.bg: Startup group mapped to %s."), szGroupFolder); }
if (fUpdFolder && !(dwFlags & BG_RECENT_DOCS)) { if (!PathFileExists(szGroupFolder)) { if (SHCreateDirectory(NULL, szGroupFolder) != 0) { DebugMsg(DM_ERROR, TEXT("gc.bg: Can't create %s folder."), (LPTSTR) szGroupFolder); } } }
// Keep track if we can create LFN link names on this drive.
// fLFN = IsLFNDrive(szGroupFolder);
if (IsLFNDrive((LPCTSTR)szGroupFolder)) dwFlags |= BG_LFN; #ifdef DEBUG
if (!(dwFlags & BG_LFN)) DebugMsg(DM_TRACE, TEXT("gc.bg: Using short names for this group."), szName); #endif
// Add the items...
//
// Warning: it appears like the data in the setup.ini file does not
// match the standard x=y, but is simpy x or x,y,z so we must
// 1 bias the indexes to ParseField
while (*pszLine) { // Set progress on how many bytes we have processed.
Group_SetProgress((int)(pszLine-lpBuf)); DebugMsg(GC_TRACE, TEXT("gc.bg: Create Link:%s"), (LPTSTR)pszLine);
// Add item.
// Get the short name if we're on a SFN drive.
szName[0] = TEXT('\0'); if (!(dwFlags & BG_LFN)) ParseField(pszLine, 7, szName, ARRAYSIZE(szName)); // Get the long name if we're not on an SFN drive
// or if there is no short name.
if (!*szName) ParseField(pszLine, 1, szName, ARRAYSIZE(szName));
DebugMsg(GC_TRACE, TEXT(" Link:%s"), (LPTSTR)szName);
// Dutch/French sometimes have illegal chars in their ini files.
// NB Progman needs the unmangled names so only remove illegal chars
// from the Explorer string, not szName.
// NB Names can contain slashes so PathFindFileName() isn't very
// useful here.
iLen = lstrlen(szGroupFolder); PathAppend(szGroupFolder, szName); PathRemoveIllegalChars(szGroupFolder, iLen+1, PRICF_NORMAL);
// Handle LFNs on a SFN volume.
PathQualify(szGroupFolder);
if (ParseField(pszLine, 2, szCL, ARRAYSIZE(szCL)) && (*szCL != 0)) { // assume that this is not a DARWIN or LOGO3 special link, and thus
// the path is just what we just read (szCL)
lstrcpy(szCLPathPart, szCL); lstrcpy(szCLSpecialPart, szCL);
// We're going to have to add something to the group,
// switch to using it's real name.
if (!(dwFlags & BG_SET_PROGRESS_TEXT)) { dwFlags |= BG_SET_PROGRESS_TEXT; Group_SetProgressNameAndRange(lpszGroupName, cb); }
// see if we have ":: or just :: which indicates a special link.
// special links have a path that is of the form:
//
// ::{GUID1}:data1::{GUID2}:data2::fullpathtolinktarget
//
// where there could be any number of guid+data sections and the full
// path to the link target at the end is optional.
//
// We seperate this out into the "special" part which contains the guids
// and the "path" part which has the fullpathtolinktarget at the end.
if (szCLSpecialPart[0]==TEXT('"') && szCLSpecialPart[1]==TEXT(':') && szCLSpecialPart[2]==TEXT(':')) { // the string was quoted and it is a special string
LPTSTR pszRealPathBegins; int cch = lstrlen(szCLSpecialPart)+1;
// get rid of the leading "
hmemcpy(szCLSpecialPart, szCLSpecialPart+1, cch * SIZEOF(TCHAR)); // find where the real path begins
pszRealPathBegins = FindPathSection(szCLSpecialPart);
if (*pszRealPathBegins) { // a path part exists, so add a leading ", and copy
// the real fullpathtolinktarget there.
lstrcpy(szCLPathPart, TEXT("\"")); lstrcat(szCLPathPart, pszRealPathBegins);
// terminate the special part after the last ::
*pszRealPathBegins = TEXT('\0'); } else { // no there is no real path, just special info
*szCLPathPart = TEXT('\0'); } } else if (szCLSpecialPart[0]==TEXT(':') && szCLSpecialPart[1]==TEXT(':')) { // the string was not quoted and it is a special string
LPTSTR pszRealPathBegins = FindPathSection(szCLSpecialPart);
if (*pszRealPathBegins) { // we have a real path, so save it
lstrcpy(szCLPathPart, pszRealPathBegins);
// terminate the special part after the last ::
*pszRealPathBegins = TEXT('\0'); } else { // no there is no real path, just special info
*szCLPathPart = TEXT('\0'); } } else { // not a "special" link
*szCLSpecialPart = TEXT('\0'); } if (*szCLPathPart) { // we have a command line so check for args
szArgs[0] = TEXT('\0'); lpszArgs = PathGetArgs(szCLPathPart); if (*lpszArgs) { *(lpszArgs-1) = TEXT('\0'); lstrcpyn(szArgs, lpszArgs, ARRAYSIZE(szArgs)); DebugMsg(GC_TRACE, TEXT(" Cmd Args:%s"), szArgs); } psl->lpVtbl->SetArguments(psl, szArgs); // arguments
PathUnquoteSpaces(szCLPathPart); PathResolve(szCLPathPart, NULL, 0);
DebugMsg(GC_TRACE, TEXT(" cmd:%s"), (LPTSTR)szCLPathPart); }
if (*szCLPathPart && (dwFlags & BG_RECENT_DOCS)) { SHAddToRecentDocs(SHARD_PATH, szCLPathPart);
// Progman is just going to get a group called "Documents".
if (!(dwFlags & BG_PROG_GRP_CREATED)) { if (Progman_CreateGroup(ppmdde, lpszGroupName)) dwFlags |= BG_PROG_GRP_CREATED; } if (dwFlags & BG_PROG_GRP_CREATED) Progman_ReplaceItem(ppmdde, szName, szCLPathPart, NULL, NULL, 0, NULL); } else if (*szCLPathPart || *szCLSpecialPart) { // all we need to call is setpath, it takes care of creating the
// pidl for us. We have to put back the special / path portions here
// so we can pass the full DARWIN or LOGO3 information.
lstrcpy(szCL, szCLSpecialPart); lstrcat(szCL, szCLPathPart);
psl->lpVtbl->SetPath(psl, szCL); // Icon file.
ParseField(pszLine, 3, szIP, ARRAYSIZE(szIP)); ParseField(pszLine, 4, szNum, ARRAYSIZE(szNum)); iIcon = StrToInt(szNum);
DebugMsg(GC_TRACE, TEXT(" Icon:%s"), (LPTSTR)szIP);
psl->lpVtbl->SetIconLocation(psl, szIP, iIcon); lstrcat(szGroupFolder, TEXT(".lnk"));
// NB Field 5 is dependancy stuff that we don't
// care about.
// WD
#ifdef WINNT
/* For NT default to the users home directory, not nothing (which results in
/ the current directory, which is unpredictable) */ lstrcpy( szWD, TEXT("%HOMEDRIVE%%HOMEPATH%") ); #else
szWD[0] = TEXT('\0'); #endif
ParseField(pszLine, 6, szWD, ARRAYSIZE(szWD)); psl->lpVtbl->SetWorkingDirectory(psl, szWD);
// Field 8 is description for the link
ParseField(pszLine, 8, szDesc, ARRAYSIZE(szDesc)); DebugMsg(GC_TRACE, TEXT(" Description:%s"), (LPTSTR)szDesc); psl->lpVtbl->SetDescription(psl, szDesc); StrToOleStrN(wszPath, ARRAYSIZE(wszPath), szGroupFolder, -1); if (fUpdFolder) ppf->lpVtbl->Save(ppf, wszPath, TRUE); // We've added stuff so don't bother trying to delete the folder
// later.
// fDeleteEmpty = FALSE;
dwFlags &= ~BG_DELETE_EMPTY; // Defer group creation.
// if (!fSendToGrp && !fProgGrpCreated)
if (!(dwFlags & BG_SEND_TO_GRP) && !(dwFlags & BG_PROG_GRP_CREATED)) { if (Progman_CreateGroup(ppmdde, lpszGroupName)) dwFlags |= BG_PROG_GRP_CREATED; } // if (fProgGrpCreated)
if (dwFlags & BG_PROG_GRP_CREATED) { // use szCLPathPart for good ol'e progman
Progman_ReplaceItem(ppmdde, szName, szCLPathPart, szArgs, szIP, iIcon, szWD); } } else { // NB The assumption is that setup.ini will only contain links
// to files that exist. If they don't exist we assume we have
// a bogus setup.ini and skip to the next item.
DebugMsg(DM_ERROR, TEXT("gc.bg: Bogus link info for item %s in setup.ini"), szName); } } else { // Delete all links with this name.
// NB We need to get this from the registry eventually.
if (fUpdFolder) { pszExt = szGroupFolder + lstrlen(szGroupFolder); lstrcpy(pszExt, TEXT(".lnk")); Win32DeleteFile(szGroupFolder); lstrcpy(pszExt, TEXT(".pif")); Win32DeleteFile(szGroupFolder); } // Tell progman too. Be careful not to create empty groups just
// to try to delete items from it.
// if (!fProgGrpShown)
if (!(dwFlags & BG_PROG_GRP_SHOWN)) { // Does the group already exist?
if (Progman_ShowGroup(ppmdde, lpszGroupName)) dwFlags |= BG_PROG_GRP_SHOWN; // if (fProgGrpShown)
if (dwFlags & BG_PROG_GRP_SHOWN) { // Yep, activate it.
Progman_CreateGroup(ppmdde, lpszGroupName); } }
// If it exists, then delete the item otherwise don't bother.
// if (fProgGrpShown)
if (dwFlags & BG_PROG_GRP_SHOWN) Progman_DeleteItem(ppmdde, szName); }
PathRemoveFileSpec(szGroupFolder); // rip the link name off for next link
// Now point to the next line
pszLine += lstrlen(pszLine) + 1; } }
// The group might now be empty now - try to delete it, if there's still
// stuff in there then this will safely fail. NB We don't delete empty
// Startup groups to give users a clue that it's something special.
// if (fUpdFolder && fDeleteEmpty && *szGroupFolder)
if (fUpdFolder && (dwFlags & BG_DELETE_EMPTY) && *szGroupFolder) { DebugMsg(DM_TRACE, TEXT("gc.bg: Deleting %s"), szGroupFolder); // keep trying to remove any directories up the path,
// so we dont leave an empty directory tree structure.
//
// SafeRemoveDirectory fails if the directory is a special folder
if(SafeRemoveDirectory(szGroupFolder)) { while(PathRemoveFileSpec(szGroupFolder)) { if (!SafeRemoveDirectory(szGroupFolder)) break; } } }
ppf->lpVtbl->Release(ppf); psl->lpVtbl->Release(psl); } }
if(hg) { GlobalFree(hg); }
Log(TEXT("Setup.Ini: %s done."), lpszGroupName); }
//---------------------------------------------------------------------------
HDDEDATA CALLBACK DdeCallback(UINT uType, UINT uFmt, HCONV hconv, HSZ hsz1, HSZ hsz2, HDDEDATA hdata, ULONG_PTR dwData1, ULONG_PTR dwData2) { return (HDDEDATA) NULL; }
//---------------------------------------------------------------------------
BOOL _PartnerIsCabinet(HCONV hconv) { //
// (reinerf)
// this sends the magical string [ConfirmCabinetID] to our current DDE partner.
// Explorer.exe will return TRUE here, so we can distinguish it from progman.exe
// which returns FALSE.
//
if (DdeClientTransaction((LPBYTE)c_szRUCabinet, SIZEOF(c_szRUCabinet), hconv, HSZNULL, 0, XTYP_EXECUTE, DDETIMEOUT, NULL)) { return TRUE; } else { return FALSE; } }
//---------------------------------------------------------------------------
// If progman is not the shell then it will be refusing DDE messages so we
// have to enable it here.
void _EnableProgmanDDE(void) { HWND hwnd;
hwnd = FindWindow(c_szProgman, NULL); while (hwnd) { // Is it progman?
if (GetProp(hwnd, c_szAppProgman)) { DebugMsg(DM_TRACE, TEXT("gc.epd: Found progman, enabling dde.")); // NB Progman will clean this up at terminate time.
SetProp(hwnd, c_szEnableDDE, (HANDLE)TRUE); break; } hwnd = GetWindow(hwnd, GW_HWNDNEXT); } }
//---------------------------------------------------------------------------
// Will the real progman please stand up?
BOOL Progman_DdeConnect(PPMDDE ppmdde, HSZ hszService, HSZ hszTopic) { HCONV hconv = HCONVNULL; Assert(ppmdde);
DebugMsg(DM_TRACE, TEXT("gc.p_dc: Looking for progman..."));
_EnableProgmanDDE();
ppmdde->hcl = DdeConnectList(ppmdde->dwInst, hszService, hszTopic, HCONVLISTNULL, NULL); if (ppmdde->hcl) { hconv = DdeQueryNextServer(ppmdde->hcl, hconv); while (hconv) { // DdeQueryConvInfo(hconv, QID_SYNC, &ci);
if (!_PartnerIsCabinet(hconv)) { DebugMsg(DM_TRACE, TEXT("gc.p_dc: Found likely candidate %x"), hconv); ppmdde->hconv = hconv; return TRUE; } else { DebugMsg(DM_TRACE, TEXT("gc.p_dc: Ignoring %x"), hconv); } hconv = DdeQueryNextServer(ppmdde->hcl, hconv); } } DebugMsg(DM_TRACE, TEXT("gc.p_dc: Couldn't find it.")); return FALSE; }
//---------------------------------------------------------------------------
BOOL Window_CreatedBy16bitProcess(HWND hwnd) { DWORD idProcess;
#ifdef WINNT
return( LOWORD(GetWindowLongPtr(hwnd,GWLP_HINSTANCE)) != 0 ); #else
GetWindowThreadProcessId(hwnd, &idProcess); return GetProcessDword(idProcess, GPD_FLAGS) & GPF_WIN16_PROCESS; #endif
}
//---------------------------------------------------------------------------
// (reinerf)
//
// check what the user has as their shell= set to (this is in the
// registry on NT and in the win.ini on win95/memphis.
BOOL IsShellExplorer() { TCHAR szShell[MAX_PATH];
#ifdef WINNT
{ HKEY hKeyWinlogon; DWORD dwSize;
szShell[0] = TEXT('\0');
// Starting with NT4 Service Pack 3, NT honors the value in HKCU over
// the one in HKLM, so read that first.
if (RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"), 0L, KEY_QUERY_VALUE, &hKeyWinlogon) == ERROR_SUCCESS) { dwSize = SIZEOF(szShell); RegQueryValueEx(hKeyWinlogon, TEXT("shell"), NULL, NULL, (LPBYTE)szShell, &dwSize); RegCloseKey(hKeyWinlogon); }
if (!szShell[0]) { // no HKCU value, so check HKLM
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"), 0L, KEY_QUERY_VALUE, &hKeyWinlogon) == ERROR_SUCCESS) { dwSize = SIZEOF(szShell); RegQueryValueEx(hKeyWinlogon, TEXT("shell"), NULL, NULL, (LPBYTE)szShell, &dwSize); RegCloseKey(hKeyWinlogon); } } } #else
{ // on win95 we need to read the shell= line from the win.ini
GetPrivateProfileString(TEXT("boot"), TEXT("shell"), TEXT("explorer.exe"), szShell, MAX_PATH, TEXT("system.ini")); } #endif
if (lstrcmpi(TEXT("explorer.exe"), szShell) == 0) return TRUE; else return FALSE; }
//---------------------------------------------------------------------------
BOOL Progman_IsRunning(void) { HWND hwnd; TCHAR sz[MAX_PATH] = {0};
hwnd = GetWindow(GetDesktopWindow(), GW_CHILD); while (hwnd) { GetClassName(hwnd, sz, ARRAYSIZE(sz)); #ifdef WINNT
if (lstrcmpi(sz, c_szProgman) == 0) #else
if (Window_CreatedBy16bitProcess(hwnd) && (lstrcmpi(sz, c_szProgman) == 0)) #endif
{ return TRUE; } hwnd = GetWindow(hwnd, GW_HWNDNEXT); } return FALSE; }
//---------------------------------------------------------------------------
BOOL Progman_Startup(PPMDDE ppmdde) { HSZ hszService, hszTopic; TCHAR szWindowsDir[MAX_PATH]; int i = 0; Assert(ppmdde); // if the users shell is explorer, we dont bother
// launching progman.exe, or doing any DDE work
if (IsShellExplorer()) { g_fInitDDE = FALSE; g_fDoProgmanDde = FALSE; ppmdde->fStartedProgman = FALSE; return FALSE; }
// Is Progman running?
if (Progman_IsRunning()) { // Yep.
DebugMsg(DM_TRACE, TEXT("gc.p_s: Progman is already running.")); ppmdde->fStartedProgman = FALSE; } else { // Nope - we'll try to startit.
DebugMsg(DM_TRACE, TEXT("gc.p_s: Starting Progman...")); ppmdde->fStartedProgman = TRUE;
GetWindowsDirectory(szWindowsDir, MAX_PATH); #ifdef UNICODE
// on WINNT progman lives in %windir%\system32
lstrcat(szWindowsDir, TEXT("\\System32\\")); #else
// on win95 & memphis, progman lives in %windir%
lstrcat(szWindowsDir, TEXT("\\")); #endif
lstrcat(szWindowsDir, c_szProgmanExe);
#ifdef UNICODE
{ STARTUPINFO si; PROCESS_INFORMATION pi;
si.cb = SIZEOF(si); si.lpReserved = NULL; si.lpDesktop = NULL; si.lpTitle = NULL; si.dwX = (DWORD)CW_USEDEFAULT; si.dwY = (DWORD)CW_USEDEFAULT; si.dwXSize = (DWORD)CW_USEDEFAULT; si.dwYSize = (DWORD)CW_USEDEFAULT; si.dwXCountChars = 0; si.dwYCountChars = 0; si.dwFillAttribute = 0; si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; si.cbReserved2 = 0; si.lpReserved2 = 0; si.hStdInput = NULL; si.hStdOutput = NULL; si.hStdError = NULL;
if (CreateProcess(szWindowsDir, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } } #else
WinExec(szWindowsDir, SW_HIDE); #endif
// Give progman a bit of time to startup but bail after 10s.
while (!Progman_IsRunning() && (i < 10)) { Sleep(1000); i++; } }
// Just a bit longer.
Sleep(1000); // Grab the focus back?
if (g_hwndProgress) SetForegroundWindow(g_hwndProgress);
// we are going to try to do DDE, so set g_fInitDDE = TRUE,
// so that we know to call DdeUninitialize later
g_fInitDDE = TRUE;
ppmdde->dwInst = 0; DdeInitialize(&ppmdde->dwInst, DdeCallback, APPCLASS_STANDARD|APPCMD_CLIENTONLY, 0); hszService = DdeCreateStringHandle(ppmdde->dwInst, (LPTSTR)c_szProgman, CP_WINNEUTRAL); hszTopic = DdeCreateStringHandle(ppmdde->dwInst, (LPTSTR)c_szProgman, CP_WINNEUTRAL); g_fDoProgmanDde = Progman_DdeConnect(ppmdde, hszService, hszTopic); DdeFreeStringHandle(ppmdde->dwInst, hszService); DdeFreeStringHandle(ppmdde->dwInst, hszTopic); return g_fDoProgmanDde; }
//---------------------------------------------------------------------------
BOOL FindProgmanIni(LPTSTR pszPath) { OFSTRUCT os; #ifdef UNICODE
LPTSTR lpszFilePart; #endif
// NB Don't bother looking for the old windows directory, in the case of
// an upgrade it will be the current windows directory.
GetWindowsDirectory(pszPath, MAX_PATH); PathAppend(pszPath, c_szProgmanIni);
if (PathFileExists(pszPath)) { return TRUE; } #ifdef UNICODE
else if (SearchPath(NULL, c_szProgmanIni, NULL, MAX_PATH, pszPath, &lpszFilePart) != 0) { return TRUE; } #else
else if (OpenFile(c_szProgmanIni, &os, OF_EXIST) != -1) { lstrcpy(pszPath, os.szPathName); return TRUE; } #endif
DebugMsg(DM_ERROR, TEXT("Can't find progman.ini")); return FALSE; }
//---------------------------------------------------------------------------
void UpdateTimeStampCallback(LPCTSTR lpszGroup) { WIN32_FIND_DATA fd; HANDLE hff;
DebugMsg(DM_TRACE, TEXT("gc.utc: Updating timestamp for %s."), lpszGroup);
hff = FindFirstFile(lpszGroup, &fd); if (hff != INVALID_HANDLE_VALUE) { Group_WriteLastModDateTime(lpszGroup,fd.ftLastWriteTime.dwLowDateTime); FindClose(hff); } }
//---------------------------------------------------------------------------
void Progman_Shutdown(PPMDDE ppmdde) { TCHAR szIniFile[MAX_PATH]; // only shutdown progman if we actually started it and we
// were doing DDE with it.
if (ppmdde->fStartedProgman && g_fDoProgmanDde) { Log(TEXT("p_s: Shutting down progman...")); Log(TEXT("p_s: DdeClientTransaction.")); DebugMsg(DM_TRACE, TEXT("gc.p_s: Shutting down progman.")); DdeClientTransaction((LPBYTE)c_szExitProgman, SIZEOF(c_szExitProgman), ppmdde->hconv, HSZNULL, 0, XTYP_EXECUTE, DDETIMEOUT, NULL); } // if we initialzied DDE then uninit it now...
if (g_fInitDDE) { Log(TEXT("p_s: DdeDisconnect.")); DdeDisconnectList(ppmdde->hcl);
Log(TEXT("p_s: DdeUnitialize.")); DdeUninitialize(ppmdde->dwInst); }
// We just went and modified all progman groups so update the time stamps.
FindProgmanIni(szIniFile); Log(TEXT("p_s: Updating time stamps.")); Group_Enum(UpdateTimeStampCallback, FALSE, TRUE); // Re-do the timestamp so that cab32 won't do another gprconv.
UpdateTimeStampCallback(szIniFile);
Log(TEXT("p_s: Done.")); }
//----------------------------------------------------------------------------
void BuildSectionGroups(LPCTSTR lpszIniFile, LPCTSTR lpszSection, PPMDDE ppmdde, BOOL fUpdFolder, DWORD dwFlags) { int cb = 0; LPTSTR pszLine; TCHAR szSectName[CCHSZSHORT]; TCHAR szGroupName[2*MAX_PATH]; LPTSTR lpBuf; // First allocate a buffer to read the section into
lpBuf = (LPTSTR) GlobalAlloc(GPTR, BUFSIZES); // Should never exceed 64K?
if (lpBuf) { // Now Read in the secint into our buffer.
if (PathFileExists(lpszIniFile)) cb = GetPrivateProfileSection(lpszSection, lpBuf, BUFSIZES/SIZEOF(TCHAR), lpszIniFile); if (cb > 0) { Group_SetProgressDesc(IDS_CREATINGNEWSCS); pszLine = lpBuf; while (*pszLine) { // Make sure we did not fall off the deep end
if (cb < (int)(pszLine - lpBuf)) { Assert(FALSE); break; }
// Now lets extract the fields off of the line
ParseField(pszLine, 0, szSectName, ARRAYSIZE(szSectName)); ParseField(pszLine, 1, szGroupName, ARRAYSIZE(szGroupName));
// Pass off to build that group and update progman.
BuildGroup(lpszIniFile, szSectName, szGroupName, ppmdde, fUpdFolder, dwFlags);
// Now setup process the next line in the section
pszLine += lstrlen(pszLine) + 1; } } GlobalFree((HGLOBAL)lpBuf); SHChangeNotify( 0, SHCNF_FLUSH, NULL, NULL); // Kick tray into updating for real
} }
#ifdef WINNT
typedef UINT (__stdcall * PFNGETSYSTEMWINDOWSDIRECTORYW)(LPWSTR pwszBuffer, UINT cchSize);
//
// we need a wrapper for this since it only exists on NT5
//
UINT Wrap_GetSystemWindowsDirectoryW(LPWSTR pszBuffer, UINT cchBuff) { static PFNGETSYSTEMWINDOWSDIRECTORYW s_pfn = (PFNGETSYSTEMWINDOWSDIRECTORYW)-1;
if (s_pfn) { HINSTANCE hinst = GetModuleHandle(TEXT("KERNEL32.DLL"));
if (hinst) s_pfn = (PFNGETSYSTEMWINDOWSDIRECTORYW)GetProcAddress(hinst, "GetSystemWindowsDirectoryW"); else s_pfn = NULL; }
if (s_pfn) return s_pfn(pszBuffer, cchBuff); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return 0; } #endif // WINNT
//
// We now look for setup.ini in 3 places: first in %userprofile%, next GetWindowsDirectory(),
// and finally in the GetWindowsSystemDirectory() (since hydra can change the return value for
// GetWindowsDirectory but apps still might be putting stuff there).
//
// The reason we look in the %USERPROFILE% directory is that Win2000's new high-security model
// does not give default users write permission to %windir%, so apps will not be able to even
// create a setup.ini in that location. This breaks the per-user install stubs (ie4uinit.exe),
// who are now going to create the setup.ini in %USERPROFILE%, where the user will always have
// write permission.
//
void FindSetupIni(LPTSTR szSetupIniPath, int cchSetupIniPath) { TCHAR szPath[MAX_PATH];
ExpandEnvironmentStrings(TEXT("%USERPROFILE%"), szPath, ARRAYSIZE(szPath)); PathAppend(szPath, c_szGrpConvInf);
if (PathFileExists(szPath)) { lstrcpyn(szSetupIniPath, szPath, cchSetupIniPath); return; }
// next try GetWindowsDirectory()
GetWindowsDirectory(szPath, ARRAYSIZE(szPath)); PathAppend(szPath, c_szGrpConvInf);
if (PathFileExists(szPath)) { lstrcpyn(szSetupIniPath, szPath, cchSetupIniPath); return; }
#ifdef WINNT
// finally if we are on NT try GetWindowsSystemDirectory()
if (Wrap_GetSystemWindowsDirectoryW(szPath, ARRAYSIZE(szPath))) { PathAppend(szPath, c_szGrpConvInf);
if (PathFileExists(szPath)) { lstrcpyn(szSetupIniPath, szPath, cchSetupIniPath); return; } } #endif
// We faild to find it! For compat reasons, we just do what the old code
// does: GetWindowsDirectory() and PathAppend() and plow ahead...
GetWindowsDirectory(szPath, ARRAYSIZE(szPath)); PathAppend(szPath, c_szGrpConvInf); return; }
//---------------------------------------------------------------------------
// This parses the grpconv.inf file and creates the appropriate programs
// folders.
void BuildDefaultGroups(void) { TCHAR szPath[MAX_PATH]; PMDDE pmdde;
Log(TEXT("bdg: ...")); // seek and you will find...
FindSetupIni(szPath, ARRAYSIZE(szPath));
// Now lets walk through the different items in this section
Group_CreateProgressDlg(); // Change the text in the progress dialog so people don't think we're
// doing the same thing twice.
// Group_SetProgressDesc(IDS_CREATINGNEWSCS);
// Crank up Progman.
Progman_Startup(&pmdde); // Build the stuff.
BuildSectionGroups(szPath, c_szProgmanGroups, &pmdde, TRUE, BG_DELETE_EMPTY); BuildSectionGroups(szPath, c_szProgmanOnly, &pmdde, FALSE, BG_DELETE_EMPTY); // Custom sections.
BuildSectionGroups(szPath, c_szDesktopGroups, &pmdde, FALSE, BG_FORCE_DESKTOP); BuildSectionGroups(szPath, c_szStartupGroups, &pmdde, FALSE, BG_FORCE_STARTUP); BuildSectionGroups(szPath, c_szSendToGroups, &pmdde, FALSE, BG_FORCE_SENDTO); BuildSectionGroups(szPath, c_szRecentDocsGroups, &pmdde, FALSE, BG_FORCE_RECENT);
// Close down progman.
Progman_Shutdown(&pmdde); Group_DestroyProgressDlg(); // HACKHACK (reinerf) - we cant rename setup.ini -> setup.old because this causes problems
// the second time when it will fail because setup.old already exists (and we possibly dont
// have acls to overwrite it), and when it fails we orpan setup.ini as well (because the
// rename failed!!). This after this, all fututre attempts to create a setup.ini will fail,
// because one already exists, and we may not have acls to overwrite it. So, we just always
// delete setup.ini when we are done.
Win32DeleteFile(szPath); Log(TEXT("bdg: Done.")); }
|