2025-04-27 07:49:33 -04:00

665 lines
16 KiB
C++

// MyInfoCtl.cpp : Implementation of CMyInfoCtl
#include "stdafx.h"
#include "MyInfo.h"
#include "MyInfCtl.h"
#include "WCParser.h"
#include <stdio.h>
#include <pudebug.h>
class CMyInfoLock
{
public:
CMyInfoLock()
{
CMyInfoCtl::Lock();
}
~CMyInfoLock()
{
CMyInfoCtl::Unlock();
}
};
/////////////////////////////////////////////////////////////////////////////
// CMyInfoCtl
static long gCMyInfoCtlCount = 0;
// Initialize will handle one-time class initialization, it should be called in DllMain
bool
CMyInfoCtl::Initialize()
{
::InitializeCriticalSection( &s_cs );
SET_CRITICAL_SECTION_SPIN_COUNT(&s_cs, IIS_DEFAULT_CS_SPIN_COUNT);
return true;
}
// Uninitialize will handle one-time class unitialization, it should be called in DllMain
bool
CMyInfoCtl::Uninitialize()
{
if ( s_pInfoBase )
{
delete s_pInfoBase;
}
::DeleteCriticalSection( &s_cs );
return true;
}
// lock the shared critical section
void
CMyInfoCtl::Lock()
{
::EnterCriticalSection( &s_cs );
}
// unlock the shared critical section
void
CMyInfoCtl::Unlock()
{
::LeaveCriticalSection( &s_cs );
}
// Constructor for CMyInfoCtl
// The myInfoTop parameter is set to true when the object is the toplevel CMyInfoCtl
// (created by the Class factory), and false for the objects that make up the rest
// of the tree.
CMyInfoCtl::CMyInfoCtl(bool myInfoTop)
{
long numInstances = ::InterlockedIncrement(&gCMyInfoCtlCount);
ATLTRACE( _T("CMyInfoCtl(): %ld instances\n"), numInstances );
m_bIsMyInfoTop = myInfoTop;
if(m_bIsMyInfoTop)
m_cRef = 1;
else
m_cRef = 0;
}
CMyInfoCtl::~CMyInfoCtl(void)
{
if(this == s_pInfoBase) // If we are deleting the top, first save it
{
CMyInfoLock lock;
CMyInfoCtl::SaveFile();
s_pInfoBase = NULL;
}
long numInstances = ::InterlockedDecrement(&gCMyInfoCtlCount);
ATLTRACE( _T("~CMyInfoCtl(): %ld instances\n"), numInstances );
}
CWDNode * CMyInfoCtl::WDClone()
{
CMyInfoCtl *newObject = new CMyInfoCtl(false);
newObject->AddRef();
return newObject;
}
STDMETHODIMP CMyInfoCtl::QueryInterface(REFIID iid, void ** ppv)
{
*ppv = NULL;
if(iid == IID_IUnknown || iid == IID_IDispatch || iid == IID_IMyInfoCtl)
{
*ppv = static_cast<IMyInfoCtl *>(this);
}
else if (iid == IID_IMarshal) {
*ppv = m_pUnkMarshaler;
}
else
return ResultFromScode(E_NOINTERFACE);
if (*ppv != NULL)
((LPUNKNOWN)*ppv)->AddRef();
return NOERROR;
}
STDMETHODIMP_(ULONG) CMyInfoCtl::AddRef(void)
{
ULONG rc = ::InterlockedIncrement( &m_cRef );
// ATLTRACE( _T("CMyInfoCtl::AddRef: %d\n"), rc );
if(rc == 1)
NodeAddRef();
return rc;
}
STDMETHODIMP_(ULONG) CMyInfoCtl::Release(void)
{
ULONG rc = ::InterlockedDecrement( &m_cRef );
// ATLTRACE( _T("CMyInfoCtl::Release: %d\n"), rc );
if(rc == 0)
{
NodeReleaseRef();
return 0;
}
return m_cRef;
}
STDMETHODIMP CMyInfoCtl::GetIDsOfNames(REFIID /*riid*/, LPOLESTR* rgszNames,
UINT cNames, LCID lcid, DISPID* rgdispid)
{
CMyInfoLock lock;
// Make sure the file is loaded. This routine does nothing if its already loaded.
LoadFile();
CMyInfoCtl *theNode;
if(m_bIsMyInfoTop)
theNode = s_pInfoBase;
else
theNode = this;
ITypeInfo* pInfo;
HRESULT hRes = GetTI(lcid, &pInfo);
if (pInfo != NULL)
{
OLECHAR *namestr = rgszNames[0];
char name[256];
if(!wcsicmp(namestr, L"OnStartPage") ||
!wcsicmp(namestr, L"OnEndPage"))
{
*rgdispid = 0;
hRes = DISP_E_UNKNOWNNAME;
}
else
{
for(int i = 0; namestr[i] != 0; i++)
name[i] = (char) namestr[i];
name[i] = 0;
CMyInfoCtl *subNode = static_cast<CMyInfoCtl *>(theNode->GetChild(name, 0));
if(subNode == NULL)
{
subNode = new CMyInfoCtl(false);
theNode->AddChild(name, subNode);
DirtyMyInfo();
}
// The collection member referenced is in subNode. Calculate
// a dispid to return by finding out its index inside its parent (theNode).
// The indexes are 0 based, while the dispid needs to be 1 based, so add 1.
*rgdispid = (DISPID) theNode->GetChildNumber(subNode) + 1;
hRes = S_OK;
}
pInfo->Release();
}
return hRes;
}
HRESULT CMyInfoCtl::PutVariant(CWDNode *theNode, VARIANT *data)
{
CSimpleUTFString value;
if(data->vt != VT_BSTR)
{
VARIANTARG dest;
VariantInit(&dest);
HRESULT result = VariantChangeType(&dest, data, 0, VT_BSTR);
if(result != S_OK)
return DISP_E_TYPEMISMATCH;
value.Copy(dest.bstrVal, SysStringLen(dest.bstrVal));
}
else
value.Copy(data->bstrVal, SysStringLen(data->bstrVal));
if(theNode->NumValues() == 0)
theNode->AddValue(&value, false);
else
theNode->ReplaceValue(0, &value, false);
DirtyMyInfo();
return S_OK;
}
STDMETHODIMP CMyInfoCtl::Invoke(DISPID dispidMember, REFIID /*riid*/,
LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
EXCEPINFO* pexcepinfo, UINT* puArgErr)
{
CMyInfoLock lock;
CWDNode *theNode;
CMyInfoCtl *subNode = NULL;
if(m_bIsMyInfoTop)
theNode = s_pInfoBase;
else
theNode = this;
if(dispidMember > 0)
{
subNode = static_cast<CMyInfoCtl *>(theNode->GetChildIndex(dispidMember - 1));
}
SetErrorInfo(0, NULL);
ITypeInfo* pInfo;
HRESULT hRes = GetTI(lcid, &pInfo);
if (pInfo != NULL)
{
short extraArgs = 0;
if(wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF))
extraArgs++; // Skip the value to store in the beginning
if(dispidMember == DISPID_NEWENUM)
{
// An enumerator for this collection has been requested
CMyInfoEnum *theEnum = NULL;
theEnum = CMyInfoEnum::Create(theNode);
if(theEnum == NULL)
{
// Error
return E_OUTOFMEMORY;
}
IUnknown *theEnumUnknown = NULL;
theEnum->QueryInterface(IID_IUnknown, (void **) &theEnumUnknown);
V_VT(pvarResult) = VT_UNKNOWN;
V_UNKNOWN(pvarResult) = theEnumUnknown;
return S_OK;
}
if(dispidMember != DISPID_VALUE || pdispparams->cArgs > (unsigned short) extraArgs)
{
bool lastArgName = false;
if(dispidMember != DISPID_VALUE)
{
theNode = subNode;
lastArgName = true;
}
// Note: The following would NOT work if argNum were not signed!
// Since pdispparams->cArgs is unsigned we cast it to a signed int first
for(int argNum = ((int) pdispparams->cArgs) - 1; argNum >= extraArgs; argNum--)
{
// Collection reference
// VARTYPE theType = pdispparams->rgvarg[argNum].vt;
VARIANTARG dest;
VariantInit(&dest);
hRes = VariantChangeType(&dest, &(pdispparams->rgvarg[argNum]), 0, VT_I4);
if(hRes != S_OK)
{
hRes = VariantChangeType(&dest, &(pdispparams->rgvarg[argNum]), 0, VT_BSTR);
if(hRes != S_OK)
{
// Error;
theNode = NULL;
break; // for loop
}
else
{
// String
OLECHAR *namestr = dest.bstrVal;
char name[256];
// BOYDM - Convert the wide character string down to ansi taking
// into account any dbcs conversions and special character conversions.
int i = WideCharToMultiByte(
CP_ACP, // code page
0, // performance and mapping flags
namestr, // address of wide-character string
-1, // number of characters in string -> -1 means null terminated
name, // address of buffer for new string
256, // size of buffer
NULL, // address of default for unmappable characters
NULL // address of flag set when default char. used
);
// for(int i = 0; i < 255 && namestr[i] != 0; i++)
// name[i] = (char) namestr[i];
// name[i] = 0;
CWDNode *oldNode = theNode;
theNode = static_cast<CMyInfoCtl *>(theNode->GetChild(name, 0));
if(!theNode)
{
theNode = new CMyInfoCtl(false);
oldNode->AddChild(name, theNode);
DirtyMyInfo();
}
lastArgName = true;
}
}
else
{
if(lastArgName)
{
// If the previous item is a name then we retrieve the
// nth item with that name
CWDNode *subNode = NULL;
subNode = static_cast<CMyInfoCtl *>(theNode->GetSibling(dest.lVal));
if(subNode == NULL && (wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)))
{
while(theNode->GetParent()->GetChild(theNode->GetMyName(), dest.lVal) == NULL)
{
subNode = new CMyInfoCtl(false);
theNode->GetParent()->AddChild(theNode->GetMyName(), subNode);
DirtyMyInfo();
}
}
theNode = subNode;
}
else
{
theNode = theNode->GetChildIndex(dest.lVal);
}
lastArgName = false;
}
if(theNode == NULL)
{
return DISP_E_MEMBERNOTFOUND;
}
}
if(wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF))
{
hRes = PutVariant(theNode, &pdispparams->rgvarg[0]);
}
else
{
CMyInfoCtl *theInfo = static_cast<CMyInfoCtl *> (theNode);
IDispatch *theInfoDispatch = NULL;
theInfo->QueryInterface(IID_IDispatch, (void **) &theInfoDispatch);
V_VT(pvarResult) = VT_DISPATCH;
V_DISPATCH(pvarResult) = theInfoDispatch;
}
}
else if(wFlags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF))
{
if(dispidMember == DISPID_VALUE)
{
hRes = PutVariant(this, &pdispparams->rgvarg[0]);
}
else
{
hRes = PutVariant(subNode, &pdispparams->rgvarg[0]);
}
}
else if(wFlags & DISPATCH_PROPERTYGET)
{
if(dispidMember == DISPID_VALUE && pdispparams->cArgs == 0)
{
CSimpleUTFString *theStr = theNode->GetValue(0);
if(theStr == NULL)
{
VariantInit(pvarResult);
}
else
{
V_VT(pvarResult) = VT_BSTR;
V_BSTR(pvarResult) = SysAllocStringLen((OLECHAR *) theStr->getData(), theStr->Length());
}
}
}
pInfo->Release();
}
return hRes;
}
void CMyInfoCtl::SaveFile(void)
{
// Notes:
// This function does not require any additional syncronization since
// the use of the CreateFile with no sharing permission effectively
// prevents multiple access to the same file. The behavior
// of a failed CreateFile call is important. We should
// not set any of the flags for whether the file has been written or not.
HANDLE hFile;
_TCHAR fileNameBuffer[MAX_PATH];
GetSystemDirectory(fileNameBuffer, MAX_PATH-16);
_tcscat(fileNameBuffer, _T("\\inetsrv\\Data\\MyInfo.xml"));
hFile = CreateFile( fileNameBuffer, // open MyInfo.xml
GENERIC_WRITE, // open for writing
0, // don't share
NULL, // no security
CREATE_ALWAYS, // overwrite existing file
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
if (hFile == INVALID_HANDLE_VALUE)
{
// ErrorHandler("Could not open file."); // process error
return;
}
CMyInfoCtl *clone = new CMyInfoCtl(false);
clone->AddRef();
CWDParser *theParser = new CWDParser(clone);
theParser->StartOutput(s_pInfoBase);
while(true)
{
char buffer[1026];
unsigned long size = 1024;
bool more = theParser->Output(buffer, &size);
if(size > 0)
{
DWORD bytesWritten;
WriteFile( hFile, buffer, size,
&bytesWritten, NULL);
}
if(!more)
break;
}
CloseHandle(hFile);
clone->Release();
delete theParser;
s_bInfoDirty = false;
s_dwLastSaveTime = GetTickCount();
}
void CMyInfoCtl::DirtyMyInfo(void)
{
s_bInfoDirty = true;
if(s_pInfoBase != NULL && s_dwLastSaveTime + 15 * 1000 < GetTickCount())
SaveFile();
}
void CMyInfoCtl::LoadFile(void)
{
// Once we have already loaded the file, s_pInfoBase is set to point to the properties
if(s_pInfoBase != NULL)
return; // Only load the file once at startup
CWCParser *theParser;
CMyInfoCtl *clone = new CMyInfoCtl(false);
clone->AddRef();
theParser = new CWDParser(clone);
CWDNode *theNode = theParser->StartParse();
s_pInfoBase = static_cast<CMyInfoCtl *> (theNode);
HANDLE hFile;
// Calculate the file location- Always put it in Windows\System32\inetsrv\MyInfo.xml
_TCHAR fileNameBuffer[MAX_PATH];
GetSystemDirectory(fileNameBuffer, MAX_PATH-16);
_tcscat(fileNameBuffer, _T("\\inetsrv\\Data\\MyInfo.xml"));
hFile = CreateFile( fileNameBuffer, // open MyInfo.xml
GENERIC_READ, // open for reading
0, // don't share
NULL, // no security
OPEN_EXISTING, // overwrite existing
// file
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
if (hFile != INVALID_HANDLE_VALUE)
{
while(true)
{
DWORD bytesRead;
unsigned char buffer[256];
if(!ReadFile( hFile,
buffer,
256,
&bytesRead,
NULL) || bytesRead == 0)
{ // Done
break;
}
theParser->Parse(buffer, bytesRead);
}
CloseHandle(hFile);
}
theParser->EndParse();
clone->Release();
delete theParser;
s_bInfoDirty = false;
s_dwLastSaveTime = GetTickCount();
}
CMyInfoCtl* CMyInfoCtl::s_pInfoBase = NULL;
bool CMyInfoCtl::s_bInfoDirty = false;
DWORD CMyInfoCtl::s_dwLastSaveTime = 0;
CRITICAL_SECTION CMyInfoCtl::s_cs;
// Implementation of CMyInfoEnum. This lets you enumerate over the contents of the collection.
STDMETHODIMP CMyInfoEnum::QueryInterface(REFIID iid, void ** ppv)
{
*ppv = NULL;
if(iid == IID_IUnknown || iid == IID_IEnumVARIANT)
*ppv = this;
else
return ResultFromScode(E_NOINTERFACE);
AddRef();
return NOERROR;
}
STDMETHODIMP_(ULONG) CMyInfoEnum::AddRef(void)
{
return ::InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) CMyInfoEnum::Release(void)
{
ULONG rc = ::InterlockedDecrement(&m_cRef);
if(rc == 0)
{
delete this;
}
return rc;
}
static long gCMyInfoEnumCount = 0;
CMyInfoEnum::CMyInfoEnum(CWDNode *theInfo)
{
_Module.Lock();
// ATLTRACE( _T("CMyInfo: new enumeration\n") );
::InterlockedIncrement(&gCMyInfoEnumCount);
m_Info = theInfo;
m_Index = 0;
m_cRef = 0;
}
CMyInfoEnum::~CMyInfoEnum(void)
{
// ATLTRACE( _T("CMyInfo: enumeration destroyed\n") );
::InterlockedDecrement(&gCMyInfoEnumCount);
_Module.Unlock();
}
STDMETHODIMP CMyInfoEnum::Next(ULONG cElements, VARIANT * pvar, ULONG * pcElementFetched)
{
CMyInfoLock lock;
if(m_Index >= m_Info->NumChildren())
{
if(pcElementFetched)
*pcElementFetched = 0;
return ResultFromScode(S_FALSE);
}
CWDNode *theNode = NULL;
theNode = m_Info->GetChildIndex(m_Index);
if ( pcElementFetched )
{
*pcElementFetched = 0;
}
for ( ULONG i = 0; ( i < cElements ) && ( m_Index < m_Info->NumChildren() ); i++ )
{
if(pcElementFetched)
{
(*pcElementFetched)++;
}
CMyInfoCtl *theInfo = static_cast<CMyInfoCtl *> (theNode);
IDispatch *theInfoDispatch = static_cast<IDispatch *>(theInfo);
theInfoDispatch->AddRef();
pvar[i].vt = VT_DISPATCH;
pvar[i].pdispVal = theInfoDispatch;
Skip(1);
}
return NOERROR;
}
STDMETHODIMP CMyInfoEnum::Skip(ULONG cElements)
{
m_Index += cElements;
CMyInfoLock lock;
if(m_Index >= m_Info->NumChildren())
{
m_Index = m_Info->NumChildren();
return ResultFromScode(S_FALSE);
}
else
{
return NOERROR;
}
}
STDMETHODIMP CMyInfoEnum::Reset()
{
m_Index = 0;
return NOERROR;
}
STDMETHODIMP CMyInfoEnum::Clone(IEnumVARIANT **ppenum)
{
CMyInfoEnum *newEnum = CMyInfoEnum::Create(m_Info);
if (newEnum == NULL)
return E_OUTOFMEMORY;
newEnum->m_Index = m_Index;
*ppenum = newEnum;
return NOERROR;
}