windows-nt/Source/XPSP1/NT/enduser/netmeeting/setup/pgmgrp/pgmgrp.cpp

1000 lines
21 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*
* 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);
}