windows-nt/Source/XPSP1/NT/shell/cpls/appwzdui/cctl.cpp
2020-09-26 16:20:57 +08:00

592 lines
15 KiB
C++

// CCtl.CPP
//
// Implementation of the control
//
// Created 17-Apr-98 [JonT]
#include "priv.h"
#include "resource.h"
#include "cctl.h"
#include "util.h"
#include "dump.h" // for Dbg_* functions
//--------------------------------------------------------------------
//
//
// CARPCtl class
//
//
//--------------------------------------------------------------------
// ARPCtlEnumCB
// Callback for EnumAppItems that adds the appdata item to
// the array.
void CALLBACK ARPCtlEnumCB(CAppData * pcad, LPARAM lParam)
{
((CARPCtl *)lParam)->EnumCallback(pcad);
}
//--------------------------------------------------------------------
// Methods
// CARPCtl::EnumCallback
// EnumAppItems callback for the ARPCtl.
void CARPCtl::EnumCallback(CAppData * pcad)
{
switch (_dwEnum)
{
case ENUM_INSTALLED:
case ENUM_PUBLISHED:
case ENUM_OCSETUP:
if (S_OK == _pmtxarray->AddItem(pcad, NULL))
{
_dwcItems++;
TraceMsg(TF_CTL, "ARP: Added item \"%s\" to list", pcad->GetData()->pszDisplayName);
// Fire the event saying the new row is ready.
// Note that this has to be AFTER we increment so that
// the ItemCount property will be correct at this point.
Fire_OnRowReady(_dwcItems - 1);
}
break;
case ENUM_CATEGORIES:
if (S_OK == _pmtxarray->AddItem(pcad, NULL))
{
_dwcItems++;
TraceMsg(TF_CTL, "ARP: Added item \"%s\" to list", pcad->GetCategory());
// Fire the event saying the new row is ready.
// Note that this has to be AFTER we increment so that
// the ItemCount property will be correct at this point.
Fire_OnRowReady(_dwcItems - 1);
}
break;
}
}
// CARPCtl::InitData
// Script tells the control to get data from the app management object
// and which order to sort in.
//
// bstrEnum can be:
// "installed" - enumerate currently installed apps.
// "" - ditto.
// "published" - enumerate published apps.
// "categories" - enumerate published categories.
STDMETHODIMP
CARPCtl::InitData(
BSTR bstrEnum,
DWORD dwSortOrder
)
{
HRESULT hres = S_FALSE;
// Determine what we're enumerating
_dwEnum = ENUM_INSTALLED; // default value
if (0 == lstrcmpiW(bstrEnum, L"published"))
_dwEnum = ENUM_PUBLISHED;
else if (0 == lstrcmpiW(bstrEnum, L"categories"))
_dwEnum = ENUM_CATEGORIES;
else if (0 == lstrcmpiW(bstrEnum, L"ocsetup"))
_dwEnum = ENUM_OCSETUP;
// Load the app manager if we haven't already
if (_pam == NULL)
{
if (FAILED(CoCreateInstance(CLSID_ShellAppManager, NULL,
CLSCTX_INPROC, IID_IShellAppManager, (void**)&_pam)))
{
TraceMsg(TF_ERROR, "Couldn't instantiate ShellAppManager object");
return S_FALSE;
}
}
// Make sure the worker thread isn't already running. Stop it if it is.
_workerthread.Kill();
// If we already have a list, nuke it
_FreeAppData();
// Now create the new list
_pmtxarray = new CMtxArray2(_dwEnum);
if (_pmtxarray)
{
// Start enumerating items
hres = EnumAppItems(_dwEnum, _pam, ARPCtlEnumCB, (LPARAM)this);
// Did the enumeration succeed?
if (SUCCEEDED(hres))
{
// Yes; tell the script we're done getting the synchronous data
// This call is synchronous and does not return until the script
// is finished responding. This seems like a bug in Trident.
Fire_OnSyncDataReady();
// We only get slow info for the installed apps
if (ENUM_INSTALLED == _dwEnum)
{
// Create and kick off the worker thread
hres = _workerthread.Initialize(SAFECAST(this, IWorkerEvent *), _pmtxarray);
}
}
}
// Can't return failure to script
if (FAILED(hres))
hres = S_FALSE;
return hres;
}
// CARPCtl::MoveFirst
// Script tells control to move to the first item in the list.
// Returns false if there are no items.
STDMETHODIMP
CARPCtl::MoveFirst(
BOOL* pbool
)
{
// Set our current index to the start
_dwCurrentIndex = 0;
// Return TRUE iff we are pointing to a valid item
*pbool = _dwCurrentIndex >= _dwcItems ? FALSE : TRUE;
return S_OK;
}
// CARPCtl::MoveNext
// Script tells control to move to the next item in the curren tlist.
// Returns false if off the end of the list.
STDMETHODIMP
CARPCtl::MoveNext(
BOOL* pbool
)
{
_dwCurrentIndex++;
// Return TRUE iff we are pointing to a valid item
*pbool = _dwCurrentIndex >= _dwcItems ? FALSE : TRUE;
return S_OK;
}
// CARPCtl::MoveTo
// Tells the control to move to a specific item
STDMETHODIMP
CARPCtl::MoveTo(
DWORD dwRecNum,
BOOL* pbool
)
{
// If they want to go past the end, fail it and don't move the pointer
if (dwRecNum >= _dwcItems)
{
*pbool = FALSE;
return S_OK;
}
// Update the pointer and return success
_dwCurrentIndex = dwRecNum;
*pbool = TRUE;
return S_OK;
}
// CARPCtl::Exec
// Tells the control to exec a command. The command may act
// upon the current record.
STDMETHODIMP
CARPCtl::Exec(
BSTR bstrCmd
)
{
TraceMsg(TF_CTL, "(Ctl) Command (%ls) called", bstrCmd);
#ifdef NOTYET
// security check must pass before we could exec anything.
if (!_fSecure)
{
TraceMsg(TF_CTL, "(Ctl) Security blocked Exec call");
return S_FALSE; // scripting methods cannot return failure
}
#endif
const static struct {
LPCWSTR pszCmd;
APPCMD appcmd;
} s_rgCmds[] = {
{ L"install", APPCMD_INSTALL },
{ L"uninstall", APPCMD_UNINSTALL },
{ L"modify", APPCMD_MODIFY },
{ L"upgrade", APPCMD_UPGRADE },
{ L"repair", APPCMD_REPAIR },
{ L"generic install", APPCMD_GENERICINSTALL },
{ L"ntoptions", APPCMD_NTOPTIONS },
{ L"winupdate", APPCMD_WINUPDATE },
};
int i;
APPCMD appcmd = APPCMD_UNKNOWN;
for (i = 0; i < ARRAYSIZE(s_rgCmds); i++)
{
if (0 == StrCmpIW(bstrCmd, s_rgCmds[i].pszCmd))
{
appcmd = s_rgCmds[i].appcmd;
break;
}
}
switch (appcmd)
{
case APPCMD_INSTALL:
case APPCMD_UNINSTALL:
case APPCMD_MODIFY:
case APPCMD_UPGRADE:
case APPCMD_REPAIR:
{
CAppData * pappdata = _GetAppData(_dwCurrentIndex);
if (pappdata)
pappdata->DoCommand(appcmd);
}
break;
case APPCMD_GENERICINSTALL:
InstallAppFromFloppyOrCDROM(NULL);
break;
case APPCMD_NTOPTIONS:
// command to invoke and OCMgr
// "sysocmgr /x /i:%systemroot%\system32\sysoc.inf"
TCHAR szInf[MAX_PATH];
if (GetSystemDirectory(szInf, ARRAYSIZE(szInf)) && PathCombine(szInf, szInf, TEXT("sysoc.inf")))
{
TCHAR szParam[MAX_PATH];
wsprintf(szParam, TEXT("/x /i:%s"), szInf);
ShellExecute(NULL, NULL, TEXT("sysocmgr"), szParam, NULL, SW_SHOWDEFAULT);
}
break;
case APPCMD_WINUPDATE:
break;
case APPCMD_UNKNOWN:
TraceMsg(TF_ERROR, "(Ctl) Received invalid appcmd %ls", bstrCmd);
break;
}
return S_OK;
}
// IWorkerEvent::FireOnDataReady
// Called by worker thread when some data is ready.
STDMETHODIMP
CARPCtl::FireOnDataReady(
LONG iRow
)
{
Fire_OnAsyncDataReady(iRow);
return S_OK;
}
// IWorkerEvent::FireOnFinished
// Called by worker thread when it is complete.
STDMETHODIMP
CARPCtl::FireOnFinished(void)
{
return S_OK;
}
//--------------------------------------------------------------------
// Properties
// SIMPLE_PROPERTY_GET
//
// Defines a simple property get method so that we don't type the same
// code over and over. It only works for strings copied from the APPINFODATA
// structure.
//
// This keeps the code cleaned up. but doesn't help
// with the code bloat, so a better approach would be great.
#define SIMPLE_PROPERTY_GET(PropName) \
STDMETHODIMP \
CARPCtl::get_##PropName##(BSTR* pbstr) \
{ \
USES_CONVERSION; \
\
if (_dwCurrentIndex >= _dwcItems) \
return E_FAIL; \
\
CAppData * pappdata = _GetAppData(_dwCurrentIndex); \
if (pappdata) \
{ \
APPINFODATA * paidata = pappdata->GetData(); \
\
ASSERT(NULL == paidata->psz##PropName || IS_VALID_STRING_PTRW(paidata->psz##PropName, -1)); \
\
*pbstr = W2BSTR(paidata->psz##PropName); \
} \
else \
*pbstr = NULL; \
\
return S_OK; \
}
// TODO: Since this is big code bloat, make sure we really need all these...
SIMPLE_PROPERTY_GET(Version)
SIMPLE_PROPERTY_GET(Publisher)
SIMPLE_PROPERTY_GET(ProductID)
SIMPLE_PROPERTY_GET(RegisteredOwner)
SIMPLE_PROPERTY_GET(Language)
SIMPLE_PROPERTY_GET(SupportUrl)
SIMPLE_PROPERTY_GET(SupportTelephone)
SIMPLE_PROPERTY_GET(HelpLink)
SIMPLE_PROPERTY_GET(InstallLocation)
SIMPLE_PROPERTY_GET(InstallSource)
SIMPLE_PROPERTY_GET(InstallDate)
SIMPLE_PROPERTY_GET(RequiredByPolicy)
SIMPLE_PROPERTY_GET(Contact)
// DisplayName
// The display name of the item.
STDMETHODIMP
CARPCtl::get_DisplayName(BSTR* pbstr)
{
USES_CONVERSION;
CAppData * pappdata;
if (_dwCurrentIndex >= _dwcItems)
return E_FAIL;
pappdata = _GetAppData(_dwCurrentIndex);
if (pappdata)
{
if (ENUM_CATEGORIES == _dwEnum)
{
*pbstr = W2BSTR(pappdata->GetCategory());
}
else
{
*pbstr = W2BSTR(pappdata->GetData()->pszDisplayName);
}
}
else
*pbstr = NULL;
return S_OK;
}
// Size
// The calculated size of the application. Returns "Unknown" if not available
STDMETHODIMP
CARPCtl::get_Size(BSTR* pbstr)
{
USES_CONVERSION;
TCHAR szSize[256];
ULONG ulSize = 0;
LPTSTR WINAPI ShortSizeFormat(DWORD dw, LPTSTR szBuf);
CAppData * pappdata;
if (_dwCurrentIndex >= _dwcItems)
return E_FAIL;
// Get the size and truncate to a ULONG. If the app is bigger than 4G,
// well, too bad.
pappdata = _GetAppData(_dwCurrentIndex);
if (pappdata)
ulSize = (ULONG)pappdata->GetSlowData()->ullSize;
// If the size is zero, return unknown, otherwise,
// Use the shell32 function to make a nicely formatted size string
if (ulSize == 0)
LoadString(g_hinst, IDS_UNKNOWN, szSize, ARRAYSIZE(szSize));
else
ShortSizeFormat(ulSize, szSize);
// Return as a BSTR
*pbstr = W2BSTR(szSize);
return S_OK;
}
// TimesUsed
// Returns the number of times used for this item
STDMETHODIMP
CARPCtl::get_TimesUsed(BSTR* pbstr)
{
USES_CONVERSION;
int ncUsed = 0;
WCHAR szUsed[256];
CAppData * pappdata;
if (_dwCurrentIndex >= _dwcItems)
return E_FAIL;
pappdata = _GetAppData(_dwCurrentIndex);
if (pappdata)
ncUsed = pappdata->GetSlowData()->iTimesUsed;
// Convert to a BSTR
wsprintf(szUsed, TEXT("%d"), ncUsed);
*pbstr = W2BSTR(szUsed);
return S_OK;
}
// LastUsed
// Returns last date the app was used
STDMETHODIMP
CARPCtl::get_LastUsed(BSTR* pbstr)
{
USES_CONVERSION;
FILETIME ft = {0};
WCHAR szDate[256];
CAppData * pappdata;
if (_dwCurrentIndex >= _dwcItems)
return E_FAIL;
pappdata = _GetAppData(_dwCurrentIndex);
if (pappdata)
ft = pappdata->GetSlowData()->ftLastUsed;
// Convert to a BSTR
FileTimeToDateTimeString(&ft, szDate, SIZECHARS(szDate));
*pbstr = W2BSTR(szDate);
return S_OK;
}
// Capability
// Flags that indicate the possible actions that can
// be performed on the item. See APPACTION_* flags.
STDMETHODIMP
CARPCtl::get_Capability(long * pVal)
{
CAppData * pappdata;
if (_dwCurrentIndex >= _dwcItems)
return E_FAIL;
pappdata = _GetAppData(_dwCurrentIndex);
if (pappdata)
*pVal = pappdata->GetCapability();
else
*pVal = 0;
return S_OK;
}
// ItemCount
// Number of items in current list
STDMETHODIMP
CARPCtl::get_ItemCount(long * pVal)
{
*pVal = _dwcItems;
return S_OK;
}
//--------------------------------------------------------------------
// Object lifetime stuff
// CARPCtl constructor
CARPCtl::CARPCtl()
{
ASSERT(NULL == _pmtxarray);
ASSERT(NULL == _pam);
}
// CARPCtl destructor
CARPCtl::~CARPCtl()
{
// Kill the worker thread if it's still around
_workerthread.Kill();
// Free our contained object
ATOMICRELEASE(_pam);
// Clean up the application list
_FreeAppData();
}
//--------------------------------------------------------------------
// Private methods
// CARPCtl::_GetAppData
// Returns the appdata of the current record, or NULL if there
// is no such record.
CAppData *
CARPCtl::_GetAppData(DWORD iItem)
{
CAppData * pappdata = NULL;
if (_pmtxarray && iItem < _dwcItems)
pappdata = _pmtxarray->GetAppData(iItem);
return pappdata;
}
// CARPCtl::_FreeAppData
// Frees all memory associated with the application
void
CARPCtl::_FreeAppData()
{
if (_pmtxarray)
{
delete _pmtxarray;
_pmtxarray = NULL;
}
_dwcItems = 0;
}