|
|
// 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; }
|