592 lines
15 KiB
C++
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;
|
|
}
|
|
|
|
|
|
|
|
|