Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1597 lines
46 KiB

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994
//
// File: ntpropb.cxx
//
// Contents: Property set implementation based on OLE Appendix B.
//
//---------------------------------------------------------------------------
#include "pch.cxx"
#include "h/propvar.hxx"
#define Dbg DEBTRACE_NTPROP
#define DbgS(s) (NT_SUCCESS(s)? Dbg : DEBTRACE_ERROR)
#if DBG
ULONG DebugLevel = DEBTRACE_ERROR;
//ULONG DebugLevel = DEBTRACE_ERROR | DEBTRACE_CREATESTREAM;
//ULONG DebugLevel = DEBTRACE_ERROR | MAXULONG;
ULONG DebugIndent;
ULONG cAlloc;
ULONG cFree;
#endif
UNICODECALLOUTS UnicodeCallouts =
{
WIN32_UNICODECALLOUTS
};
//+---------------------------------------------------------------------------
// Function: RtlSetUnicodeCallouts, public
//
// Synopsis: Set the Unicode conversion function pointers
//
// Arguments: [pUnicodeCallouts] -- Unicode callouts table
//
// Returns: Nothing
//---------------------------------------------------------------------------
VOID PROPSYSAPI PROPAPI
RtlSetUnicodeCallouts(
IN UNICODECALLOUTS *pUnicodeCallouts)
{
UnicodeCallouts = *pUnicodeCallouts;
}
//+---------------------------------------------------------------------------
// Function: RtlCreatePropertySet, public
//
// Synopsis: Allocate and initialize a property set context
//
// Arguments: [ms] -- Nt Mapped Stream
// [Flags] -- *one* of READ/WRITE/CREATE/CREATEIF/DELETE
// [pguid] -- property set guid (create only)
// [pclsid] -- CLASSID of propset code (create only)
// [ma] -- caller's memory allocator
// [LocaleId] -- Locale Id (create only)
// [pOSVersion] -- pointer to the OS Version header field
// [pCodePage] -- pointer to new/returned CodePage of propset
// [pnp] -- pointer to returned property set context
//
// Returns: Status code
//---------------------------------------------------------------------------
NTSTATUS PROPSYSAPI PROPAPI
RtlCreatePropertySet(
IN NTMAPPEDSTREAM ms, // Nt Mapped Stream
IN USHORT Flags, // *one* of READ/WRITE/CREATE/CREATEIF/DELETE
OPTIONAL IN GUID const *pguid, // property set guid (create only)
OPTIONAL IN GUID const *pclsid, // CLASSID of propset code (create only)
IN NTMEMORYALLOCATOR ma, // caller's memory allocator
IN ULONG LocaleId, // Locale Id (create only)
OPTIONAL OUT ULONG *pOSVersion, // OS Version from the propset header
IN OUT USHORT *pCodePage, // IN: CodePage of property set (create only)
// OUT: CodePage of property set (always)
OUT NTPROP *pnp) // pointer to return prop set context
{
NTSTATUS Status;
CMappedStream *pmstm = (CMappedStream *) ms;
CPropertySetStream *ppsstm = NULL;
BOOLEAN fOpened = FALSE;
DebugTrace(0, Dbg, ("RtlCreatePropertySet(ms=%x, f=%x, codepage=%x)\n",
ms,
Flags,
*pCodePage));
*pnp = NULL;
Status = STATUS_INVALID_PARAMETER;
if( pOSVersion != NULL )
*pOSVersion = PROPSETHDR_OSVERSION_UNKNOWN;
// Validate the input flags
if (Flags & ~(CREATEPROP_MODEMASK | CREATEPROP_NONSIMPLE))
{
DebugTrace(0, DbgS(Status), (
"RtlCreatePropertySet(ms=%x, Flags=%x) ==> bad flags!\n",
ms,
Flags));
goto Exit;
}
switch (Flags & CREATEPROP_MODEMASK)
{
case CREATEPROP_DELETE:
case CREATEPROP_CREATE:
case CREATEPROP_CREATEIF:
case CREATEPROP_WRITE:
if (!pmstm->IsWriteable())
{
Status = STATUS_ACCESS_DENIED;
goto Exit;
}
// FALLTHROUGH
case CREATEPROP_READ:
if (ma == NULL)
{
goto Exit;
}
break;
default:
DebugTrace(0, DbgS(Status), (
"RtlCreatePropertySet(ms=%x, Flags=%x) ==> invalid mode!\n",
ms,
Flags));
goto Exit;
}
ppsstm = new CPropertySetStream(
Flags,
pmstm,
(PMemoryAllocator *) ma);
if (ppsstm == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
else
{
ppsstm->Open(pguid, pclsid, LocaleId,
pOSVersion,
*pCodePage,
&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
*pCodePage = ppsstm->GetCodePage();
*pnp = (NTPROP) ppsstm;
}
// ----
// Exit
// ----
Exit:
// If we created a CPropertySetStream object, but
// the overall operation failed, we must close/delete
// the object. Note that we must do this after
// the above unlock, since ppsstm will be gone after
// this.
if (!NT_SUCCESS(Status) && ppsstm != NULL)
{
RtlClosePropertySet((NTPROP) ppsstm);
}
DebugTrace(0, DbgS(Status), (
"RtlCreatePropertySet() ==> ms=%x, s=%x\n--------\n",
*pnp,
Status));
return(Status);
}
//+---------------------------------------------------------------------------
// Function: RtlClosePropertySet, public
//
// Synopsis: Delete a property set context
//
// Arguments: [np] -- property set context
//
// Returns: Status code
//---------------------------------------------------------------------------
NTSTATUS PROPSYSAPI PROPAPI
RtlClosePropertySet(
IN NTPROP np) // property set context
{
NTSTATUS Status = STATUS_SUCCESS;
CPropertySetStream *ppsstm = (CPropertySetStream *) np;
DebugTrace(0, Dbg, ("RtlClosePropertySet(np=%x)\n", np));
ppsstm->Close(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
// ----
// Exit
// ----
Exit:
delete ppsstm;
DebugTrace(0, DbgS(Status), ("RtlClosePropertySet() ==> s=%x(%x)\n", STATUS_SUCCESS, Status));
return(STATUS_SUCCESS);
}
//+---------------------------------------------------------------------------
// Function: RtlOnMappedStreamEvent, public
//
// Synopsis: Handle a MappedStream event. Every such
// event requires a byte-swap of the property set
// headers.
//
// Arguments: [np] -- property set context
// [pbuf] -- property set buffer
// [cbstm] -- size of mapped stream (or CBSTM_UNKNOWN)
//
// NOTE: It is assumed that the caller has already taken
// the CPropertySetStream::Lock.
//
// Returns: Status code
//---------------------------------------------------------------------------
NTSTATUS PROPSYSAPI PROPAPI
RtlOnMappedStreamEvent(
IN VOID * np, // property set context (an NTPROP)
IN VOID *pbuf, // property set buffer
IN ULONG cbstm )
{
NTSTATUS Status = STATUS_SUCCESS;
CPropertySetStream *ppsstm = (CPropertySetStream *) np;
DebugTrace(0, Dbg, ("RtlOnMappedStreamEvent(np=%x)\n", np));
// Byte-swap the property set headers.
ppsstm->ByteSwapHeaders((PROPERTYSETHEADER*) pbuf, cbstm, &Status );
if( !NT_SUCCESS(Status) ) goto Exit;
// ----
// Exit
// ----
Exit:
DebugTrace(0, DbgS(Status), ("RtlOnMappedStreamEvent() ==> s=%x\n", Status));
return(Status);
} // RtlOnMappedStreamEvent()
//+---------------------------------------------------------------------------
// Function: RtlFlushPropertySet, public
//
// Synopsis: Flush property set changes to disk
//
// Arguments: [np] -- property set context
//
// Returns: Status code
//---------------------------------------------------------------------------
NTSTATUS PROPSYSAPI PROPAPI
RtlFlushPropertySet(
IN NTPROP np) // property set context
{
CPropertySetStream *ppsstm = (CPropertySetStream *) np;
NTSTATUS Status = STATUS_SUCCESS;
DebugTrace(0, Dbg, ("RtlFlushPropertySet(np=%x)\n", np));
if (ppsstm->IsModified())
{
ppsstm->ReOpen(&Status); // Reload header/size info
if( !NT_SUCCESS(Status) ) goto Exit;
ppsstm->Validate(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
ppsstm->Flush(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
ppsstm->Validate(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
}
DebugTrace(0, DbgS(Status), ("RtlFlushPropertySet() ==> s=%x\n--------\n", Status));
Exit:
return(Status);
}
//+---------------------------------------------------------------------------
// Function: MapNameToPropId, private
//
// Synopsis: Find an available propid and map it to the passed name
//
// Arguments: [ppsstm] -- property set stream
// [CodePage] -- property set codepage
// [aprs] -- array of property specifiers
// [cprop] -- count of property specifiers
// [iprop] -- index of propspec with name to map
// [pidStart] -- first PROPID to start mapping attempts
// [pstatus] -- NTSTATUS code
//
// Returns: PROPID mapped to passed name
//
// Note: Find the first unused propid starting at pidStart.
//---------------------------------------------------------------------------
PROPID
MapNameToPropId(
IN CPropertySetStream *ppsstm, // property set stream
IN USHORT CodePage,
IN PROPSPEC const aprs[], // array of property specifiers
IN ULONG cprop,
IN ULONG iprop,
IN PROPID pidStart,
OUT NTSTATUS *pstatus)
{
PROPID pid = PID_ILLEGAL;
OLECHAR const *poszName;
*pstatus = STATUS_SUCCESS;
PROPASSERT(aprs[iprop].ulKind == PRSPEC_LPWSTR);
poszName = aprs[iprop].lpwstr;
#ifdef LITTLEENDIAN // this check will only work for litte Endian
PROPASSERT(IsOLECHARString(poszName, MAXULONG));
#endif
for (pid = pidStart; ; pid++)
{
ULONG i;
OLECHAR aocName[CCH_MAXPROPNAMESZ];
ULONG cbName = sizeof(aocName);
// The caller must specify a starting propid of 2 or larger, and we
// must not increment into the reserved propids.
if (pid == PID_DICTIONARY ||
pid == PID_CODEPAGE ||
pid < PID_FIRST_USABLE)
{
*pstatus = STATUS_INVALID_PARAMETER;
goto Exit;
}
// Do not assign any propids that explitly appear in the array of
// propspecs involved in this RtlSetProperties call, nor any propids
// that are associated with any names in the propspec array.
for (i = 0; i < cprop; i++)
{
if (i != iprop) // skip the entry we are mapping
{
// Is the current PID in Propspec[]?
if (aprs[i].ulKind == PRSPEC_PROPID &&
aprs[i].propid == pid)
{
goto nextpid; // skip colliding pid
}
// Is the current PID already used in the property set?
if (aprs[i].ulKind == PRSPEC_LPWSTR &&
ppsstm->QueryPropid(aprs[i].lpwstr, pstatus) == pid)
{
goto nextpid; // skip colliding pid
}
if (!NT_SUCCESS(*pstatus)) goto Exit;
}
} // for (i = 0; i < cprop; i++)
// Do not assign any propids that currently map to any name.
// Note that the property name we are mapping does not appear in the
// dictionary -- the caller checked for this case already.
if (!ppsstm->QueryPropertyNameBuf(pid, aocName, &cbName, pstatus))
{
// The property name could not be found in the dictionary
ULONG cbT;
SERIALIZEDPROPERTYVALUE const *pprop;
// was the name not found due to an error in QueryProperyBuf?
if( !NT_SUCCESS(*pstatus) ) goto Exit;
// Do not assign any propids that currently have a property value.
pprop = ppsstm->GetValue(pid, &cbT, pstatus);
if( !NT_SUCCESS(*pstatus) ) goto Exit;
if (pprop == NULL)
{
DebugTrace(0, Dbg, (
"MapNameToPropId(Set Entry: pid=%x, name=L'%ws')\n",
pid,
poszName));
// Add the caller-provided name to the dictionary, using
// the PID that we now know is nowhere in use.
ppsstm->SetPropertyNames(1, &pid, &poszName, pstatus);
if( !NT_SUCCESS(*pstatus) ) goto Exit;
ppsstm->Validate(pstatus);
if( !NT_SUCCESS(*pstatus) ) goto Exit;
break;
} // if (pprop == NULL)
} // if (!ppsstm->QueryPropertyNameBuf(pid, awcName,
nextpid:
;
} // for (pid = pidStart; ; pid++)
Exit:
return(pid);
}
//+---------------------------------------------------------------------------
// Function: ConvertVariantToPropInfo, private
//
// Synopsis: Convert variant property values to PROPERTY_INFORMATION values
//
// Arguments: [ppsstm] -- property set stream
// [cprop] -- property count
// [pidNameFirst] -- first PROPID for new named properties
// [aprs] -- array of property specifiers
// [apid] -- buffer for array of propids
// [avar] -- array of PROPVARIANTs
// [apinfo] -- output array of property info
//
// Returns: none
//
//---------------------------------------------------------------------------
VOID
ConvertVariantToPropInfo(
IN CPropertySetStream *ppsstm, // property set stream
IN ULONG cprop, // property count
IN PROPID pidNameFirst, // first PROPID for new named properties
IN PROPSPEC const aprs[], // array of property specifiers
OPTIONAL OUT PROPID apid[], // buffer for array of propids
OPTIONAL IN PROPVARIANT const avar[],// array of properties+values
OUT PROPERTY_INFORMATION *apinfo, // output array of property info
OUT NTSTATUS *pstatus)
{
*pstatus = STATUS_SUCCESS;
USHORT CodePage = ppsstm->GetCodePage();
PROPID pidStart = pidNameFirst;
ULONG iprop;
for (iprop = 0; iprop < cprop; iprop++)
{
PROPID pid;
ULONG cbprop;
switch(aprs[iprop].ulKind)
{
case PRSPEC_LPWSTR:
{
PROPASSERT(IsOLECHARString(aprs[iprop].lpwstr, MAXULONG));
pid = ppsstm->QueryPropid(aprs[iprop].lpwstr, pstatus);
if( !NT_SUCCESS(*pstatus) ) goto Exit;
if (pid == PID_ILLEGAL && avar != NULL)
{
pid = MapNameToPropId(
ppsstm,
CodePage,
aprs,
cprop,
iprop,
pidStart,
pstatus);
if( !NT_SUCCESS(*pstatus) ) goto Exit;
pidStart = pid + 1;
}
break;
}
case PRSPEC_PROPID:
pid = aprs[iprop].propid;
break;
default:
PROPASSERT(!"Bad ulKind");
*pstatus = STATUS_INVALID_PARAMETER;
goto Exit;
break;
}
if (apid != NULL)
{
apid[iprop] = pid;
}
// RtlConvertVariantToProperty returns NULL on overflow and
// Raises on bad data.
cbprop = 0; // Assume property deletion
if (pid != PID_ILLEGAL && avar != NULL)
{
RtlConvertVariantToProperty(
&avar[iprop],
CodePage,
NULL,
&cbprop,
pid,
FALSE,
pstatus);
if( !NT_SUCCESS(*pstatus) ) goto Exit;
PROPASSERT(cbprop == DwordAlign(cbprop));
}
apinfo[iprop].cbprop = cbprop;
apinfo[iprop].pid = pid;
}
// ----
// Exit
// ----
Exit:
return;
}
//+---------------------------------------------------------------------------
// Function: RtlSetProperties, public
//
// Synopsis: Set property values for a property set
//
// Arguments: [np] -- property set context
// [cprop] -- property count
// [pidNameFirst] -- first PROPID for new named properties
// [aprs] -- array of property specifiers
// [apid] -- buffer for array of propids
// [avar] -- array of PROPVARIANTs
//
// Returns: Status code
//---------------------------------------------------------------------------
NTSTATUS PROPSYSAPI PROPAPI
RtlSetProperties(
IN NTPROP np, // property set context
IN ULONG cprop, // property count
IN PROPID pidNameFirst, // first PROPID for new named properties
IN PROPSPEC const aprs[], // array of property specifiers
OPTIONAL OUT PROPID apid[], // buffer for array of propids
OPTIONAL IN PROPVARIANT const avar[]) // array of properties+values
{
CPropertySetStream *ppsstm = (CPropertySetStream *) np;
NTSTATUS Status = STATUS_SUCCESS;
PROPERTY_INFORMATION apinfoStack[6];
PROPERTY_INFORMATION *apinfo = apinfoStack;
DebugTrace(0, Dbg, (
"RtlSetProperties(np=%x,cprop=%x,pidNameFirst=%x,aprs=%x,apid=%x)\n",
np,
cprop,
pidNameFirst,
aprs,
apid));
if( !NT_SUCCESS(Status) ) goto Exit;
// Is the stack-based apinfo big enough?
if (cprop > sizeof(apinfoStack)/sizeof(apinfoStack[0]))
{
// No - we need to allocate an apinfo.
apinfo = new PROPERTY_INFORMATION[cprop];
if (NULL == apinfo)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
}
ppsstm->ReOpen(&Status); // Reload header/size info
if( !NT_SUCCESS(Status) ) goto Exit;
ppsstm->Validate(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
ConvertVariantToPropInfo(
ppsstm,
cprop,
pidNameFirst,
aprs,
apid,
avar,
apinfo,
&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
ppsstm->SetValue(cprop, avar, apinfo, &Status);
if( !NT_SUCCESS(Status) ) goto Exit;
ppsstm->Validate(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
// ----
// Exit
// ----
Exit:
// If we allocated a temporary apinfo buffer, free it.
if (cprop > sizeof(apinfoStack)/sizeof(apinfoStack[0]))
{
delete [] apinfo;
}
DebugTrace(0, DbgS(Status), (
"RtlSetProperties() ==> status=%x\n--------\n",
Status));
return(Status);
}
//+---------------------------------------------------------------------------
// Function: RtlQueryProperties, public
//
// Synopsis: Query property values from a property set
//
// Arguments: [np] -- property set context
// [cprop] -- property count
// [aprs] -- array of property specifiers
// [apid] -- buffer for array of propids
// [avar] -- array of PROPVARIANTs
//
// Returns: Status code
//---------------------------------------------------------------------------
NTSTATUS PROPSYSAPI PROPAPI
RtlQueryProperties(
IN NTPROP np, // property set context
IN ULONG cprop, // property count
IN PROPSPEC const aprs[], // array of property specifiers
OPTIONAL OUT PROPID apid[], // buffer for array of propids
IN OUT PROPVARIANT *avar, // IN: array of uninitialized PROPVARIANTs,
// OUT: may contain pointers to alloc'd memory
OUT ULONG *pcpropFound) // count of property values retrieved
{
CPropertySetStream *ppsstm = (CPropertySetStream *) np;
SERIALIZEDPROPERTYVALUE const *pprop = NULL;
NTSTATUS Status = STATUS_SUCCESS;
ULONG iprop;
DebugTrace(0, Dbg, (
"RtlQueryProperties(np=%x, cprop=%x, aprs=%x, apid=%x)\n",
np,
cprop,
aprs,
apid));
// Initialize the variant array enough to allow it to be cleaned up
// by the caller (even on partial failure).
*pcpropFound = 0;
// Zero-ing out the caller-provided PropVariants, essentially
// sets them all to VT_EMPTY. It also zeros out the data portion,
// which prevents cleanup problems in error paths.
RtlZeroMemory(avar, cprop * sizeof(avar[0]));
ppsstm->ReOpen(&Status); // Reload header/size info
if( !NT_SUCCESS(Status) ) goto Exit;
ppsstm->Validate(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
for (iprop = 0; iprop < cprop; iprop++)
{
OLECHAR *poc;
PROPID pid;
ULONG cbprop;
switch(aprs[iprop].ulKind)
{
case PRSPEC_LPWSTR:
poc = aprs[iprop].lpwstr;
pid = ppsstm->QueryPropid(poc, &Status);
if( !NT_SUCCESS(Status) ) goto Exit;
break;
case PRSPEC_PROPID:
pid = aprs[iprop].propid;
break;
default:
PROPASSERT(!"Bad ulKind");
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
pprop = ppsstm->GetValue(pid, &cbprop, &Status);
if( !NT_SUCCESS(Status) ) goto Exit;
if (pprop != NULL)
{
(*pcpropFound)++;
RtlConvertPropertyToVariant( pprop,
ppsstm->GetCodePage(),
&avar[iprop],
ppsstm->GetAllocator(),
&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
}
if (apid != NULL)
{
apid[iprop] = pid;
}
} // for (iprop = 0; iprop < cprop; iprop++)
ppsstm->Validate(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
// ----
// Exit
// ----
Exit:
if( !NT_SUCCESS(Status) )
{
CleanupVariants(avar, cprop, ppsstm->GetAllocator());
}
DebugTrace(0, DbgS(Status), (
"RtlQueryProperties() ==> s=%x\n--------\n",
Status));
return(Status);
}
//+---------------------------------------------------------------------------
// Function: RtlEnumerateProperties, public
//
// Synopsis: Enumerate properties in a property set
//
// Arguments: [np] -- property set context
// [cskip] -- count of properties to skip
// [pcprop] -- pointer to property count
// [Flags] -- flags: No Names (propids only), etc.
// [asps] -- array of STATPROPSTGs
//
// Returns: Status code
//---------------------------------------------------------------------------
NTSTATUS PROPSYSAPI PROPAPI
RtlEnumerateProperties(
IN NTPROP np, // property set context
IN ULONG Flags, // flags: No Names (propids only), etc.
IN ULONG *pkey, // count of properties to skip
IN OUT ULONG *pcprop, // pointer to property count
OPTIONAL OUT PROPSPEC aprs[],// IN: array of uninitialized PROPSPECs
// OUT: may contain pointers to alloc'd strings
OPTIONAL OUT STATPROPSTG asps[]) // IN: array of uninitialized STATPROPSTGs
// OUT: may contain pointers to alloc'd strings
{
CPropertySetStream *ppsstm = (CPropertySetStream *) np;
NTSTATUS Status = STATUS_SUCCESS;
SERIALIZEDPROPERTYVALUE const *pprop = NULL;
PROPSPEC *pprs;
STATPROPSTG *psps;
PROPID *ppidBase = NULL;
ULONG i;
ULONG cpropin;
PROPID apidStack[20];
PROPID *ppid;
ULONG cprop;
PMemoryAllocator *pma = ppsstm->GetAllocator();
DebugTrace(0, Dbg, (
"RtlEnumerateProperties(np=%x, f=%x, key=%x, cprop=%x, aprs=%x, asps=%x)\n",
np,
Flags,
*pkey,
*pcprop,
aprs,
asps));
cpropin = *pcprop;
// Eliminate confusion for easy cleanup
if (aprs != NULL)
{
// Set all the PropSpecs to PROPID (which require
// no cleanup).
for (i = 0; i < cpropin; i++)
{
aprs[i].ulKind = PRSPEC_PROPID;
}
}
// Zero all pointers in the array for easy cleanup
if (asps != NULL)
{
RtlZeroMemory(asps, cpropin * sizeof(asps[0]));
}
ppidBase = NULL;
cprop = ppsstm->ReOpen(&Status); // Reload header/size info
if( !NT_SUCCESS(Status) ) goto Exit;
if (cprop > cpropin)
{
cprop = cpropin;
}
ppsstm->Validate(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
ppid = NULL;
if (aprs != NULL || asps != NULL)
{
ppid = apidStack;
if (cprop > sizeof(apidStack)/sizeof(apidStack[0]))
{
ppidBase = new PROPID[cprop];
if (ppidBase == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Exit;
}
ppid = ppidBase;
}
}
ppsstm->EnumeratePropids(pkey, &cprop, ppid, &Status);
if( !NT_SUCCESS(Status) ) goto Exit;
*pcprop = cprop;
if (ppid != NULL)
{
psps = asps;
pprs = aprs;
while (cprop-- > 0)
{
OLECHAR aocName[CCH_MAXPROPNAMESZ];
ULONG cbName;
ULONG cbprop;
BOOLEAN fHasName;
PROPASSERT(*ppid != PID_DICTIONARY && *ppid != PID_CODEPAGE);
fHasName = FALSE;
if ((Flags & ENUMPROP_NONAMES) == 0)
{
cbName = sizeof(aocName);
fHasName = ppsstm->QueryPropertyNameBuf(
*ppid,
aocName,
&cbName,
&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
}
if (pprs != NULL)
{
PROPASSERT(pprs->ulKind == PRSPEC_PROPID);
if (fHasName)
{
pprs->lpwstr = ppsstm->DuplicatePropertyName(
aocName,
cbName,
&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
PROPASSERT(pprs->lpwstr != NULL);
// Make this assignment *after* memory allocation
// succeeds so we free only valid pointers in below
// cleanup code.
pprs->ulKind = PRSPEC_LPWSTR;
}
else
{
pprs->propid = *ppid;
}
pprs++;
} // if (pprs != NULL)
if (psps != NULL)
{
pprop = ppsstm->GetValue(*ppid, &cbprop, &Status);
if( !NT_SUCCESS(Status) ) goto Exit;
PROPASSERT(psps->lpwstrName == NULL);
if (fHasName)
{
psps->lpwstrName = ppsstm->DuplicatePropertyName(
aocName,
cbName,
&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
PROPASSERT(psps->lpwstrName != NULL);
}
psps->propid = *ppid;
psps->vt = (VARTYPE) PropByteSwap(pprop->dwType);
psps++;
} // if (psps != NULL)
ppid++;
} // while (cprop-- > 0)
} // if (ppid != NULL)
ppsstm->Validate(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
// ----
// Exit
// ----
Exit:
delete [] ppidBase;
if (!NT_SUCCESS(Status))
{
PMemoryAllocator *pma = ppsstm->GetAllocator();
if (aprs != NULL)
{
for (i = 0; i < cpropin; i++)
{
if (aprs[i].ulKind == PRSPEC_LPWSTR)
{
pma->Free(aprs[i].lpwstr);
aprs[i].ulKind = PRSPEC_PROPID;
}
}
}
if (asps != NULL)
{
for (i = 0; i < cpropin; i++)
{
if (asps[i].lpwstrName != NULL)
{
pma->Free(asps[i].lpwstrName);
asps[i].lpwstrName = NULL;
}
}
}
} // if (!NT_SUCCESS(Status))
#if DBG
if (NT_SUCCESS(Status))
{
if (aprs != NULL)
{
for (i = 0; i < cpropin; i++)
{
if (aprs[i].ulKind == PRSPEC_LPWSTR)
{
PROPASSERT(aprs[i].lpwstr != NULL);
PROPASSERT(ocslen(aprs[i].lpwstr) > 0);
}
}
}
if (asps != NULL)
{
for (i = 0; i < cpropin; i++)
{
if (asps[i].lpwstrName != NULL)
{
PROPASSERT(ocslen(asps[i].lpwstrName) > 0);
}
}
}
}
#endif // DBG
DebugTrace(0, DbgS(Status), (
"RtlEnumerateProperties() ==> key=%x, cprop=%x, s=%x\n--------\n",
*pkey,
*pcprop,
Status));
return(Status);
}
//+---------------------------------------------------------------------------
// Function: RtlQueryPropertyNames, public
//
// Synopsis: Read property names for PROPIDs in a property set
//
// Arguments: [np] -- property set context
// [cprop] -- property count
// [apid] -- array of PROPIDs
// [aposz] -- array of pointers to OLECHAR strings
//
// Returns: Status code
//---------------------------------------------------------------------------
NTSTATUS PROPSYSAPI PROPAPI
RtlQueryPropertyNames(
IN NTPROP np, // property set context
IN ULONG cprop, // property count
IN PROPID const *apid, // PROPID array
OUT OLECHAR *aposz[]) // OUT pointers to allocated strings
{
CPropertySetStream *ppsstm = (CPropertySetStream *) np;
NTSTATUS Status = STATUS_SUCCESS;
NTSTATUS StatusQuery = STATUS_SUCCESS;
DebugTrace(0, Dbg, (
"RtlQueryPropertyNames(np=%x, cprop=%x, apid=%x, aposz=%x)\n",
np,
cprop,
apid,
aposz));
RtlZeroMemory(aposz, cprop * sizeof(aposz[0]));
ppsstm->ReOpen(&Status); // Reload header/size info
if( !NT_SUCCESS(Status) ) goto Exit;
ppsstm->Validate(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
// we'will save the status from the following call. If there are no
// other errors, we'll return it to the caller (it might contain a useful
// success code
ppsstm->QueryPropertyNames(cprop, apid, aposz, &StatusQuery);
if( !NT_SUCCESS(StatusQuery) )
{
Status = StatusQuery;
goto Exit;
}
ppsstm->Validate(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
// ----
// Exit
// ----
Exit:
DebugTrace(
0,
Status == STATUS_BUFFER_ALL_ZEROS? Dbg : DbgS(Status),
("RtlQueryPropertyNames() ==> s=%x\n--------\n", Status));
if( NT_SUCCESS(Status) )
Status = StatusQuery;
return(Status);
} // RtlQueryPropertyNames()
//+---------------------------------------------------------------------------
// Function: RtlSetPropertyNames, public
//
// Synopsis: Write property names for PROPIDs in a property set
//
// Arguments: [np] -- property set context
// [cprop] -- property count
// [apid] -- array of PROPIDs
// [aposz] -- array of pointers to OLECHAR strings
//
// Returns: Status code
//---------------------------------------------------------------------------
NTSTATUS PROPSYSAPI PROPAPI
RtlSetPropertyNames(
IN NTPROP np, // property set context
IN ULONG cprop, // property count
IN PROPID const *apid, // PROPID array
IN OLECHAR const * const aposz[]) // pointers to property names
{
CPropertySetStream *ppsstm = (CPropertySetStream *) np;
NTSTATUS Status = STATUS_SUCCESS;
DebugTrace(0, Dbg, (
"RtlSetPropertyNames(np=%x, cprop=%x, apid=%x, aposz=%x)\n",
np,
cprop,
apid,
aposz));
ppsstm->ReOpen(&Status); // Reload header/size info
if( !NT_SUCCESS(Status) ) goto Exit;
ppsstm->Validate(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
ppsstm->SetPropertyNames(cprop, apid, aposz, &Status);
if( !NT_SUCCESS(Status) ) goto Exit;
ppsstm->Validate(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
// ----
// Exit
// ----
Exit:
DebugTrace(0, DbgS(Status), ("RtlSetPropertyNames() ==> s=%x\n--------\n", Status));
return(Status);
} // RtlSetPropertyNames()
//+---------------------------------------------------------------------------
// Function: RtlSetPropertySetClassId, public
//
// Synopsis: Set the property set's ClassId
//
// Arguments: [np] -- property set context
// [pspss] -- pointer to STATPROPSETSTG
//
// Returns: Status code
//---------------------------------------------------------------------------
NTSTATUS PROPSYSAPI PROPAPI
RtlSetPropertySetClassId(
IN NTPROP np, // property set context
IN GUID const *pclsid) // new CLASSID of propset code
{
CPropertySetStream *ppsstm = (CPropertySetStream *) np;
NTSTATUS Status = STATUS_SUCCESS;
DebugTrace(0, Dbg, ("RtlSetPropertySetClassId(np=%x)\n", np));
ppsstm->ReOpen(&Status); // Reload header/size info
if( !NT_SUCCESS(Status) ) goto Exit;
ppsstm->Validate(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
ppsstm->SetClassId(pclsid, &Status);
if( !NT_SUCCESS(Status) ) goto Exit;
ppsstm->Validate(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
// ----
// Exit
// ----
Exit:
DebugTrace(0, DbgS(Status), ("RtlSetPropertySetClassId() ==> s=%x\n--------\n", Status));
return(Status);
} // RtlSetPropertySetClassId()
//+---------------------------------------------------------------------------
// Function: RtlQueryPropertySet, public
//
// Synopsis: Query the passed property set
//
// Arguments: [np] -- property set context
// [pspss] -- pointer to STATPROPSETSTG
//
// Returns: Status code
//---------------------------------------------------------------------------
NTSTATUS PROPSYSAPI PROPAPI
RtlQueryPropertySet(
IN NTPROP np, // property set context
OUT STATPROPSETSTG *pspss) // buffer for property set stat information
{
NTSTATUS Status = STATUS_SUCCESS;
CPropertySetStream *ppsstm = (CPropertySetStream *) np;
DebugTrace(0, Dbg, ("RtlQueryPropertySet(np=%x, pspss=%x)\n", np, pspss));
RtlZeroMemory(pspss, sizeof(*pspss));
ppsstm->ReOpen(&Status); // Reload header/size info
if( !NT_SUCCESS(Status) ) goto Exit;
ppsstm->Validate(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
ppsstm->QueryPropertySet(pspss, &Status);
if( !NT_SUCCESS(Status) ) goto Exit;
ppsstm->Validate(&Status);
if( !NT_SUCCESS(Status) ) goto Exit;
// ----
// Exit
// ----
Exit:
DebugTrace(0, DbgS(Status), ("RtlQueryPropertySet() ==> s=%x\n--------\n", Status));
return(Status);
} // RtlQueryPropertySet()
inline BOOLEAN
_Compare_VT_BOOL(VARIANT_BOOL bool1, VARIANT_BOOL bool2)
{
// Allow any non-zero value to match any non-zero value
return((bool1 == FALSE) == (bool2 == FALSE));
}
BOOLEAN
_Compare_VT_CF(CLIPDATA *pclipdata1, CLIPDATA *pclipdata2)
{
BOOLEAN fSame;
if (pclipdata1 != NULL && pclipdata2 != NULL)
{
if (fSame =
pclipdata1->cbSize == pclipdata2->cbSize &&
pclipdata1->ulClipFmt == pclipdata2->ulClipFmt)
{
if (pclipdata1->pClipData != NULL && pclipdata2->pClipData != NULL)
{
fSame = memcmp(
pclipdata1->pClipData,
pclipdata2->pClipData,
CBPCLIPDATA(*pclipdata1)
) == 0;
}
else
{
// They're the same if both are NULL, or if
// they have a zero length (if they have a zero
// length, either one may or may not be NULL, but they're
// still considered the same).
fSame = pclipdata1->pClipData == pclipdata2->pClipData
||
CBPCLIPDATA(*pclipdata1) == 0;
}
}
}
else
{
fSame = pclipdata1 == pclipdata2;
}
return(fSame);
}
//+---------------------------------------------------------------------------
// Function: RtlCompareVariants, public
//
// Synopsis: Compare two passed PROPVARIANTs -- case sensitive for strings
//
// Arguments: [CodePage] -- CodePage
// [pvar1] -- pointer to PROPVARIANT
// [pvar2] -- pointer to PROPVARIANT
//
// Returns: TRUE if identical, else FALSE
//---------------------------------------------------------------------------
STDAPI_(BOOLEAN) PROPSYSAPI PROPAPI
RtlCompareVariants(
USHORT CodePage,
PROPVARIANT const *pvar1,
PROPVARIANT const *pvar2)
{
if (pvar1->vt != pvar2->vt)
{
return(FALSE);
}
BOOLEAN fSame;
ULONG i;
switch (pvar1->vt)
{
case VT_EMPTY:
case VT_NULL:
fSame = TRUE;
break;
#ifdef PROPVAR_VT_I1
case VT_I1:
#endif
case VT_UI1:
fSame = pvar1->bVal == pvar2->bVal;
break;
case VT_I2:
case VT_UI2:
fSame = pvar1->iVal == pvar2->iVal;
break;
case VT_BOOL:
fSame = _Compare_VT_BOOL(pvar1->boolVal, pvar2->boolVal);
break;
case VT_I4:
case VT_UI4:
case VT_R4:
case VT_ERROR:
fSame = pvar1->lVal == pvar2->lVal;
break;
case VT_I8:
case VT_UI8:
case VT_R8:
case VT_CY:
case VT_DATE:
case VT_FILETIME:
fSame = (pvar1->hVal.QuadPart == pvar2->hVal.QuadPart);
break;
case VT_CLSID:
fSame = memcmp(pvar1->puuid, pvar2->puuid, sizeof(CLSID)) == 0;
break;
case VT_BLOB:
case VT_BLOB_OBJECT:
if (fSame = pvar1->blob.cbSize == pvar2->blob.cbSize)
{
fSame = memcmp(
pvar1->blob.pBlobData,
pvar2->blob.pBlobData,
pvar1->blob.cbSize) == 0;
}
break;
case VT_CF:
fSame = _Compare_VT_CF(pvar1->pclipdata, pvar2->pclipdata);
break;
case VT_BSTR:
if (pvar1->bstrVal != NULL && pvar2->bstrVal != NULL)
{
if (fSame = BSTRLEN(pvar1->bstrVal) == BSTRLEN(pvar2->bstrVal))
{
fSame = memcmp(
pvar1->bstrVal,
pvar2->bstrVal,
BSTRLEN(pvar1->bstrVal)) == 0;
}
}
else
{
fSame = pvar1->bstrVal == pvar2->bstrVal;
}
break;
case VT_LPSTR:
if (pvar1->pszVal != NULL && pvar2->pszVal != NULL)
{
fSame = strcmp(pvar1->pszVal, pvar2->pszVal) == 0;
}
else
{
fSame = pvar1->pszVal == pvar2->pszVal;
}
break;
case VT_LPWSTR:
if (pvar1->pwszVal != NULL && pvar2->pwszVal != NULL)
{
fSame = Prop_wcscmp(pvar1->pwszVal, pvar2->pwszVal) == 0;
}
else
{
fSame = pvar1->pwszVal == pvar2->pwszVal;
}
break;
#ifdef PROPVAR_VT_I1
case VT_VECTOR | VT_I1:
#endif
case VT_VECTOR | VT_UI1:
if (fSame = pvar1->caub.cElems == pvar2->caub.cElems)
{
fSame = memcmp(
pvar1->caub.pElems,
pvar2->caub.pElems,
pvar1->caub.cElems * sizeof(pvar1->caub.pElems[0])) == 0;
}
break;
case VT_VECTOR | VT_I2:
case VT_VECTOR | VT_UI2:
if (fSame = pvar1->cai.cElems == pvar2->cai.cElems)
{
fSame = memcmp(
pvar1->cai.pElems,
pvar2->cai.pElems,
pvar1->cai.cElems * sizeof(pvar1->cai.pElems[0])) == 0;
}
break;
case VT_VECTOR | VT_BOOL:
if (fSame = pvar1->cabool.cElems == pvar2->cabool.cElems)
{
for (i = 0; i < pvar1->cabool.cElems; i++)
{
fSame = _Compare_VT_BOOL(
pvar1->cabool.pElems[i],
pvar2->cabool.pElems[i]);
if (!fSame)
{
break;
}
}
}
break;
case VT_VECTOR | VT_I4:
case VT_VECTOR | VT_UI4:
case VT_VECTOR | VT_R4:
case VT_VECTOR | VT_ERROR:
if (fSame = pvar1->cal.cElems == pvar2->cal.cElems)
{
fSame = memcmp(
pvar1->cal.pElems,
pvar2->cal.pElems,
pvar1->cal.cElems * sizeof(pvar1->cal.pElems[0])) == 0;
}
break;
case VT_VECTOR | VT_I8:
case VT_VECTOR | VT_UI8:
case VT_VECTOR | VT_R8:
case VT_VECTOR | VT_CY:
case VT_VECTOR | VT_DATE:
case VT_VECTOR | VT_FILETIME:
if (fSame = pvar1->cah.cElems == pvar2->cah.cElems)
{
fSame = memcmp(
pvar1->cah.pElems,
pvar2->cah.pElems,
pvar1->cah.cElems *
sizeof(pvar1->cah.pElems[0])) == 0;
}
break;
case VT_VECTOR | VT_CLSID:
if (fSame = (pvar1->cauuid.cElems == pvar2->cauuid.cElems))
{
fSame = memcmp(
pvar1->cauuid.pElems,
pvar2->cauuid.pElems,
pvar1->cauuid.cElems *
sizeof(pvar1->cauuid.pElems[0])) == 0;
}
break;
case VT_VECTOR | VT_CF:
if (fSame = pvar1->caclipdata.cElems == pvar2->caclipdata.cElems)
{
for (i = 0; i < pvar1->caclipdata.cElems; i++)
{
fSame = _Compare_VT_CF(
&pvar1->caclipdata.pElems[i],
&pvar2->caclipdata.pElems[i]);
if (!fSame)
{
break;
}
}
}
break;
case VT_VECTOR | VT_BSTR:
if (fSame = (pvar1->cabstr.cElems == pvar2->cabstr.cElems))
{
for (i = 0; i < pvar1->cabstr.cElems; i++)
{
if (pvar1->cabstr.pElems[i] != NULL &&
pvar2->cabstr.pElems[i] != NULL)
{
if (fSame =
BSTRLEN(pvar1->cabstr.pElems[i]) ==
BSTRLEN(pvar2->cabstr.pElems[i]))
{
fSame = memcmp(
pvar1->cabstr.pElems[i],
pvar2->cabstr.pElems[i],
BSTRLEN(pvar1->cabstr.pElems[i])) == 0;
}
}
else
{
fSame = pvar1->cabstr.pElems[i] == pvar2->cabstr.pElems[i];
}
if (!fSame)
{
break;
}
}
}
break;
case VT_VECTOR | VT_LPSTR:
if (fSame = (pvar1->calpstr.cElems == pvar2->calpstr.cElems))
{
for (i = 0; i < pvar1->calpstr.cElems; i++)
{
if (pvar1->calpstr.pElems[i] != NULL &&
pvar2->calpstr.pElems[i] != NULL)
{
fSame = strcmp(
pvar1->calpstr.pElems[i],
pvar2->calpstr.pElems[i]) == 0;
}
else
{
fSame = pvar1->calpstr.pElems[i] ==
pvar2->calpstr.pElems[i];
}
if (!fSame)
{
break;
}
}
}
break;
case VT_VECTOR | VT_LPWSTR:
if (fSame = pvar1->calpwstr.cElems == pvar2->calpwstr.cElems)
{
for (i = 0; i < pvar1->calpwstr.cElems; i++)
{
if (pvar1->calpwstr.pElems[i] != NULL &&
pvar2->calpwstr.pElems[i] != NULL)
{
fSame = Prop_wcscmp(
pvar1->calpwstr.pElems[i],
pvar2->calpwstr.pElems[i]) == 0;
}
else
{
fSame = pvar1->calpwstr.pElems[i] ==
pvar2->calpwstr.pElems[i];
}
if (!fSame)
{
break;
}
}
}
break;
case VT_VECTOR | VT_VARIANT:
if (fSame = pvar1->capropvar.cElems == pvar2->capropvar.cElems)
{
for (i = 0; i < pvar1->capropvar.cElems; i++)
{
fSame = RtlCompareVariants(
CodePage,
&pvar1->capropvar.pElems[i],
&pvar2->capropvar.pElems[i]);
if (!fSame)
{
break;
}
}
}
break;
default:
PROPASSERT(!"Invalid type for PROPVARIANT Comparison");
fSame = FALSE;
break;
}
return(fSame);
}