|
|
#if !defined(_FUSION_INC_PSPIPEHELPER_H_INCLUDED_)
#define _FUSION_INC_PSPIPEHELPER_H_INCLUDED_
#pragma once
#pragma warning(disable: 4127)
#include "fusioncom.h"
#include "smartref.h"
#include "fusewin.h"
#include "fusionbytebuffer.h"
#include "pstream.h"
#define IFFAIL_RETURN(x) \
do { \ const HRESULT __hr = (x); \ if (FAILED(__hr)) \ return __hr; \ } while (0)
#define IFFAIL_SET_PIPE_BAD_AND_RETURN(x) \
do { \ const HRESULT __hr = (x);\ if (FAILED(__hr)) \ { \ m_fPipeBad = true; \ return __hr; \ } \ } while (0)
#define IFFAIL_SET_PIPE_BAD_IF_AND_RETURN(x, cond) \
do { \ const HRESULT __hr = (x);\ if (FAILED(__hr)) \ { \ if ((cond)) m_fPipeBad = true; \ return __hr; \ } \ } while (0)
// SET_PIPE_BAD_IF_AND_EXIT evaluates the first parameter. If it is
// failure and "cond" is true, the pipe is set to be broken. Regardless
// of success vs. failure, the value of the first parameter is returned.
#define SET_PIPE_BAD_IF_AND_EXIT(x, cond) \
do { \ const HRESULT __hr = (x); \ if (FAILED(__hr)) \ { \ if ((cond)) m_fPipeBad = true; \ hr = __hr; \ goto Exit; \ } \ } while (0)
#define UNUSED(x) (x)
extern HRESULT FusionWriteProperty(IPipeByte *pIPipeByte, ULONG propid, const PROPVARIANT &rvarValue, bool &rfAnyDataWritten); extern HRESULT FusionWriteBlobVectorProperty(IPipeByte *pIPipeByte, ULONG propid, ULONG cElements, va_list ap, bool &rfAnyDataWritten); extern HRESULT FusionWriteUI2VectorProperty(IPipeByte *pIPipeByte, ULONG propid, ULONG cElements, va_list ap, bool &rfAnyDataWritten);
class CPropertyStreamPipeWriter { public: CPropertyStreamPipeWriter() : m_fInitialized(false), m_dwNextSectionCookie(0), m_fPipeBad(false) { } ~CPropertyStreamPipeWriter() { }
HRESULT Initialize(IPipeByte *pIPipeByte) { if (m_fInitialized) return E_UNEXPECTED; if (pIPipeByte == NULL) return E_POINTER; m_srpIPipeByte = pIPipeByte; m_fInitialized = true; m_fPipeBad = false; return NOERROR; }
// The pipe data stream is considered bad if we started writing some part of a data
// item and failed before completing. Once a pipe is bad, there is nothing
// you can do with it except close it.
bool IsPipeBad() const { return m_fPipeBad; }
HRESULT WriteHeader(REFCLSID rclsidOriginator) { if (!m_fInitialized) return E_UNEXPECTED; if (m_dwNextSectionCookie != 0) return E_UNEXPECTED; if (m_fPipeBad) return HRESULT_FROM_WIN32(ERROR_BAD_PIPE); FUSION_PSTREAM_VERSION_1_HEADER hdr; hdr.vih.wByteOrder = 0xFFFE; hdr.vih.wFormat = 1; hdr.vih.dwOSVer = (DWORD) MAKELONG(LOWORD(::GetVersion()), 2); // taken from MSDN mk:@MSITStore:\\infosrv2\msdn_oct99\MSDN\COM.chm::/devdoc/com/stgasstg_44mr.htm
hdr.vih.clsid = rclsidOriginator; hdr.vih.reserved = 0; IFFAIL_RETURN(m_srpIPipeByte->Push((LPBYTE) &hdr, sizeof(hdr))); m_dwNextSectionCookie = 1; return NOERROR; }
HRESULT BeginSection(REFGUID rguidSectionSet, ULONG ulSectionID, DWORD &rdwSectionCookie) { if (!m_fInitialized) return E_UNEXPECTED; ASSERT(m_dwNextSectionCookie != 0); // forgot to send header
if (m_dwNextSectionCookie == 0) return E_UNEXPECTED; if (m_fPipeBad) return HRESULT_FROM_WIN32(ERROR_BAD_PIPE); FUSION_PSTREAM_ELEMENT elt; elt.bIndicator = FUSION_PSTREAM_INDICATOR_SECTION_BEGIN; elt.BeginSectionVal.guidSectionSet = rguidSectionSet; elt.BeginSectionVal.ulSectionID = ulSectionID; IFFAIL_RETURN(m_srpIPipeByte->Push((LPBYTE) &elt, FUSION_PSTREAM_SIZEOF_SECTION_BEGIN)); rdwSectionCookie = m_dwNextSectionCookie++; return NOERROR; }
HRESULT EndSection(DWORD dwSectionCookie) { UNUSED(dwSectionCookie); // Eventually check this so that we can make sure caller doesn't mis-match begins/ends
if (!m_fInitialized) return E_UNEXPECTED; if (m_fPipeBad) return HRESULT_FROM_WIN32(ERROR_BAD_PIPE); FUSION_PSTREAM_ELEMENT elt; elt.bIndicator = FUSION_PSTREAM_INDICATOR_SECTION_END; IFFAIL_RETURN(m_srpIPipeByte->Push((LPBYTE) &elt, FUSION_PSTREAM_SIZEOF_SECTION_END)); return NOERROR; }
HRESULT WriteProperty(DWORD dwSectionCookie, ULONG ulPropertyID, PROPVARIANT varValue) { UNUSED(dwSectionCookie); if (!m_fInitialized) return E_UNEXPECTED; if (m_fPipeBad) return HRESULT_FROM_WIN32(ERROR_BAD_PIPE); // Too much work to even try to do inline..
bool fAnyDataWritten = false; IFFAIL_SET_PIPE_BAD_IF_AND_RETURN(::FusionWriteProperty(m_srpIPipeByte, ulPropertyID, varValue, fAnyDataWritten), fAnyDataWritten); return NOERROR; }
// cch should not include space for the null...
HRESULT WriteProperty(DWORD dwSectionCookie, ULONG ulPropertyID, LPCWSTR szValue, INT cch = -1) { UNUSED(dwSectionCookie); if (!m_fInitialized) return E_UNEXPECTED; if (m_fPipeBad) return HRESULT_FROM_WIN32(ERROR_BAD_PIPE); if (cch < 0) { cch = ::wcslen(szValue); // Handle overflow - crazy case, but might as well not crash.
if (cch < 0) cch = 0x7fffffff; } else { // Trim off trailing null characters when user passed in cch > 0
while ((cch > 0) && (szValue[cch - 1] == L'\0')) cch--; } FUSION_PSTREAM_ELEMENT elt; elt.bIndicator = FUSION_PSTREAM_INDICATOR_PROPERTY; elt.PropertyVal.propid = ulPropertyID; elt.PropertyVal.wType = VT_LPWSTR; bool fAnyDataWritten = false; IFFAIL_RETURN(m_srpIPipeByte->Push((LPBYTE) &elt, FUSION_PSTREAM_SIZEOF_PROPERTY)); IFFAIL_SET_PIPE_BAD_AND_RETURN(m_srpIPipeByte->Push((LPBYTE) &cch, sizeof(cch))); IFFAIL_SET_PIPE_BAD_AND_RETURN(m_srpIPipeByte->Push((LPBYTE) szValue, cch * sizeof(WCHAR))); return NOERROR; }
// WriteVector() currently only understands: VT_UI2, VT_BLOB
HRESULT WriteVectorVa(DWORD dwSectionCookie, ULONG ulPropertyID, VARTYPE vt, ULONG cElements, va_list ap) { HRESULT hr = NOERROR; bool fAnyDataWritten = false; UNUSED(dwSectionCookie); if (!m_fInitialized) { hr = E_UNEXPECTED; goto Exit; } if (m_fPipeBad) { hr = HRESULT_FROM_WIN32(ERROR_BAD_PIPE); goto Exit; } switch (vt) { case VT_BLOB: SET_PIPE_BAD_IF_AND_EXIT( ::FusionWriteBlobVectorProperty(m_srpIPipeByte, ulPropertyID, cElements, ap, fAnyDataWritten), fAnyDataWritten); break;
case VT_UI2: SET_PIPE_BAD_IF_AND_EXIT( ::FusionWriteUI2VectorProperty(m_srpIPipeByte, ulPropertyID, cElements, ap, fAnyDataWritten), fAnyDataWritten); break;
default: ASSERT(FALSE); hr = E_INVALIDARG; goto Exit;
break; }
hr = NOERROR; Exit: return hr; }
HRESULT WriteVector(DWORD dwSectionCookie, ULONG ulPropertyID, VARTYPE vt, ULONG cElements, ...) { va_list ap; if (!m_fInitialized) return E_UNEXPECTED; if (m_fPipeBad) return HRESULT_FROM_WIN32(ERROR_BAD_PIPE); va_start(ap, cElements); HRESULT hr = this->WriteVectorVa(dwSectionCookie, ulPropertyID, vt, cElements, ap); va_end(ap); if (!FAILED(hr)) hr = NOERROR; return hr; }
HRESULT Close() { if (!m_fInitialized) return E_UNEXPECTED; HRESULT hr = m_srpIPipeByte->Push(0, 0); if (FAILED(hr)) return hr; m_srpIPipeByte.Release(); m_fInitialized = false; return NOERROR; }
protected: CSmartRef<IPipeByte> m_srpIPipeByte; bool m_fInitialized; DWORD m_dwNextSectionCookie; bool m_fPipeBad; };
//
// CPropertyStreamPipeReader is an aggregatable/tear-off-able COM object that
// you can instantiate on your class in order to get callbacks when a piped
// property stream has data available on it.
//
// We expect to invoke the following member functions on the T pointer passed in:
//
// HRESULT OnPropertyStreamHeader(PFUSION_PSTREAM_VERSION_INDEPENDENT_HEADER phdr);
// Called when the header has been recognized from the stream in.
// If this function returns a non-successful HRESULT, the remainder of
// the property stream is discarded, and when OnPropertyStreamEnd() is
// called, the caller should call StopParsingPropertyStream().
//
// HRESULT OnPropertyStreamElement(PFUSION_PSTREAM_ELEMENT pelt);
// Called when a property stream element header has been recognized.
// This may be either a section begin (in which case the section guid
// and ID are available in the pelt), a section end (no additional data),
// or a property (in which case the property id and property type are
// available in the pelt). Once a property header has been found, the
// property data is buffered until entirely available.
// If this function returns a non-successful HRESULT, the remainder of
// the property stream is discarded, and when OnPropertyStreamEnd() is
// called, the caller should call StopParsingPropertyStream().
//
// HRESULT OnPropertyStreamPropertyValue(LPCBYTE *prgbValue, ULONG cbValue);
// Called when an entire property value is available. Note that
// the buffer passed in is the raw format of the property value; strings
// are not null terminated and there may or may not be valid data past
// prgbValue[cbValue-1].
// If this function returns a non-successful HRESULT, the remainder of
// the property stream is discarded, and when OnPropertyStreamEnd() is
// called, the caller should call StopParsingPropertyStream().
//
// VOID OnPropertyStreamEnd(CPropertyStreamPipeReaderBase::PropertyStreamEndReason er);
// Called when the pipe is closed/ended.
//
class __declspec(novtable) CPropertyStreamPipeReaderBase : public CFusionCOMObjectBase, public IPipeByte { public: FUSION_BEGIN_COM_MAP(CPropertyStreamPipeReaderBase) FUSION_COM_INTERFACE_ENTRY(IPipeByte) FUSION_END_COM_MAP()
HRESULT StartParsingPropertyStream(); HRESULT StopParsingPropertyStream();
// IPipeByte methods:
STDMETHODIMP Pull(BYTE *prgbBuffer, ULONG cRequest, ULONG *pcReturned); STDMETHODIMP Push(BYTE *prgbBuffer, ULONG cbBuffer);
enum PropertyStreamEndReason { eNormalEnd, eStreamFormatError, ePropertyTypeError, eStreamEndEarly, eInternalError, // indicates a code bug
eOutOfMemory, eOtherError, // indicates some HRESULT other than E_OUTOFMEMORY was encountered
};
protected: CPropertyStreamPipeReaderBase() : m_fInitialized(false), m_iByteCurrent(0) { } ~CPropertyStreamPipeReaderBase() { }
virtual HRESULT FireOnPropertyStreamHeader(PCFUSION_PSTREAM_VERSION_INDEPENDENT_HEADER phdr) = 0; virtual HRESULT FireOnPropertyStreamElement(PCFUSION_PSTREAM_ELEMENT pelt) = 0; virtual HRESULT FireOnPropertyStreamPropertyValue(PCBYTE prgbValue, ULONG cbValue) = 0; virtual VOID FireOnPropertyStreamEnd(PropertyStreamEndReason pser) = 0;
bool m_fInitialized; CByteBuffer m_buffer; ULONG m_iByteCurrent;
enum State { eIdle, // Not yet started parsing
eWaitingForHeader, // waiting for header bytes
eWaitingForIndicator, // waiting for indicator byte
eWaitingForSection, // waiting for remainder of section header
eWaitingForProperty, // waiting for property
eWaitingForPropertySize, // Waiting for some size/count on the property
eWaitingForPropertyElementSize, eWaitingForPropertyElementData, eWaitingForPropertyValue, // waiting for property value
eWaitingForPipeEnd, // Hit end indicator; next push should be length 0 and there should
// be no data in the buffer.
ePipeDone, // end of pipe hit; will transition to idle when parsing is stopped
eDiscarded, // we don't care about the rest of the data pushed to us.
} m_state;
ULONG m_cbRequiredForNextStateTransition; ULONG m_cVectorElementsLeft; // Used when parsing VT_VECTOR properties; we store the
// total number of elements we still expect to find in the
// data stream here so we can tell when we can fire
// OnPropertyStreamPropertyValue().
ULONG m_iNextVectorSize; // Offset from m_iByteCurrent to the next vector element size.
WORD m_wType; // current property type we're parsing; makes a lot of code
// simpler to just copy it here.
HRESULT OnStateMet(); HRESULT OnWaitingForHeaderStateMet(); HRESULT OnWaitingForIndicatorStateMet(); HRESULT OnWaitingForSectionStateMet(); HRESULT OnWaitingForPropertyStateMet(); HRESULT OnWaitingForPropertySizeStateMet(); HRESULT OnWaitingForPropertyValueStateMet(); HRESULT OnWaitingForPropertyElementSizeStateMet(); HRESULT OnWaitingForPropertyElementDataStateMet(); };
template <class T> class __declspec(novtable) CPropertyStreamPipeReader : public CPropertyStreamPipeReaderBase { public: CPropertyStreamPipeReader() : m_pt(NULL) { } ~CPropertyStreamPipeReader() { m_pt = NULL; }
HRESULT Initialize(T *pt) { if (m_fInitialized) return E_UNEXPECTED; if (pt == NULL) return E_POINTER; IFFAIL_RETURN(CFusionCOMObjectBase::Initialize()); m_pt = pt; m_state = eIdle; return NOERROR; }
protected: T *m_pt;
virtual HRESULT FireOnPropertyStreamHeader(PCFUSION_PSTREAM_VERSION_INDEPENDENT_HEADER phdr) { ASSERT(m_fInitialized); if (!m_fInitialized) return E_UNEXPECTED; return m_pt->OnPropertyStreamHeader(phdr); }
virtual HRESULT FireOnPropertyStreamElement(PCFUSION_PSTREAM_ELEMENT pelt) { ASSERT(m_fInitialized); if (!m_fInitialized) return E_UNEXPECTED; return m_pt->OnPropertyStreamElement(pelt); }
virtual HRESULT FireOnPropertyStreamPropertyValue(PCBYTE prgbValue, ULONG cbValue) { ASSERT(m_fInitialized); if (!m_fInitialized) return E_UNEXPECTED; return m_pt->OnPropertyStreamPropertyValue(prgbValue, cbValue); }
virtual VOID FireOnPropertyStreamEnd(PropertyStreamEndReason er) { ASSERT(m_fInitialized); if (m_fInitialized) m_pt->OnPropertyStreamEnd(er); } };
#endif
|