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.
1010 lines
29 KiB
1010 lines
29 KiB
//*************************************************************
|
|
//
|
|
// Copyright (c) Microsoft Corporation 1998-2000
|
|
// All rights reserved
|
|
//
|
|
// apis.cxx
|
|
//
|
|
//*************************************************************
|
|
|
|
#include "appmgmt.hxx"
|
|
|
|
static CLoadMsi * gLoadMsi = 0;
|
|
static WCHAR * gpwszWinsta = 0;
|
|
|
|
static DWORD ReportInstallStatus(
|
|
PINSTALLCONTEXT pInstallContext,
|
|
DWORD InstallStatus,
|
|
BOOL bUninstall,
|
|
WCHAR * pwszDeploymentName,
|
|
WCHAR * pwszGPOName,
|
|
WCHAR * pwszDeploymentId
|
|
);
|
|
|
|
DWORD WINAPI
|
|
InstallApplication(
|
|
IN PINSTALLDATA pInstallData
|
|
)
|
|
{
|
|
APPKEY AppKey;
|
|
HINSTANCE hMsi;
|
|
MSICONFIGUREPRODUCTEXW * pfnConfigureProduct;
|
|
PINSTALLCONTEXT pInstallContext;
|
|
APPLICATION_INFO InstallInfo;
|
|
UNINSTALL_APPS UninstallApps;
|
|
DWORD InstallUILevel;
|
|
INSTALLUILEVEL OldUILevel;
|
|
DWORD UninstallCount;
|
|
DWORD n;
|
|
DWORD ExtraStatus;
|
|
DWORD Status;
|
|
BOOL bStatus;
|
|
boolean bInstall;
|
|
|
|
Status = Bind();
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
return Status;
|
|
|
|
if ( DebugLevelOn(DM_VERBOSE) )
|
|
{
|
|
WCHAR * pwszCommandLine;
|
|
WCHAR UserName[32];
|
|
DWORD NameLength;
|
|
|
|
NameLength = sizeof(UserName) / sizeof(WCHAR);
|
|
UserName[0] = 0;
|
|
|
|
GetUserName( UserName, &NameLength );
|
|
|
|
pwszCommandLine = GetCommandLine();
|
|
if ( ! pwszCommandLine )
|
|
pwszCommandLine = L"";
|
|
|
|
DebugMsg((DM_VERBOSE, IDS_INSTALL_REQUEST, pwszCommandLine, UserName));
|
|
}
|
|
|
|
AppKey.Type = pInstallData->Type;
|
|
AppKey.ProcessorArchitecture = DEFAULT_ARCHITECTURE;
|
|
|
|
switch ( pInstallData->Type )
|
|
{
|
|
case APPNAME :
|
|
AppKey.uType.AppName.Name = pInstallData->Spec.AppName.Name;
|
|
memcpy( &AppKey.uType.AppName.PolicyId, &pInstallData->Spec.AppName.GPOId, sizeof(GUID) );
|
|
break;
|
|
case FILEEXT :
|
|
AppKey.uType.FileExt = pInstallData->Spec.FileExt;
|
|
break;
|
|
case PROGID :
|
|
AppKey.uType.ProgId = pInstallData->Spec.ProgId;
|
|
break;
|
|
case COMCLASS :
|
|
AppKey.uType.COMClass.Clsid = pInstallData->Spec.COMClass.Clsid;
|
|
AppKey.uType.COMClass.ClsCtx = pInstallData->Spec.COMClass.ClsCtx;
|
|
|
|
break;
|
|
default :
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
pInstallContext = 0;
|
|
memset( &InstallInfo, 0, sizeof(InstallInfo) );
|
|
memset( &UninstallApps, 0, sizeof(UninstallApps) );
|
|
|
|
Status = InstallBegin(
|
|
ghRpc,
|
|
&AppKey,
|
|
&pInstallContext,
|
|
&InstallInfo,
|
|
&UninstallApps );
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
return Status;
|
|
|
|
CLoadMsi LoadMsi( Status );
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
goto InstallApplicationExit;
|
|
|
|
if ( InstallInfo.pwszSetupCommand )
|
|
{
|
|
HINSTANCE hShell32;
|
|
SHELLEXECUTEEXW * pfnShellExecuteEx;
|
|
SHELLEXECUTEINFO ShellExInfo;
|
|
WCHAR * pwszCommand;
|
|
WCHAR * pwszParams;
|
|
WCHAR * pwszDirectory;
|
|
|
|
pwszDirectory = NULL;
|
|
|
|
pfnShellExecuteEx = 0;
|
|
hShell32 = LoadLibrary( L"shell32.dll" );
|
|
|
|
if ( hShell32 )
|
|
pfnShellExecuteEx = (SHELLEXECUTEEXW *) GetProcAddress( hShell32, "ShellExecuteExW" );
|
|
|
|
if ( ! pfnShellExecuteEx )
|
|
Status = GetLastError();
|
|
|
|
if ( ERROR_SUCCESS == Status )
|
|
{
|
|
pwszCommand = InstallInfo.pwszSetupCommand;
|
|
|
|
if ( L'"' == pwszCommand[0] )
|
|
{
|
|
pwszParams = wcschr( &pwszCommand[1], L'"' );
|
|
if ( pwszParams )
|
|
pwszCommand++;
|
|
}
|
|
else
|
|
{
|
|
pwszParams = wcschr( pwszCommand, L' ' );
|
|
}
|
|
|
|
if ( pwszParams )
|
|
{
|
|
*pwszParams++ = 0;
|
|
while ( L' ' == *pwszParams )
|
|
pwszParams++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Need to set the current directory to that
|
|
// containing the actual executable
|
|
//
|
|
if ( ERROR_SUCCESS == Status )
|
|
{
|
|
WCHAR* pwszDirectoryEnd;
|
|
|
|
//
|
|
// Find the last pathsep, which marks the
|
|
// end of the containing directory -- if we
|
|
// don't find it, the admin specified some strange
|
|
// path and we'll just end up passing NULL for
|
|
// the current directory
|
|
//
|
|
pwszDirectoryEnd = wcsrchr( pwszCommand, L'\\' );
|
|
|
|
if ( pwszDirectoryEnd )
|
|
{
|
|
//
|
|
// Get a copy of the full path that we can
|
|
// truncate to the containing directory
|
|
//
|
|
pwszDirectory = StringDuplicate( pwszCommand );
|
|
|
|
if ( pwszDirectory )
|
|
{
|
|
//
|
|
// Truncate to the containing directory
|
|
//
|
|
pwszDirectory[ pwszDirectoryEnd - pwszCommand ] = L'\0';
|
|
}
|
|
else
|
|
{
|
|
Status = ERROR_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( ERROR_SUCCESS == Status )
|
|
{
|
|
ShellExInfo.cbSize = sizeof( ShellExInfo );
|
|
ShellExInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
|
|
ShellExInfo.hwnd = NULL;
|
|
ShellExInfo.lpVerb = NULL;
|
|
ShellExInfo.lpFile = pwszCommand;
|
|
ShellExInfo.lpParameters = pwszParams;
|
|
ShellExInfo.lpDirectory = pwszDirectory;
|
|
ShellExInfo.nShow = SW_SHOWNORMAL;
|
|
ShellExInfo.hProcess = 0;
|
|
|
|
DebugMsg((DM_VERBOSE, IDS_LEGACY_INSTALL, pwszCommand));
|
|
|
|
bStatus = (*pfnShellExecuteEx)( &ShellExInfo );
|
|
|
|
if ( ! bStatus )
|
|
Status = GetLastError();
|
|
}
|
|
|
|
delete [] pwszDirectory;
|
|
|
|
if ( hShell32 )
|
|
FreeLibrary( hShell32 );
|
|
|
|
if ( (ERROR_SUCCESS == Status) && ShellExInfo.hProcess )
|
|
{
|
|
Status = WAIT_OBJECT_0;
|
|
|
|
if ( LoadUser32Funcs() )
|
|
{
|
|
MSG msg;
|
|
HANDLE Handles[2];
|
|
|
|
Handles[0] = ShellExInfo.hProcess;
|
|
|
|
for (;;)
|
|
{
|
|
Status = (*pfnMsgWaitForMultipleObjects)( 1, Handles, FALSE, INFINITE, QS_ALLINPUT );
|
|
|
|
if ( (WAIT_OBJECT_0+1) == Status )
|
|
{
|
|
if ( (*pfnPeekMessageW)( &msg, NULL, 0, 0, PM_REMOVE) )
|
|
{
|
|
(*pfnTranslateMessage)( &msg );
|
|
(*pfnDispatchMessageW)( &msg );
|
|
}
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
CloseHandle( ShellExInfo.hProcess );
|
|
|
|
if ( WAIT_OBJECT_0 == Status )
|
|
Status = ERROR_SUCCESS;
|
|
else
|
|
Status = GetLastError();
|
|
}
|
|
|
|
gpEvents->ZAPInstall(
|
|
Status,
|
|
InstallInfo.pwszDeploymentName,
|
|
InstallInfo.pwszGPOName,
|
|
pwszCommand );
|
|
|
|
goto InstallApplicationExit;
|
|
}
|
|
|
|
OldUILevel = (*gpfnMsiSetInternalUI)( INSTALLUILEVEL_NOCHANGE, NULL );
|
|
|
|
if ( UninstallApps.Products > 0 )
|
|
(*gpfnMsiSetInternalUI)( INSTALLUILEVEL_BASIC, NULL );
|
|
|
|
for ( UninstallCount = 0; UninstallCount < UninstallApps.Products; )
|
|
{
|
|
if ( UninstallApps.ApplicationInfo[UninstallCount].Flags & APPINFOFLAG_UNINSTALL )
|
|
{
|
|
DebugMsg((DM_VERBOSE, IDS_UNINSTALL, UninstallApps.ApplicationInfo[UninstallCount].pwszDeploymentName, UninstallApps.ApplicationInfo[UninstallCount].pwszGPOName));
|
|
|
|
//
|
|
// The MsiQueryProductState could fail due to out of memory -- it could
|
|
// return INSTALL_STATE_UNKNOWN in out of memory situations, and we would incorrectly
|
|
// unmanage the application -- so we use MsiGetProductInfo which returns a status
|
|
// that allows us to distinguish these cases
|
|
//
|
|
Status = gpfnMsiGetProductInfo(
|
|
UninstallApps.ApplicationInfo[UninstallCount].pwszProductCode,
|
|
INSTALLPROPERTY_PACKAGECODE,
|
|
NULL,
|
|
NULL);
|
|
|
|
//
|
|
// If the product is installed, try to uninstall. If it exists but has
|
|
// bad configuration data, attempt the uninstall anyway in the hope that
|
|
// it will get removed
|
|
//
|
|
if ( ( ERROR_SUCCESS == Status ) ||
|
|
( ERROR_BAD_CONFIGURATION == Status ) )
|
|
{
|
|
//
|
|
// ERROR_SUCCESS_REBOOT_INITIATED here will be treated as an error
|
|
// since we don't want to attempt to do an install if the machine
|
|
// is being rebooted.
|
|
// The two REJECTED errors can be encountered when the application is
|
|
// only advertised and is prevented from (un)installing by policy.
|
|
//
|
|
Status = (*gpfnMsiConfigureProductEx)( UninstallApps.ApplicationInfo[UninstallCount].pwszProductCode, INSTALLLEVEL_MAXIMUM, INSTALLSTATE_ABSENT, NULL );
|
|
|
|
if ( ERROR_SUCCESS_REBOOT_REQUIRED == Status )
|
|
{
|
|
Status = ERROR_SUCCESS;
|
|
}
|
|
else if ( (ERROR_INSTALL_PACKAGE_REJECTED == Status) ||
|
|
(ERROR_INSTALL_TRANSFORM_REJECTED == Status) )
|
|
{
|
|
Status = InstallUnmanageApp( pInstallContext, UninstallApps.ApplicationInfo[UninstallCount].pwszDeploymentId, TRUE );
|
|
}
|
|
}
|
|
else if ( ERROR_UNKNOWN_PRODUCT == Status )
|
|
{
|
|
//
|
|
// If we didn't find the product, treat this as a successful removal
|
|
// since the desired state, absence of the app, is achieved.
|
|
//
|
|
Status = ERROR_SUCCESS;
|
|
}
|
|
|
|
ReportInstallStatus(
|
|
pInstallContext,
|
|
Status,
|
|
TRUE,
|
|
UninstallApps.ApplicationInfo[UninstallCount].pwszDeploymentName,
|
|
UninstallApps.ApplicationInfo[UninstallCount].pwszGPOName,
|
|
UninstallApps.ApplicationInfo[UninstallCount].pwszDeploymentId);
|
|
}
|
|
|
|
// Nothing to do here for an orphaned app.
|
|
|
|
if ( ERROR_SUCCESS == Status )
|
|
UninstallCount++;
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
goto InstallApplicationRollback;
|
|
}
|
|
|
|
Status = InstallManageApp( pInstallContext, InstallInfo.pwszDeploymentId, ERROR_SUCCESS, &bInstall );
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
goto InstallApplicationRollback;
|
|
|
|
if ( InstallInfo.Flags & APPINFOFLAG_FULLUI )
|
|
InstallUILevel = INSTALLUILEVEL_FULL;
|
|
else // InstallInfo.Flags & APPINFOFLAG_BASICUI
|
|
InstallUILevel = INSTALLUILEVEL_BASIC;
|
|
|
|
if ( (APPNAME == pInstallData->Type) && (InstallInfo.Flags & APPINFOFLAG_BASICUI) )
|
|
InstallUILevel |= INSTALLUILEVEL_ENDDIALOG;
|
|
|
|
(void) (*gpfnMsiSetInternalUI)( (INSTALLUILEVEL) InstallUILevel, NULL );
|
|
|
|
if ( InstallInfo.pwszDescriptor )
|
|
{
|
|
DebugMsg((DM_VERBOSE, IDS_INSTALL_DESC, InstallInfo.pwszDeploymentName, InstallInfo.pwszGPOName));
|
|
Status = (*gpfnMsiProvideComponentFromDescriptor)( InstallInfo.pwszDescriptor, NULL, NULL, NULL );
|
|
REMAP_DARWIN_STATUS( Status );
|
|
|
|
ReportInstallStatus(
|
|
pInstallContext,
|
|
Status,
|
|
FALSE,
|
|
InstallInfo.pwszDeploymentName,
|
|
InstallInfo.pwszGPOName,
|
|
InstallInfo.pwszDeploymentId);
|
|
}
|
|
else
|
|
{
|
|
DebugMsg((DM_VERBOSE, IDS_INSTALL_PC, InstallInfo.pwszDeploymentName, InstallInfo.pwszGPOName));
|
|
Status = (*gpfnMsiConfigureProductEx)( InstallInfo.pwszProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, NULL );
|
|
REMAP_DARWIN_STATUS( Status );
|
|
|
|
ReportInstallStatus(
|
|
pInstallContext,
|
|
Status,
|
|
FALSE,
|
|
InstallInfo.pwszDeploymentName,
|
|
InstallInfo.pwszGPOName,
|
|
InstallInfo.pwszDeploymentId);
|
|
}
|
|
|
|
(void) (*gpfnMsiSetInternalUI)( OldUILevel, NULL );
|
|
|
|
//
|
|
// If the added app was already managed, any error must be ignored, we don't want to
|
|
// unmanage it in this case. Darwin rollback & repair should handle these cases.
|
|
//
|
|
if ( (Status != ERROR_SUCCESS) && (InstallInfo.Flags & APPINFOFLAG_ALREADYMANAGED) )
|
|
goto InstallApplicationExit;
|
|
|
|
if ( ERROR_SUCCESS == Status )
|
|
{
|
|
//
|
|
// Now we can finally unmanage any upgraded apps.
|
|
// If these fail, it will be fixed up in a subsequent run of policy.
|
|
//
|
|
for ( n = 0; n < UninstallCount; n++ )
|
|
ExtraStatus = InstallUnmanageApp( pInstallContext, UninstallApps.ApplicationInfo[n].pwszDeploymentId, FALSE );
|
|
|
|
goto InstallApplicationExit;
|
|
}
|
|
|
|
//
|
|
// Failed to install the new app, unmanage it and fall through to our rollback.
|
|
//
|
|
|
|
ExtraStatus = InstallUnmanageApp( pInstallContext, InstallInfo.pwszDeploymentId, FALSE );
|
|
|
|
InstallApplicationRollback:
|
|
|
|
for ( n = 0; n < UninstallCount; n++ )
|
|
{
|
|
bInstall = FALSE;
|
|
ExtraStatus = InstallManageApp( pInstallContext, UninstallApps.ApplicationInfo[n].pwszDeploymentId, Status, &bInstall );
|
|
if ( (ERROR_SUCCESS == ExtraStatus) && bInstall )
|
|
{
|
|
(void) (*gpfnMsiConfigureProductEx)( UninstallApps.ApplicationInfo[n].pwszProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, NULL );
|
|
gpEvents->Install( Status, UninstallApps.ApplicationInfo[n].pwszDeploymentName, UninstallApps.ApplicationInfo[n].pwszGPOName );
|
|
}
|
|
}
|
|
|
|
InstallApplicationExit:
|
|
|
|
if ( pInstallContext )
|
|
InstallEnd( (Status == ERROR_SUCCESS), &pInstallContext );
|
|
|
|
FreeApplicationInfo( &InstallInfo );
|
|
|
|
while ( UninstallApps.Products )
|
|
FreeApplicationInfo( &UninstallApps.ApplicationInfo[--UninstallApps.Products] );
|
|
LocalFree( UninstallApps.ApplicationInfo );
|
|
|
|
return Status;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
UninstallApplication(
|
|
IN WCHAR * ProductCode,
|
|
IN DWORD dwStatus
|
|
)
|
|
{
|
|
DWORD Status;
|
|
|
|
Status = Bind();
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
return Status;
|
|
|
|
return ARPRemoveApp( ghRpc, ProductCode, dwStatus );
|
|
}
|
|
|
|
DWORD WINAPI
|
|
CommandLineFromMsiDescriptor(
|
|
IN WCHAR * Descriptor,
|
|
OUT WCHAR * CommandLine,
|
|
IN OUT DWORD * CommandLineLength
|
|
)
|
|
{
|
|
INSTALLUILEVEL NewUILevel;
|
|
INSTALLUILEVEL OldUILevel;
|
|
HKEY hkAppmgmt;
|
|
WCHAR ProductCode[40];
|
|
DWORD Size;
|
|
DWORD ArgStart;
|
|
DWORD Status;
|
|
|
|
Status = ERROR_SUCCESS;
|
|
|
|
//
|
|
// For this api we can not affort to dynamically load/unload msi each time, it
|
|
// is just called too frequently.
|
|
// We'll store the load object in a global rather then a static in case we
|
|
// add support for a call-in mechanism to free this.
|
|
//
|
|
if ( ! gLoadMsi )
|
|
gLoadMsi = new CLoadMsi( Status );
|
|
|
|
if ( gLoadMsi && (Status != ERROR_SUCCESS) )
|
|
{
|
|
delete gLoadMsi;
|
|
gLoadMsi = 0;
|
|
}
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
return Status;
|
|
|
|
if ( ! LoadUser32Funcs() )
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
//
|
|
// We get the process window station in order to detect processes running
|
|
// in non-interactive desktops where Darwin UI is not possible. Darwin
|
|
// itself is not aware of these situations.
|
|
//
|
|
if ( ! gpwszWinsta )
|
|
{
|
|
HWINSTA hWinsta;
|
|
BOOL bStatus;
|
|
|
|
hWinsta = (*pfnGetProcessWindowStation)();
|
|
|
|
if ( hWinsta )
|
|
{
|
|
Size = 0;
|
|
(void) (*pfnGetUserObjectInformationW)( hWinsta, UOI_NAME, NULL, 0, &Size );
|
|
|
|
gpwszWinsta = new WCHAR[Size/2];
|
|
if ( gpwszWinsta )
|
|
{
|
|
bStatus = (*pfnGetUserObjectInformationW)( hWinsta, UOI_NAME, gpwszWinsta, Size, &Size );
|
|
if ( ! bStatus )
|
|
{
|
|
Status = GetLastError();
|
|
delete [] gpwszWinsta;
|
|
gpwszWinsta = 0;
|
|
}
|
|
}
|
|
else
|
|
Status = ERROR_OUTOFMEMORY;
|
|
|
|
(*pfnCloseWindowStation)( hWinsta );
|
|
}
|
|
|
|
//
|
|
// Since this code was added very late (just before nt5 rc3) we don't
|
|
// treat an error in getting the window station handle as a failure in
|
|
// this API. Too many unknowns with the security configuration of
|
|
// services. We'll treat this case as a non-winsta0 process.
|
|
//
|
|
}
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
return Status;
|
|
|
|
//
|
|
// If we can't grab the winsta handle or can confirm that this is a
|
|
// non-winsta0 process, we fix the ui level to none.
|
|
//
|
|
if ( (0 == gpwszWinsta) || (lstrcmp( L"WinSta0", gpwszWinsta ) != 0) )
|
|
(*gpfnMsiSetInternalUI)( INSTALLUILEVEL_NONE, NULL );
|
|
|
|
OldUILevel = (*gpfnMsiSetInternalUI)( INSTALLUILEVEL_NOCHANGE, NULL );
|
|
|
|
//
|
|
// Some processes, like the dcom service, may not be able to handle UI
|
|
// of any kind. So if the UI level is currently set to none we never
|
|
// change it.
|
|
//
|
|
if ( OldUILevel != INSTALLUILEVEL_NONE )
|
|
{
|
|
Status = (*gpfnMsiDecomposeDescriptor)(
|
|
Descriptor,
|
|
ProductCode,
|
|
NULL,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
return Status;
|
|
|
|
Status = RegOpenKeyEx(
|
|
HKEY_CURRENT_USER,
|
|
APPMGMTKEY,
|
|
0,
|
|
KEY_READ,
|
|
&hkAppmgmt );
|
|
|
|
if ( ERROR_SUCCESS == Status )
|
|
{
|
|
Size = sizeof( NewUILevel );
|
|
|
|
Status = RegQueryValueEx(
|
|
hkAppmgmt,
|
|
ProductCode,
|
|
NULL,
|
|
NULL,
|
|
(PBYTE) &NewUILevel,
|
|
&Size );
|
|
|
|
RegCloseKey( hkAppmgmt );
|
|
}
|
|
|
|
if ( ERROR_SUCCESS == Status )
|
|
(*gpfnMsiSetInternalUI)( NewUILevel, NULL );
|
|
}
|
|
|
|
//
|
|
// Returns a quoted exe path with appended args. The forth parameter
|
|
// is obsolete and returns no usefull value.
|
|
//
|
|
Status = (*gpfnMsiProvideComponentFromDescriptor)(
|
|
Descriptor,
|
|
CommandLine,
|
|
CommandLineLength,
|
|
&ArgStart );
|
|
|
|
REMAP_DARWIN_STATUS( Status );
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
DebugMsg((DM_VERBOSE, IDS_DESC_FAIL, Descriptor, Status));
|
|
|
|
(*gpfnMsiSetInternalUI)( OldUILevel, NULL );
|
|
|
|
return Status;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
GetLocalManagedApplications(
|
|
IN BOOL bUserApps,
|
|
OUT LPDWORD pdwApps,
|
|
OUT PLOCALMANAGEDAPPLICATION * prgLocalApps
|
|
)
|
|
|
|
{
|
|
PLOCALMANAGEDAPPLICATION pLocalApps;
|
|
HKEY hkRoot;
|
|
HKEY hkAppmgmt;
|
|
HKEY hkApp;
|
|
WCHAR wszDeploymentId[44];
|
|
DWORD Size;
|
|
DWORD Index;
|
|
DWORD Apps;
|
|
DWORD Status;
|
|
|
|
*pdwApps = 0;
|
|
*prgLocalApps = 0;
|
|
|
|
Status = ERROR_SUCCESS;
|
|
|
|
if ( bUserApps )
|
|
Status = RegOpenCurrentUser( KEY_READ, &hkRoot );
|
|
else
|
|
hkRoot = HKEY_LOCAL_MACHINE;
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
return Status;
|
|
|
|
Status = RegOpenKeyEx(
|
|
hkRoot,
|
|
APPMGMTKEY,
|
|
0,
|
|
KEY_READ,
|
|
&hkAppmgmt );
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
return Status;
|
|
|
|
Status = RegQueryInfoKey (
|
|
hkAppmgmt,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&Apps,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( ERROR_SUCCESS == Status )
|
|
{
|
|
pLocalApps = (PLOCALMANAGEDAPPLICATION) LocalAlloc( LMEM_ZEROINIT, Apps * sizeof(LOCALMANAGEDAPPLICATION) );
|
|
if ( ! pLocalApps )
|
|
Status = ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
{
|
|
RegCloseKey( hkAppmgmt );
|
|
if ( bUserApps )
|
|
RegCloseKey( hkRoot );
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
for ( Index = 0; Index < Apps; Index++ )
|
|
{
|
|
Status = RegEnumKey(
|
|
hkAppmgmt,
|
|
Index,
|
|
wszDeploymentId,
|
|
sizeof(wszDeploymentId) / sizeof(WCHAR) );
|
|
|
|
if ( ERROR_SUCCESS == Status )
|
|
{
|
|
Status = RegOpenKeyEx(
|
|
hkAppmgmt,
|
|
wszDeploymentId,
|
|
0,
|
|
KEY_READ,
|
|
&hkApp );
|
|
}
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
break;
|
|
|
|
Size = sizeof(DWORD);
|
|
|
|
Status = RegQueryValueEx(
|
|
hkApp,
|
|
APPSTATEVALUE,
|
|
0,
|
|
NULL,
|
|
(LPBYTE) &pLocalApps[Index].dwState,
|
|
&Size );
|
|
|
|
if ( ERROR_SUCCESS == Status )
|
|
Status = ReadStringValue( hkApp, DEPLOYMENTNAMEVALUE, &pLocalApps[Index].pszDeploymentName );
|
|
|
|
if ( ERROR_SUCCESS == Status )
|
|
Status = ReadStringValue( hkApp, GPONAMEVALUE, &pLocalApps[Index].pszPolicyName );
|
|
|
|
if ( ERROR_SUCCESS == Status )
|
|
Status = ReadStringValue( hkApp, PRODUCTIDVALUE, &pLocalApps[Index].pszProductId );
|
|
|
|
RegCloseKey( hkApp );
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
break;
|
|
}
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
{
|
|
for ( Index = 0; Index < Apps; Index++ )
|
|
{
|
|
LocalFree( pLocalApps[Index].pszDeploymentName );
|
|
LocalFree( pLocalApps[Index].pszPolicyName );
|
|
LocalFree( pLocalApps[Index].pszProductId );
|
|
}
|
|
LocalFree( pLocalApps );
|
|
}
|
|
else
|
|
{
|
|
*prgLocalApps = pLocalApps;
|
|
*pdwApps = Apps;
|
|
}
|
|
|
|
RegCloseKey( hkAppmgmt );
|
|
if ( bUserApps )
|
|
RegCloseKey( hkRoot );
|
|
|
|
return Status;
|
|
}
|
|
|
|
void WINAPI
|
|
GetLocalManagedApplicationData(
|
|
WCHAR * ProductCode,
|
|
LPWSTR * DisplayName,
|
|
LPWSTR * SupportUrl
|
|
)
|
|
{
|
|
HKEY hkRoot;
|
|
HKEY hkAppmgmt;
|
|
HKEY hkApp;
|
|
WCHAR wszDeploymentId[44];
|
|
WCHAR wszProductId[40];
|
|
DWORD Index;
|
|
DWORD Size;
|
|
DWORD Status;
|
|
BOOL bUser;
|
|
|
|
*DisplayName = 0;
|
|
*SupportUrl = 0;
|
|
|
|
for ( int i = 0; i < 2; i++ )
|
|
{
|
|
Status = ERROR_SUCCESS;
|
|
bUser = (0 == i);
|
|
|
|
if ( bUser )
|
|
Status = RegOpenCurrentUser( KEY_READ, &hkRoot );
|
|
else
|
|
hkRoot = HKEY_LOCAL_MACHINE;
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
break;
|
|
|
|
hkAppmgmt = 0;
|
|
|
|
Status = RegOpenKeyEx(
|
|
hkRoot,
|
|
APPMGMTKEY,
|
|
0,
|
|
KEY_READ,
|
|
&hkAppmgmt );
|
|
|
|
if ( bUser )
|
|
RegCloseKey( hkRoot );
|
|
|
|
if ( ERROR_SUCCESS == Status )
|
|
{
|
|
DWORD InstallUI;
|
|
|
|
Size = sizeof(InstallUI);
|
|
|
|
// This is the hint that we will use to determine if this product is managed.
|
|
Status = RegQueryValueEx(
|
|
hkAppmgmt,
|
|
ProductCode,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE) &InstallUI,
|
|
&Size );
|
|
}
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
{
|
|
if ( hkAppmgmt )
|
|
RegCloseKey( hkAppmgmt );
|
|
continue;
|
|
}
|
|
|
|
for ( Index = 0 ;; Index++ )
|
|
{
|
|
Status = RegEnumKey(
|
|
hkAppmgmt,
|
|
Index,
|
|
wszDeploymentId,
|
|
sizeof(wszDeploymentId) / sizeof(WCHAR) );
|
|
|
|
if ( ERROR_SUCCESS == Status )
|
|
{
|
|
Status = RegOpenKeyEx(
|
|
hkAppmgmt,
|
|
wszDeploymentId,
|
|
0,
|
|
KEY_READ,
|
|
&hkApp );
|
|
}
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
break;
|
|
|
|
Size = sizeof(wszProductId);
|
|
wszProductId[0] = 0;
|
|
(void) RegQueryValueEx( hkApp, PRODUCTIDVALUE, NULL, NULL, (LPBYTE) wszProductId, &Size );
|
|
|
|
if ( lstrcmpi( ProductCode, wszProductId ) != 0 )
|
|
{
|
|
RegCloseKey( hkApp );
|
|
continue;
|
|
}
|
|
|
|
(void) ReadStringValue( hkApp, DEPLOYMENTNAMEVALUE, DisplayName );
|
|
(void) ReadStringValue( hkApp, SUPPORTURL, SupportUrl );
|
|
|
|
RegCloseKey( hkApp );
|
|
break;
|
|
}
|
|
|
|
RegCloseKey( hkAppmgmt );
|
|
|
|
if ( *DisplayName || *SupportUrl )
|
|
break;
|
|
}
|
|
}
|
|
|
|
DWORD WINAPI
|
|
GetManagedApplications(
|
|
IN GUID* pCategory,
|
|
IN DWORD dwQueryFlags,
|
|
IN DWORD dwInfoLevel,
|
|
OUT LPDWORD pdwApps,
|
|
OUT PMANAGEDAPPLICATION* prgManagedApps)
|
|
{
|
|
LONG Status;
|
|
MANAGED_APPLIST AppList;
|
|
|
|
//
|
|
// Initialize the out parameters
|
|
//
|
|
if (pdwApps) {
|
|
*pdwApps = NULL;
|
|
}
|
|
|
|
if (prgManagedApps) {
|
|
*prgManagedApps = NULL;
|
|
}
|
|
|
|
//
|
|
// Validate caller parameters that aren't passed to the
|
|
// rpc interface -- other parameters will be validated
|
|
// by the server
|
|
//
|
|
if (!pdwApps || !prgManagedApps) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = Bind();
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
return Status;
|
|
|
|
//
|
|
// Initialize our stack variables
|
|
//
|
|
memset(&AppList, 0, sizeof(AppList));
|
|
|
|
//
|
|
// Call the method on the server.
|
|
//
|
|
Status = GetManagedApps( ghRpc, pCategory, dwQueryFlags, dwInfoLevel, &AppList);
|
|
|
|
//
|
|
// On success, set the caller's out parameters to refer
|
|
// to the results returned by the server
|
|
//
|
|
if ( ERROR_SUCCESS == Status)
|
|
{
|
|
*pdwApps = AppList.Applications;
|
|
*prgManagedApps = (PMANAGEDAPPLICATION) AppList.rgApps;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
static DWORD
|
|
ReportInstallStatus(
|
|
PINSTALLCONTEXT pInstallContext,
|
|
DWORD InstallStatus,
|
|
BOOL bUninstall,
|
|
WCHAR * pwszDeploymentName,
|
|
WCHAR * pwszGPOName,
|
|
WCHAR * pwszDeploymentId
|
|
)
|
|
{
|
|
DWORD Status;
|
|
DWORD dwEventId;
|
|
|
|
Status = ERROR_SUCCESS;
|
|
|
|
//
|
|
// First, report the correct event based on whether
|
|
// this is an install or an uninstall
|
|
//
|
|
if ( ! bUninstall )
|
|
{
|
|
gpEvents->Install(
|
|
InstallStatus,
|
|
pwszDeploymentName,
|
|
pwszGPOName
|
|
);
|
|
|
|
( ERROR_SUCCESS == InstallStatus ) ?
|
|
( dwEventId = EVENT_APPMGMT_INSTALL ) : ( dwEventId = EVENT_APPMGMT_INSTALL_FAILED );
|
|
}
|
|
else
|
|
{
|
|
gpEvents->Uninstall(
|
|
InstallStatus,
|
|
pwszDeploymentName,
|
|
pwszGPOName
|
|
);
|
|
|
|
( ERROR_SUCCESS == InstallStatus ) ?
|
|
( dwEventId = EVENT_APPMGMT_UNINSTALL ) : ( dwEventId = EVENT_APPMGMT_UNINSTALL_FAILED );
|
|
}
|
|
|
|
//
|
|
// If there was an error, log a failure status
|
|
//
|
|
if ( ERROR_SUCCESS != InstallStatus )
|
|
{
|
|
Status = RsopReportInstallFailure(
|
|
pInstallContext,
|
|
pwszDeploymentId,
|
|
dwEventId);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
DWORD WINAPI
|
|
GetManagedApplicationCategories(
|
|
DWORD dwReserved,
|
|
APPCATEGORYINFOLIST* pAppCategory
|
|
)
|
|
{
|
|
DWORD Status;
|
|
APPCATEGORYLIST CategoryList;
|
|
|
|
if ( ( 0 != dwReserved ) ||
|
|
! pAppCategory )
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
memset( &CategoryList, 0, sizeof( CategoryList ) );
|
|
|
|
Status = Bind();
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
return Status;
|
|
|
|
Status = GetManagedAppCategories(
|
|
ghRpc,
|
|
&CategoryList);
|
|
|
|
*pAppCategory = *( ( APPCATEGORYINFOLIST* ) &CategoryList );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
|