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

904 lines
25 KiB
C++

// adcctl.cpp : Implementation of CADCCtl
#include "priv.h"
// Do not build this file if on Win9X or NT4
#ifndef DOWNLEVEL_PLATFORM
#include "adcctl.h"
#include "shdguid.h"
#include "shguidp.h"
#include "util.h" // for InstallAppFromFloppy...
#include "datasrc.h" // for CDataSrc_CreateInstance
#include "dump.h"
EXTERN_C BOOL bForceX86Env;
// Declare the private GUIDs
#include "initguid.h"
#include "iface.h"
#include "mshtml.h"
/*-------------------------------------------------------------------------
Purpose: Returns TRUE if the bstr is an empty string.
*/
inline BOOL IsEmptyBSTR(BSTR bstr)
{
return bstr == NULL || bstr[0] == 0;
}
//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL CADCCtl::_IsMyComputerOnDomain()
{
// NOTE: assume it's on the domain
BOOL bRet = TRUE;
#if WINNT
LPWSTR pszDomain;
NETSETUP_JOIN_STATUS nsjs;
if (NERR_Success == NetGetJoinInformation(NULL, &pszDomain, &nsjs))
{
if (nsjs != NetSetupDomainName)
bRet = FALSE;
NetApiBufferFree(pszDomain);
}
#endif
return bRet;
}
// constructor
CADCCtl::CADCCtl()
{
TraceMsg(TF_OBJLIFE, "(Ctl) creating");
// This object cannot be stack-allocated
ASSERT(NULL == _hwndTB);
ASSERT(FALSE == _fInReset);
ASSERT(FALSE == _fSecure);
// We do this so comctl32.dll won't fail to create the DATETIME_PICKER
// control in case we do "Add Later"
INITCOMMONCONTROLSEX icex = {0};
icex.dwSize = sizeof(icex);
icex.dwICC = ICC_DATE_CLASSES;
InitCommonControlsEx(&icex);
_fOnDomain = _IsMyComputerOnDomain();
// Set default sort values
_cbstrSort = L"displayname";
}
// destructor
CADCCtl::~CADCCtl()
{
TraceMsg(TF_OBJLIFE, "(Ctl) destroying");
ATOMICRELEASE(_psam);
_ReleaseAllMatrixObjects();
_ReleaseAllEventBrokers();
}
//------------------------------------------------------------------------
//
// These set/get methods implement the control's properties,
// copying values to and from class members. They perform no
// other processing apart from argument validation.
//
//------------------------------------------------------------------------
STDMETHODIMP CADCCtl::get_Dirty(VARIANT_BOOL * pbDirty)
{
*pbDirty = _fDirty ? VARIANT_TRUE : VARIANT_FALSE;
return S_OK;
}
STDMETHODIMP CADCCtl::put_Dirty(VARIANT_BOOL bDirty)
{
// to its current value.
_fDirty = (bDirty == VARIANT_TRUE) ? TRUE : FALSE;
return S_OK;
}
STDMETHODIMP CADCCtl::get_Category(BSTR* pbstr)
{
*pbstr = _cbstrCategory.Copy();
return *pbstr ? S_OK : E_OUTOFMEMORY;
}
STDMETHODIMP CADCCtl::put_Category(BSTR bstr)
{
if (NULL == (LPWSTR)_cbstrCategory || 0 != StrCmpIW(bstr, _cbstrCategory))
{
_cbstrCategory = bstr;
_fCategoryChanged = TRUE;
}
return S_OK;
}
STDMETHODIMP CADCCtl::get_DefaultCategory(BSTR* pbstr)
{
WCHAR sz[64];
ARPGetPolicyString(L"DefaultCategory", sz, SIZECHARS(sz));
CComBSTR bstr(sz);
*pbstr = bstr.Copy();
return *pbstr ? S_OK : E_OUTOFMEMORY;
}
STDMETHODIMP CADCCtl::get_Sort(BSTR* pbstr)
{
*pbstr = _cbstrSort.Copy();
return *pbstr ? S_OK : E_OUTOFMEMORY;
}
STDMETHODIMP CADCCtl::put_Sort(BSTR bstr)
{
_cbstrSort = bstr;
return S_OK;
}
STDMETHODIMP CADCCtl::get_Forcex86(VARIANT_BOOL * pbForce)
{
*pbForce = VARIANT_FALSE;
#ifdef WX86
*pbForce = bForceX86Env ? VARIANT_TRUE : VARIANT_FALSE;
#endif
return S_OK;
}
STDMETHODIMP CADCCtl::put_Forcex86(VARIANT_BOOL bForce)
{
#ifdef WX86
bForceX86Env = (bForce == VARIANT_TRUE) ? TRUE : FALSE;
#else
// nobody should be calling this if we are not under WX86
ASSERT(0);
#endif
return S_OK;
}
// Property: ShowPostSetup
// Returns TRUE if the Post Setup Page should be shown.
STDMETHODIMP CADCCtl::get_ShowPostSetup(VARIANT_BOOL * pbShow)
{
// Only show the page if it is needed
if (COCSetupEnum::s_OCSetupNeeded())
*pbShow = VARIANT_TRUE;
else
*pbShow = VARIANT_FALSE;
return S_OK;
}
STDMETHODIMP CADCCtl::get_OnDomain(VARIANT_BOOL * pbOnDomain)
{
*pbOnDomain = _fOnDomain ? VARIANT_TRUE : VARIANT_FALSE;
return S_OK;
}
STDMETHODIMP CADCCtl::put_OnDomain(VARIANT_BOOL bOnDomain)
{
// to its current value.
_fOnDomain = (bOnDomain == VARIANT_TRUE) ? TRUE : FALSE;
return S_OK;
}
/*-------------------------------------------------------------------------
Purpose: Creates the data source object. Also initiates the data enumeration.
*/
HRESULT CADCCtl::_CreateMatrixObject(DWORD dwEnum, IARPSimpleProvider ** pparposp)
{
HRESULT hres;
IARPSimpleProvider * parposp = NULL;
TraceMsg(TF_CTL, "(Ctl) creating matrix object");
// Security check must pass before we can provide a DSO.
if (!_fSecure)
{
TraceMsg(TF_WARNING, "(Ctl) Security blocked creating DSO");
hres = E_FAIL;
}
else
{
hres = THR(CDataSrc_CreateInstance(IID_IARPSimpleProvider, (LPVOID *)&parposp));
if (SUCCEEDED(hres))
{
if (_psam == NULL)
{
hres = THR(CoCreateInstance(CLSID_ShellAppManager, NULL, CLSCTX_INPROC_SERVER,
IID_IShellAppManager, (LPVOID *)&_psam));
}
if (_psam)
{
ASSERT(_rgparpevt[dwEnum]);
hres = THR(parposp->Initialize(_psam, _rgparpevt[dwEnum], dwEnum));
if (SUCCEEDED(hres))
{
parposp->SetSortCriteria(_cbstrSort);
parposp->SetFilter(_cbstrCategory);
}
}
if (FAILED(hres))
{
parposp->Release();
parposp = NULL;
}
}
}
*pparposp = parposp;
return hres;
}
/*-------------------------------------------------------------------------
Purpose: Helper method to kill the worker thread
*/
HRESULT CADCCtl::_KillDatasrcWorkerThread(IARPSimpleProvider * parp)
{
HRESULT hres = S_OK;
if (parp)
{
IARPWorker * pDatasrcWorker;
hres = parp->QueryInterface(IID_IARPWorker, (LPVOID *)&pDatasrcWorker);
if (SUCCEEDED(hres))
{
hres = pDatasrcWorker->KillWT();
pDatasrcWorker->Release();
}
}
return hres;
}
HRESULT CADCCtl::_ReleaseMatrixObject(DWORD dwIndex)
{
_fDirty = FALSE;
_fCategoryChanged = FALSE;
if (_rgparposp[dwIndex])
{
_KillDatasrcWorkerThread(_rgparposp[dwIndex]);
ATOMICRELEASE(_rgparposp[dwIndex]);
if (_rgparpevt[dwIndex])
_rgparpevt[dwIndex]->SetOSPListener(NULL);
}
return S_OK;
}
/*-------------------------------------------------------------------------
Purpose: Release the control's embedded matrix object.
Terminates any pending data download.
*/
HRESULT CADCCtl::_ReleaseAllMatrixObjects(void)
{
int i;
for (i = 0; i < ARRAYSIZE(_rgparposp); i++)
_ReleaseMatrixObject(i);
return S_OK;
}
const static struct {
DWORD dwEnum;
LPWSTR pszAreaText;
} c_rgEnumAreas[] = {
{ENUM_INSTALLED, L"Remove"},
{ENUM_PUBLISHED, L"Add"},
{ENUM_CATEGORIES, L"Categories"},
{ENUM_OCSETUP, L"ocsetup"},
};
/*-------------------------------------------------------------------------
Purpose: Release all the event broker objects.
*/
HRESULT CADCCtl::_ReleaseAllEventBrokers()
{
HRESULT hres = S_OK;
int i;
for (i = 0; i < NUM_ARP_SIMPLE_PROVIDERS; i++)
{
// Release our previous Event Broker.
ATOMICRELEASE(_rgparpevt[i]);
}
return hres;
}
/*-------------------------------------------------------------------------
Purpose: Initialize the event broker object. This function will create
if it doesn't exist already.
bRecreate - if TRUE, the broker object will be released and recreated.
*/
HRESULT CADCCtl::_InitEventBrokers(DataSourceListener * pdsl, BOOL bRecreate)
{
HRESULT hres = S_OK;
TraceMsg(TF_CTL, "(Ctl) initializing event broker");
int i;
for (i = 0; i < NUM_ARP_SIMPLE_PROVIDERS; i++)
{
if (bRecreate)
{
// Release our previous Event Broker.
ATOMICRELEASE(_rgparpevt[i]);
// Create a new event broker for each DSO
hres = CARPEvent_CreateInstance(IID_IARPEvent, (LPVOID *)&_rgparpevt[i], c_rgEnumAreas[i].pszAreaText);
if (FAILED(hres))
break;
}
if (_rgparpevt[i])
{
// Set the DataSourceListener for the event broker.
_rgparpevt[i]->SetDataSourceListener(pdsl);
}
}
return hres;
}
/*--------------------------------------------------------------------------
Purpose: Matches the bstrQualifiers passed in to the Enum Areas we have
*/
DWORD CADCCtl::_GetEnumAreaFromQualifier(BSTR bstrQualifier)
{
DWORD dwEnum = ENUM_UNKNOWN;
int i;
for (i = 0; i < ARRAYSIZE(c_rgEnumAreas); i++)
{
// (Trident databinding treats qualifiers case-sensitively, so we
// should too.)
if (0 == StrCmpW(bstrQualifier, c_rgEnumAreas[i].pszAreaText))
{
dwEnum = c_rgEnumAreas[i].dwEnum;
break;
}
}
return dwEnum;
}
/*-------------------------------------------------------------------------
Purpose: Creates an object that supports the OLEDBSimpleProvider interface.
We call this the OSP. This object is the main workhorse that
provides data back to the consumer (Trident's databinding agent).
MSHTML calls this method to obtain the object. It always passes
an empty BSTR for bstrQualifier.
This function can return S_OK and a NULL *ppunk! It actually is
required to do this if the OSP doesn't have any data yet. Then,
once the OSP has some data, it should fire READYSTATE_COMPLETE and
dataSetMemberChanged, which will cause this method to be called
again.
Our implementation immediately enumerates and fills the OSP before
returning, so we always hand back an OSP if we return S_OK.
*/
STDMETHODIMP CADCCtl::msDataSourceObject(BSTR bstrQualifier, IUnknown **ppunk)
{
HRESULT hres = E_FAIL;
*ppunk = NULL; // NULL in case of failure
// Let the event brokers initialize even before we have real data
if (NULL == _rgparpevt[ENUM_OCSETUP])
hres = _InitEventBrokers(NULL, TRUE);
// Check the last event broker to determine if they were created correctly
if (_rgparpevt[ENUM_OCSETUP])
{
DWORD dwEnum = _GetEnumAreaFromQualifier(bstrQualifier);
TraceMsg(TF_CTL, "(Ctl) msDataSourceObject called for %d", dwEnum);
if (dwEnum != ENUM_UNKNOWN)
{
// Hand back a data source object
if (NULL == _rgparposp[dwEnum])
{
hres = THR(_CreateMatrixObject(dwEnum, &_rgparposp[dwEnum]));
if (SUCCEEDED(hres))
{
// Tell the OSP to enumerate the items.
hres = THR(_rgparposp[dwEnum]->EnumerateItemsAsync());
if (FAILED(hres))
_ReleaseMatrixObject(dwEnum);
}
}
else
{
// Recalculate all the data.
hres = _rgparposp[dwEnum]->Recalculate();
if (SUCCEEDED(hres))
{
// fetch OLEDBSimpleProvider interface pointer and cache it
hres = THR(_rgparposp[dwEnum]->QueryInterface(IID_OLEDBSimpleProvider, (void **)ppunk));
}
}
}
}
if (FAILED(hres))
TraceMsg(TF_ERROR, "(Ctl) msDataSourceObject failed %s", Dbg_GetHRESULT(hres));
else
TraceMsg(TF_CTL, "(Ctl) msDataSourceObject returned %s and %#lx", Dbg_GetHRESULT(hres), *ppunk);
return hres;
}
const IID IID_IDATASRCListener = {0x3050f380,0x98b5,0x11cf,{0xbb,0x82,0x00,0xaa,0x00,0xbd,0xce,0x0b}};
const IID IID_DataSourceListener = {0x7c0ffab2,0xcd84,0x11d0,{0x94,0x9a,0x00,0xa0,0xc9,0x11,0x10,0xed}};
//------------------------------------------------------------------------
//
// Method: addDataSourceListener()
//
// Synopsis: Sets the COM object which should receive notification
// events.
//
// Arguments: pEvent Pointer to COM object to receive notification
// events, or NULL if no notifications to be sent.
//
// Returns: S_OK upon success.
// Error code upon failure.
//
//------------------------------------------------------------------------
STDMETHODIMP CADCCtl::addDataSourceListener(IUnknown *punkListener)
{
HRESULT hres = E_FAIL;
TraceMsg(TF_CTL, "(Ctl) addDataSourceListener called. Listener is %#lx", punkListener);
ASSERT(IS_VALID_CODE_PTR(punkListener, IUnknown));
DataSourceListener * pdsl;
// Make sure this is the interface we expect
hres = punkListener->QueryInterface(IID_DataSourceListener, (void **)&pdsl);
if (SUCCEEDED(hres))
{
_InitEventBrokers(pdsl, FALSE);
pdsl->Release();
}
return hres;
}
const TCHAR c_szStubWindowClass[] = TEXT("Add/Remove Stub Window");
HWND _CreateTransparentStubWindow(HWND hwndParent)
{
WNDCLASS wc;
RECT rc = {0};
if (hwndParent)
{
RECT rcParent = {0};
GetWindowRect(hwndParent, &rcParent);
rc.left = (rcParent.left + RECTWIDTH(rcParent)) / 2;
rc.top = (rcParent.top + RECTHEIGHT(rcParent)) / 2;
}
else
{
rc.left = CW_USEDEFAULT;
rc.top = CW_USEDEFAULT;
}
if (!GetClassInfo(HINST_THISDLL, c_szStubWindowClass, &wc))
{
wc.style = 0;
wc.lpfnWndProc = DefWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = SIZEOF(DWORD) * 2;
wc.hInstance = HINST_THISDLL;
wc.hIcon = NULL;
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = c_szStubWindowClass;
RegisterClass(&wc);
}
// WS_EX_APPWINDOW makes this show up in ALT+TAB, but not the tray.
return CreateWindowEx(WS_EX_TRANSPARENT, c_szStubWindowClass, TEXT(""), WS_POPUP | WS_VISIBLE, rc.left,
rc.top, 1, 1, hwndParent, NULL, HINST_THISDLL, NULL);
}
/*-------------------------------------------------------------------------
Purpose: Retreive the top level HWND for our HTA host from the clientsite
*/
HRESULT CADCCtl::_GetToplevelHWND()
{
HRESULT hres = E_FAIL;
if (_pclientsite)
{
IOleInPlaceSite * pops = NULL;
if (SUCCEEDED(_pclientsite->QueryInterface(IID_IOleInPlaceSite, (void **)&pops)))
{
pops->GetWindow(&_hwndTB);
pops->Release();
// Do we have a valid window?
if (_hwndTB)
{
// Yes, then go up the hwnd chain to find the top level window
HWND hwndTmp = NULL;
while (hwndTmp = ::GetParent(_hwndTB))
_hwndTB = hwndTmp;
hres = S_OK;
}
}
}
return hres;
}
/*-------------------------------------------------------------------------
Purpose: Invoke a specific command. The list of commands are specified
in the APPCMD enumerated type.
This method can be called via script.
*/
STDMETHODIMP CADCCtl::Exec(BSTR bstrQualifier, BSTR bstrCmd, LONG nRecord)
{
SetErrorInfo(0, NULL);
TraceMsg(TF_CTL, "(Ctl) Command (%ls, %d) called", bstrCmd, nRecord);
// security check must pass before we could exec anything.
if (!_fSecure)
{
TraceMsg(TF_CTL, "(Ctl) Security blocked Exec call");
return S_OK; // scripting methods cannot return failure
}
// We should always get passed a legal qualifier
DWORD dwEnum = _GetEnumAreaFromQualifier(bstrQualifier);
RIP(dwEnum != ENUM_UNKNOWN);
if (dwEnum == ENUM_UNKNOWN)
return S_OK;
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 },
{ L"addlater", APPCMD_ADDLATER },
};
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;
}
}
HWND hwndStub = NULL;
if (_hwndTB == NULL)
_GetToplevelHWND();
if (_hwndTB)
{
hwndStub = _CreateTransparentStubWindow(_hwndTB);
::EnableWindow(_hwndTB, FALSE);
::SetActiveWindow(hwndStub);
}
switch (appcmd)
{
case APPCMD_INSTALL:
case APPCMD_UNINSTALL:
case APPCMD_MODIFY:
case APPCMD_UPGRADE:
case APPCMD_REPAIR:
case APPCMD_ADDLATER:
if (_rgparposp[dwEnum])
_rgparposp[dwEnum]->DoCommand(hwndStub, appcmd, nRecord);
break;
case APPCMD_GENERICINSTALL:
InstallAppFromFloppyOrCDROM(_hwndTB);
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("/i:%s"), szInf);
ShellExecute(NULL, NULL, TEXT("sysocmgr"), szParam, NULL, SW_SHOWDEFAULT);
}
}
break;
case APPCMD_WINUPDATE:
{
TCHAR szUrl[512];
if (0 < LoadString(g_hinst, IDS_WINUPD_URL, szUrl, SIZECHARS(szUrl)))
{
ShellExecute(NULL, NULL, TEXT("wupdmgr.exe"), szUrl, NULL, SW_SHOWDEFAULT);
}
}
break;
case APPCMD_UNKNOWN:
TraceMsg(TF_ERROR, "(Ctl) Received invalid appcmd %ls", bstrCmd);
break;
default:
ASSERTMSG(0, "You forgot to add %d to the command list above", appcmd);
break;
}
if (_hwndTB)
{
::EnableWindow(_hwndTB, TRUE);
if (hwndStub)
::DestroyWindow(hwndStub);
::SetForegroundWindow(_hwndTB);
}
return S_OK;
}
/*-------------------------------------------------------------------------
Purpose: Provide an interface to the policies stored in the registry
to the poor scripting languages
This method can be called via script.
*/
STDMETHODIMP CADCCtl::IsRestricted(BSTR bstrPolicy, VARIANT_BOOL * pbRestricted)
{
RIP(pbRestricted);
*pbRestricted = ARPGetRestricted(bstrPolicy) ? VARIANT_TRUE : VARIANT_FALSE;
return S_OK;
}
/*-------------------------------------------------------------------------
Purpose: Reset the control's filter and sort criteria
This method can be called via script.
*/
STDMETHODIMP CADCCtl::Reset(BSTR bstrQualifier)
{
HRESULT hres = E_FAIL;
DWORD dwEnum = _GetEnumAreaFromQualifier(bstrQualifier);
// We should always get a legal qualifier
RIP(dwEnum != ENUM_UNKNOWN);
if (dwEnum == ENUM_UNKNOWN)
return S_OK;
// security check must pass before we could exec anything.
if (!_fSecure)
{
TraceMsg(TF_CTL, "(Ctl) Security blocked Reset call");
return S_OK; // scripting methods cannot return failure
}
TraceMsg(TF_CTL, "(Ctl) Reset called");
// Infinite recursive calls to Reset can occur if script code calls reset
// from within the datasetchanged event. This isn't a good idea.
if ( !_fInReset )
{
_fInReset = TRUE; // prevent reentrancy
// Did the EnumArea change OR
// did the category change for these published apps?
if (_fDirty || ((ENUM_PUBLISHED == dwEnum) && _fCategoryChanged))
{
// Yes; release the matrix object and recreate the event broker
_ReleaseMatrixObject(dwEnum);
// Make sure if we call Reset() right away now, we don't re-download
// the data.
_fDirty = FALSE;
_fCategoryChanged = FALSE;
// Create a new matrix object and read the new data into it
hres = THR(_CreateMatrixObject(dwEnum, &_rgparposp[dwEnum]));
if (SUCCEEDED(hres))
{
// Tell the OSP to enumerate the items.
hres = THR(_rgparposp[dwEnum]->EnumerateItemsAsync());
if (FAILED(hres))
ATOMICRELEASE(_rgparposp[dwEnum]);
}
}
else if (_rgparposp[dwEnum])
{
// No; simply re-apply the sort and filter criteria
hres = S_OK;
// Did the sort criteria change since the last sort?
if (S_OK == _rgparposp[dwEnum]->SetSortCriteria(_cbstrSort))
{
// Yes
// Create a new matrix object and transfer the contents of the
// existing object to that. We must do this because Trident's
// databinding expects to get a different object from msDataSourceObject
// when it queries for another dataset.
IARPSimpleProvider * parposp;
hres = THR(_CreateMatrixObject(dwEnum, &parposp));
if (SUCCEEDED(hres))
{
// Transferring the data is much faster than recreating it...
hres = THR(parposp->TransferData(_rgparposp[dwEnum]));
if (SUCCEEDED(hres))
{
// Release the old datasource and remember the new one
_ReleaseMatrixObject(dwEnum);
_rgparposp[dwEnum] = parposp;
// Now apply the sort on the new dataset
hres = _rgparposp[dwEnum]->Sort();
}
}
}
}
_fInReset = FALSE;
}
// reset should always return S_OK, otherwise we get scrip error.
return S_OK;
}
HRESULT CADCCtl::_CheckSecurity(IOleClientSite * pClientSite)
{
IOleContainer * poc;
if (SUCCEEDED(pClientSite->GetContainer(&poc)))
{
IHTMLDocument2 * phd;
if (SUCCEEDED(poc->QueryInterface(IID_IHTMLDocument2, (void **)&phd)))
{
IHTMLWindow2 * phw;
if (SUCCEEDED(phd->get_parentWindow(&phw)))
{
IHTMLWindow2 * phwTop;
if (SUCCEEDED(phw->get_top(&phwTop)))
{
IHTMLLocation * phl;
if (SUCCEEDED(phwTop->get_location(&phl)))
{
BSTR bstrHref;
if (SUCCEEDED(phl->get_href(&bstrHref)))
{
ASSERT(IS_VALID_STRING_PTRW(bstrHref, -1));
WCHAR szResURL[] = L"res://appwiz.cpl/default.hta";
DWORD cchUrl = lstrlen(szResURL);
if (!StrCmpNIW(bstrHref, szResURL, cchUrl))
{
_fSecure = TRUE;
}
SysFreeString(bstrHref);
}
phl->Release();
}
phwTop->Release();
}
phw->Release();
}
phd->Release();
}
poc->Release();
}
return S_OK;
}
/*-------------------------------------------------------------------------
Purpose: IOleObject::SetClientSite
For security reasons, on top of ATL's implementation of this, we need to
check our top level browser's URL location, it must be our official URL
namely "res://appwiz.cpl/frm_*.htm"
*/
STDMETHODIMP CADCCtl::IOleObject_SetClientSite(IOleClientSite *pClientSite)
{
HRESULT hres = S_OK;
// Has the site already been set?
if (pClientSite != _pclientsite)
{
ATOMICRELEASE(_pclientsite);
if (pClientSite)
{
// No; check some things out and cache them
_hwndTB = NULL;
_pclientsite = pClientSite;
if (_pclientsite)
_pclientsite->AddRef();
_fSecure = FALSE;
if (g_dwPrototype & PF_NOSECURITYCHECK)
{
_fSecure = TRUE;
}
else
_CheckSecurity(_pclientsite);
}
hres = CComControlBase::IOleObject_SetClientSite(pClientSite);
}
return hres;
}
#endif //DOWNLEVEL_PLATFORM