|
|
/*
* NMPGMGRP - Tiny program to add and remove items from the program group. * Its initial purpose is to support Windows NT common program groups, * which are not supported by GRPCONV. * * Usage: * * NMPGMGRP /add [/common] [/g:"<group name>"] /n:"<program name>" * /p:"<program path>" * NMPGMGRP /delete [/common] [/g:"<group name>"] /n:"<program name>" * * NMPGMGRP /i /n:"<src mnmdd.dll>" /p:"<dst mnmdd.dll>" INSTALL NT DD * NMPGMGRP /u /n:"<src mnmdd.dll>" UNINSTALL NT DD * * NMPGMGRP /s [/q] /n:"<inf file>" /f"<friendly name>" SETUP * * /add is used to add a new program item. * /delete is used to remove an existing program item. * * /common indicates that this item belong in the common (as opposed to * per-user) program groups. * * <group name> is the name of the program group, expressed as a pathname * relative to the Programs group. For items in the Programs group, * this parameter should be omitted. * * <program name> is the name of the program, and is also used as the name * of the shortcut file itself. * * <program path> is the full path name of the program. * * <inf file> is the name of the installation inf. * * <friendly name> is the text to be used for any message box title. * * Limitations: * * Because some of these strings may contain spaces, the group name, program * name, and program path MUST be enclosed in quotes. Currently we do not * support strings with quotes in them. * * Some of the system functions used in this program are Unicode * specific, so this program will require some modifications to run on * Windows 95. * * Author: * DannyGl, 23 Mar 97 */
#include "precomp.h"
#include "resource.h"
#include <nmremote.h>
#pragma intrinsic(memset)
// DEBUG only -- Define debug zone
#ifdef DEBUG
HDBGZONE ghZone = NULL; // Node Controller Zones
static PTCHAR rgZones[] = { TEXT("NMPgmGrp") }; #endif // DEBUG
// PROGRAM_ITEM_INFO structure:
//
// Intended to be passed as input to the CreateProgramItem and
// DeleteProgramItem functions. Fields are:
//
// pszProgramGroup - The full path of the program group in which the
// item is to be stored.
// pszProgramName - The name of the program item.
// pszProgramPath - The full path of the program.
typedef struct tagProgramItemInfo { PTSTR pszProgramGroup; PTSTR pszProgramName; PTSTR pszProgramPath; } PROGRAM_ITEM_INFO, *PPROGRAM_ITEM_INFO;
// Command line option data
enum tagGroupOperation { GRPOP_NONE = 0, GRPOP_ADD, GRPOP_DEL, GRPOP_NTDDINSTALL, GRPOP_NTDDUNINSTALL, GRPOP_SETUP } g_goAction;
BOOL g_fCommonGroup = FALSE; PTSTR g_pszGroupName = NULL; PTSTR g_pszProgramName = NULL; PTSTR g_pszProgramPath = NULL; PTSTR g_pszFriendlyName = NULL; BOOL g_fQuietInstall = FALSE;
const TCHAR g_cszSetupDll[] = TEXT("advpack.dll"); const TCHAR g_cszSetupEntry[] = TEXT("LaunchINFSection"); typedef int (CALLBACK * PFNSETUPENTRY)(HWND hwnd, HINSTANCE hinst, LPTSTR lpszCmdLine, int nCmdShow);
// ProcessCommandLineArgs:
//
// Get the command line and parse it into individual parameters using the
// above global variables.
//
// Return: TRUE on success, FALSE if it could not parse the command line.
BOOL ProcessCommandLineArgs(void) { PTSTR pszTemp;
pszTemp = GetCommandLine();
// Search for forward slashes
pszTemp = (PTSTR) _StrChr(pszTemp, TEXT('/'));
while (NULL != pszTemp) { PTSTR *ppszCurrentArg = NULL;
switch(*++pszTemp) { case TEXT('S'): case TEXT('s'): ASSERT(GRPOP_NONE == g_goAction); // Check for duplicate parameter
g_goAction = GRPOP_SETUP; break;
case TEXT('I'): case TEXT('i'): //
// Install NT-specific display driver stuff
//
ASSERT(GRPOP_NONE == g_goAction); // Check for duplicate parameter
g_goAction = GRPOP_NTDDINSTALL; break;
case TEXT('U'): case TEXT('u'): //
// Uninstall NT-specific display driver stuff
//
ASSERT(GRPOP_NONE == g_goAction); // Check for duplicate parameter
g_goAction = GRPOP_NTDDUNINSTALL; break;
case TEXT('A'): case TEXT('a'): ASSERT(GRPOP_NONE == g_goAction); // Check for duplicate parameter
g_goAction = GRPOP_ADD; break;
case TEXT('D'): case TEXT('d'): ASSERT(GRPOP_NONE == g_goAction); // Check for duplicate parameter
g_goAction = GRPOP_DEL; break;
case TEXT('C'): case TEXT('c'): ASSERT(! g_fCommonGroup); // Check for duplicate parameter
g_fCommonGroup = TRUE; break;
case TEXT('Q'): case TEXT('q'): g_fQuietInstall = TRUE; break;
case TEXT('G'): case TEXT('g'): if (NULL == ppszCurrentArg) { ppszCurrentArg = &g_pszGroupName; }
// NO break HERE -- fall through
case TEXT('N'): case TEXT('n'): if (NULL == ppszCurrentArg) { ppszCurrentArg = &g_pszProgramName; }
// NO break HERE -- fall through
case TEXT('P'): case TEXT('p'): if (NULL == ppszCurrentArg) { ppszCurrentArg = &g_pszProgramPath; }
// NO break HERE -- fall through
case TEXT('F'): case TEXT('f'): if (NULL == ppszCurrentArg) { ppszCurrentArg = &g_pszFriendlyName; }
// ***** Processing for all string parameters *****
ASSERT(NULL == *ppszCurrentArg); // Check for duplicate parameter
// Save the string pointer after skipping past the colon and open quote
ASSERT(TEXT(':') == pszTemp[1] && TEXT('\"') == pszTemp[2]); *ppszCurrentArg = pszTemp += 3;
// Find the closing quote and set it to null, then skip past it
// Note that we don't handle strings with quotes in them.
pszTemp = (PTSTR) _StrChr(pszTemp, TEXT('\"')); ASSERT(NULL != pszTemp); if (NULL != pszTemp) { *pszTemp++ = TEXT('\0'); } else { return FALSE; }
break;
default: ERROR_OUT(("Unknown parameter begins at %s", pszTemp)); return FALSE;
break; }
// Find the next option flag
ASSERT(NULL != pszTemp); pszTemp = (PTSTR) _StrChr(pszTemp, TEXT('/')); }
// Return based on minimal parameter validation:
// 1) The program name must be specified.
// 2) Either add or delete must be specified.
// 3) If add is specified, the program path must be specified
switch (g_goAction) { case GRPOP_ADD: case GRPOP_NTDDINSTALL: return((NULL != g_pszProgramName) && (NULL != g_pszProgramPath));
case GRPOP_DEL: case GRPOP_NTDDUNINSTALL: return(NULL != g_pszProgramName);
case GRPOP_SETUP: return((NULL != g_pszProgramName) && (NULL != g_pszFriendlyName));
default: return(FALSE); } }
// GetFolderPathname:
//
// Use the official shell interfaces to retrieve the full pathname of a
// a programs folder.
//
// Input:
// ptstrPath, ccPath - The pointer to a size of the buffer in
// which to store the path.
// nFolder - The folder to locate, expressed as a CSIDL constant.
// See SHGetSpecialFolderLocation for details.
// pctstrSubFolder - A specific subfolder, can be NULL if not specified.
// If specified, this is appended (after a backslash) to the path.
//
// Returns:
// An HRESULT to indicate success or failure of the Shell methods.
// The path is returned in <ptstrPath>.
HRESULT GetFolderPathname( PTSTR ptstrPath, UINT cchPath, int nFolder, LPCTSTR pctstrSubFolder) { HRESULT hr; LPMALLOC pMalloc = NULL; LPSHELLFOLDER pDesktopFolder = NULL; LPITEMIDLIST pidlSpecialFolder = NULL;
// Get the allocator object
hr = CoGetMalloc(MEMCTX_TASK, &pMalloc);
// Get the desktop object
if (SUCCEEDED(hr)) { hr = SHGetDesktopFolder(&pDesktopFolder); }
// Get the special folder item ID
if (SUCCEEDED(hr)) { hr = SHGetSpecialFolderLocation( GetDesktopWindow(), nFolder, &pidlSpecialFolder); }
// Retrieve the folder name
STRRET strFolder;
if (SUCCEEDED(hr)) { strFolder.uType = STRRET_WSTR;
hr = pDesktopFolder->GetDisplayNameOf( pidlSpecialFolder, SHGDN_FORPARSING, &strFolder); }
if (SUCCEEDED(hr)) { CUSTRING custrPath;
switch(strFolder.uType) { case STRRET_WSTR: custrPath.AssignString(strFolder.pOleStr);
break;
case STRRET_OFFSET: custrPath.AssignString(((LPSTR) pidlSpecialFolder) + strFolder.uOffset);
break;
case STRRET_CSTR: custrPath.AssignString(strFolder.cStr);
break; }
ASSERT(NULL != (PTSTR) custrPath);
lstrcpyn(ptstrPath, custrPath, cchPath);
if (STRRET_WSTR == strFolder.uType) { pMalloc->Free(strFolder.pOleStr); }
}
// Append subgroup name, if it's specified
if (SUCCEEDED(hr) && NULL != pctstrSubFolder) { // BUGBUG - We don't create this folder if it doesn't already exist
int cchLen = lstrlen(ptstrPath);
ASSERT((UINT) cchLen < cchPath);
// Insert a path separator
ptstrPath[cchLen++] = TEXT('\\');
// Copy the subgroup
lstrcpyn(ptstrPath + cchLen, pctstrSubFolder, cchPath - cchLen); }
// Release resources
if (pDesktopFolder) { pDesktopFolder->Release(); }
if (pMalloc) { if (pidlSpecialFolder) { pMalloc->Free(pidlSpecialFolder); }
pMalloc->Release(); }
return hr; }
// BuildLinkFileName:
//
// Inline utility function to construct the full file name of a link given its
// directory name and item name.
inline void BuildLinkFileName( OUT LPWSTR wszOutputPath, IN LPCTSTR pcszDirectory, IN LPCTSTR pcszFile) { // The file name is of the form <directory>\<file>.LNK
#ifdef UNICODE
static const WCHAR wszFileFormat[] = L"%s\\%s.LNK"; #else // UNICODE
static const WCHAR wszFileFormat[] = L"%hs\\%hs.LNK"; #endif // UNICODE
int cchSize;
cchSize = wsprintfW( wszOutputPath, wszFileFormat, pcszDirectory, pcszFile);
ASSERT(cchSize > ARRAY_ELEMENTS(wszFileFormat) - 1 && cchSize < MAX_PATH); }
// CreateProgramItem:
//
// Use the official shell interfaces to create a shortcut to a program.
//
// Input: A pointer to a PROGRAM_ITEM_INFO structure, defined above.
//
// Returns:
// An HRESULT to indicate success or failure of the Shell methods.
HRESULT CreateProgramItem( PPROGRAM_ITEM_INFO ppii) { HRESULT hr; IShellLink *psl = NULL; IPersistFile *ppf = NULL;
// Get the shell link object
hr = CoCreateInstance( CLSID_ShellLink, NULL, CLSCTX_INPROC, IID_IShellLink, (LPVOID *) &psl);
// Fill in the fields of the program group item
if (SUCCEEDED(hr)) { hr = psl->SetDescription(ppii->pszProgramName); }
if (SUCCEEDED(hr)) { hr = psl->SetPath(ppii->pszProgramPath); }
// Save the link as a file
if (SUCCEEDED(hr)) { hr = psl->QueryInterface(IID_IPersistFile, (LPVOID *) &ppf); }
if (SUCCEEDED(hr)) { WCHAR wszFileName[MAX_PATH];
BuildLinkFileName( wszFileName, ppii->pszProgramGroup, ppii->pszProgramName);
hr = ppf->Save(wszFileName, TRUE); }
// Release the objects we used
if (ppf) { ppf->Release(); }
if (psl) { psl->Release(); }
return hr; }
// DeleteProgramItem:
//
// Delete a shortcut to a program.
//
// Input: A pointer to a PROGRAM_ITEM_INFO structure, defined above.
//
// Returns:
// An HRESULT to indicate success or failure of the Shell methods.
HRESULT DeleteProgramItem( PPROGRAM_ITEM_INFO ppii) { HRESULT hr = S_OK;
WCHAR wszFileName[MAX_PATH];
BuildLinkFileName( wszFileName, ppii->pszProgramGroup, ppii->pszProgramName);
if (! DeleteFileW(wszFileName)) { WARNING_OUT(("DeleteFile failed")); hr = E_FAIL; }
return hr; }
//
// NtDDInstall()
// This does NT-specific display driver install stuff, which depends on
// whether it's NT4 or NT5
//
//
HRESULT NtDDInstall(LPTSTR pszOrigDd, LPTSTR pszNewDd) { HRESULT hr = E_FAIL; OSVERSIONINFO osvi; RegEntry re(NM_NT_DISPLAY_DRIVER_KEY, HKEY_LOCAL_MACHINE, FALSE);
//
// If NT4, set service key to disabled
// If NT5, copy mnmdd.dll from NM dir to cur (system32) dir
//
osvi.dwOSVersionInfoSize = sizeof(osvi);
if (!GetVersionEx(&osvi)) { ERROR_OUT(("GetVersionEx() failed")); goto AllDone; }
if ((osvi.dwPlatformId == VER_PLATFORM_WIN32s) || (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)) { WARNING_OUT(("NT setup running on non-NT platform!")); goto AllDone; }
if (osvi.dwMajorVersion >= 5) { //
// This is NT5. Always set the service key to enabled (in case
// the end user managed to munge it) and copy mnmdd.dll to the
// current (system) directory. For example, if somebody had a
// stand-alone version of a beta, uninstalled it, then installed
// NM 3.0 proper--or same for 2.11.
//
re.SetValue(REGVAL_NM_NT_DISPLAY_DRIVER_ENABLED, NT_DRIVER_START_SYSTEM);
if (!CopyFile(pszOrigDd, pszNewDd, FALSE)) { WARNING_OUT(("CopyFile from %s to %s failed", pszOrigDd, pszNewDd)); goto AllDone; } } else { // This is NT4. Set the disabled service key
re.SetValue(REGVAL_NM_NT_DISPLAY_DRIVER_ENABLED, NT_DRIVER_START_DISABLED); }
hr = S_OK;
AllDone: return(hr); }
//
// NtDDUninstall()
// This does NT-specific display driver uninstall stuff, which depends
// on whether it's NT4 or NT5
//
HRESULT NtDDUninstall(LPTSTR pszOrigFile) { HRESULT hr = E_FAIL; OSVERSIONINFO osvi;
//
// If NT4, set service key to disabled
// If NT5, delete mnmdd.dll from cur (system32) dir
//
osvi.dwOSVersionInfoSize = sizeof(osvi);
if (!GetVersionEx(&osvi)) { ERROR_OUT(("GetVersionEx() failed")); goto AllDone; }
if ((osvi.dwPlatformId == VER_PLATFORM_WIN32s) || (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)) { ERROR_OUT(("NT setup running on non-NT platform!")); goto AllDone; }
if (osvi.dwMajorVersion >= 5) { // This is NT5. Delete mnmdd.dll from the current (system) directory
if (!DeleteFile(pszOrigFile)) { WARNING_OUT(("DeleteFile of %s failed", pszOrigFile)); goto AllDone; } } else { // This is NT4. Set the disabled service key
RegEntry re(NM_NT_DISPLAY_DRIVER_KEY, HKEY_LOCAL_MACHINE, FALSE);
re.SetValue(REGVAL_NM_NT_DISPLAY_DRIVER_ENABLED, NT_DRIVER_START_DISABLED); }
hr = S_OK;
AllDone: return(hr); }
UINT _MessageBox(HINSTANCE hInst, UINT uID, LPCTSTR lpCaption, UINT uType) { TCHAR szText[512];
if (0 != LoadString(hInst, uID, szText, CCHMAX(szText))) { return MessageBox(NULL, szText, lpCaption, uType); }
return IDCANCEL; }
#define CONF_INIT_EVENT TEXT("CONF:Init")
BOOL FIsNetMeetingRunning() { HANDLE hEvent;
hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, SERVICE_STOP_EVENT); if (hEvent) { CloseHandle(hEvent); return TRUE; }
hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, CONF_INIT_EVENT); if (hEvent) { CloseHandle(hEvent); return TRUE; }
return FALSE; }
BOOL FIsNT5() { OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof(osvi);
if (GetVersionEx(&osvi)) { if ((osvi.dwPlatformId != VER_PLATFORM_WIN32s) && (osvi.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS)) { if (osvi.dwMajorVersion >= 5) { return TRUE; } } } else { ERROR_OUT(("GetVersionEx() failed")); } return FALSE; }
#define IE4_KEY TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Last Update\\IEXPLOREV4")
BOOL FIsIE4Installed() { RegEntry re(IE4_KEY, HKEY_LOCAL_MACHINE, FALSE); if (ERROR_SUCCESS != re.GetError()) { return FALSE; }
return TRUE; }
#define INTEL_KEY1 TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\vphone.exe")
#define INTEL_KEY2 TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\rvp.exe")
#define INTEL_KEY3 TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\vp30.exe")
#define INTEL_NM_VERSION_SZ TEXT("NetMeeting")
#define INTEL_NM_VERSION_DW 3
#define PANTHER_KEY TEXT("CLSID\\{690968D0-418C-11D1-8E0B-00A0C95A83DA}\\Version")
#define PANTHER_VERSION_VALUE TEXT("1.0")
#define TRANSPORTS_KEY TEXT("SOFTWARE\\Microsoft\\Conferencing\\Transports")
#define REGKEY_PSTN TEXT("PSTN")
#define REGKEY_TCPIP TEXT("TCPIP")
#define REGKEY_IPX TEXT("IPX")
#define REGKEY_NETBIOS TEXT("NETBIOS")
#define REGKEY_DIRCB TEXT("DIRCB")
long GetIntelVersion(LPCTSTR pszKey) { RegEntry re(pszKey, HKEY_LOCAL_MACHINE, FALSE); if (ERROR_SUCCESS != re.GetError()) { return -1; }
return re.GetNumber(INTEL_NM_VERSION_SZ, 0); }
BOOL FAnyBadIntelApps() { long lVersion;
lVersion = GetIntelVersion(INTEL_KEY1); if (0 > lVersion) { lVersion = GetIntelVersion(INTEL_KEY2); if (0 > lVersion) { lVersion = GetIntelVersion(INTEL_KEY3); } }
return ((0 <= lVersion) && (3 > lVersion)); }
BOOL FAnyBadPantherApps() { RegEntry re(PANTHER_KEY, HKEY_CLASSES_ROOT, FALSE); if (ERROR_SUCCESS != re.GetError()) { return FALSE; }
LPCTSTR pszVersion = re.GetString(TEXT(""));
return 0 == lstrcmp(pszVersion, PANTHER_VERSION_VALUE); }
BOOL FAnyUnknownTransports() { RegEntry TransportsKey(TRANSPORTS_KEY, HKEY_LOCAL_MACHINE, FALSE); RegEnumSubKeys EnumTransports(&TransportsKey);
while( 0 == EnumTransports.Next() ) { LPCTSTR pszName = EnumTransports.GetName();
if ((0 != lstrcmpi(pszName, REGKEY_PSTN)) && (0 != lstrcmpi(pszName, REGKEY_TCPIP)) && (0 != lstrcmpi(pszName, REGKEY_IPX)) && (0 != lstrcmpi(pszName, REGKEY_NETBIOS)) && (0 != lstrcmpi(pszName, REGKEY_DIRCB))) { return TRUE; } }
return FALSE; }
BOOL FAnyIncompatibleApps() { return FAnyUnknownTransports() || FAnyBadIntelApps() || FAnyBadPantherApps(); }
HRESULT Setup(HINSTANCE hInst, LPTSTR pszInfFile, LPTSTR pszFriendlyName, BOOL fQuietInstall) { if (FIsNT5()) { _MessageBox(hInst, IDS_SETUP_WIN2K, pszFriendlyName, MB_OK); // if the SHFT-CTRL was pressed continue with the install, else exit
if ((0 == GetAsyncKeyState(VK_CONTROL)) || (0 == GetAsyncKeyState(VK_SHIFT))) { return S_FALSE; } }
if (!FIsIE4Installed()) { _MessageBox(hInst, IDS_SETUP_IE4, pszFriendlyName, MB_OK); return S_FALSE; } while (FIsNetMeetingRunning()) { if (IDCANCEL == _MessageBox(hInst, IDS_SETUP_RUNNING, pszFriendlyName, MB_OKCANCEL)) { return S_FALSE; } }
if (!fQuietInstall) { if (FAnyIncompatibleApps()) { if (IDNO == _MessageBox(hInst, IDS_SETUP_INCOMPATIBLE, pszFriendlyName, MB_YESNO)) { return S_FALSE; } } }
HRESULT hr = S_FALSE;
HINSTANCE hLib = LoadLibrary(g_cszSetupDll); if (NULL != hLib) { PFNSETUPENTRY pfnEntry = (PFNSETUPENTRY)GetProcAddress(hLib, g_cszSetupEntry); if (pfnEntry) { TCHAR szArgs[MAX_PATH]; lstrcpy(szArgs, pszInfFile); if (fQuietInstall) { lstrcat(szArgs, TEXT(",,1,N")); } else { lstrcat(szArgs, TEXT(",,,N")); }
int iRet = pfnEntry(NULL, GetModuleHandle(NULL), szArgs, SW_SHOWNORMAL); if (0 == iRet) { if (!fQuietInstall) { _MessageBox(hInst, IDS_SETUP_SUCCESS, pszFriendlyName, MB_OK); }
hr = S_OK; } } else { ERROR_OUT(("Could not find setup DLL entry point")); } FreeLibrary(hLib); } else { ERROR_OUT(("Could not load setup DLL")); }
return hr; }
// main:
//
// The entry point of the program, it pulls everything together using the
// above utility functions.
void __cdecl main( void) { HRESULT hr; HINSTANCE hInstance; BOOL fErrorReported = FALSE; TCHAR szFolderPath[MAX_PATH];
// Initialization
hInstance = GetModuleHandle(NULL); DBGINIT(&ghZone, rgZones); DBG_INIT_MEMORY_TRACKING(hInstance);
hr = CoInitialize(NULL);
// Process the command line.
if (SUCCEEDED(hr)) { hr = ProcessCommandLineArgs() ? S_OK : E_INVALIDARG; } else if (!fErrorReported) { ERROR_OUT(("CoInitialize fails")); fErrorReported = TRUE; }
// Retreive the path of the Programs folder
if (SUCCEEDED(hr)) { if ((g_goAction != GRPOP_NTDDINSTALL) && (g_goAction != GRPOP_NTDDUNINSTALL) && (g_goAction != GRPOP_SETUP)) { hr = GetFolderPathname( szFolderPath, CCHMAX(szFolderPath), g_fCommonGroup ? CSIDL_COMMON_PROGRAMS : CSIDL_PROGRAMS, g_pszGroupName); } } else if (!fErrorReported) { ERROR_OUT(("Invalid command line parameters specified.")); fErrorReported = TRUE; }
// Add or delete the program item, as appropriate
if (SUCCEEDED(hr)) { PROGRAM_ITEM_INFO pii;
switch(g_goAction) { case GRPOP_NTDDINSTALL: //
// Hack: Use program name for source mnmdd.dll
// Use program path for dest mnmdd.dll
//
hr = NtDDInstall(g_pszProgramName, g_pszProgramPath); break;
case GRPOP_NTDDUNINSTALL: //
// Hack: Use program name for source mnmdd.dll
//
hr = NtDDUninstall(g_pszProgramName); break;
case GRPOP_ADD: pii.pszProgramGroup = szFolderPath; pii.pszProgramName = g_pszProgramName; pii.pszProgramPath = g_pszProgramPath;
hr = CreateProgramItem(&pii);
break;
case GRPOP_DEL: pii.pszProgramGroup = szFolderPath; pii.pszProgramName = g_pszProgramName;
hr = DeleteProgramItem(&pii);
break;
case GRPOP_SETUP: hr = Setup(hInstance, g_pszProgramName, g_pszFriendlyName, g_fQuietInstall); break;
default: ERROR_OUT(("No operation type specified")); hr = E_INVALIDARG;
break; } } else if (!fErrorReported) { ERROR_OUT(("GetFolderPathname returns %lu", hr)); fErrorReported = TRUE; }
// Process cleanup
CoUninitialize(); DBG_CHECK_MEMORY_TRACKING(hInstance); DBGDEINIT(&ghZone);
ExitProcess(SUCCEEDED(hr) ? 0 : 2); }
|