diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/dcomidl | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/dcomidl')
-rw-r--r-- | private/dcomidl/comhndl.h | 52 | ||||
-rw-r--r-- | private/dcomidl/daytona.inc | 39 | ||||
-rw-r--r-- | private/dcomidl/dce.idl | 17 | ||||
-rw-r--r-- | private/dcomidl/iface.idl | 60 | ||||
-rw-r--r-- | private/dcomidl/lclor.acf | 22 | ||||
-rw-r--r-- | private/dcomidl/lclor.idl | 199 | ||||
-rw-r--r-- | private/dcomidl/makefile | 10 | ||||
-rw-r--r-- | private/dcomidl/makefile.inc | 74 | ||||
-rw-r--r-- | private/dcomidl/ntprop.cxx | 409 | ||||
-rw-r--r-- | private/dcomidl/ntpropb.cxx | 2378 | ||||
-rw-r--r-- | private/dcomidl/obase.idl | 266 | ||||
-rw-r--r-- | private/dcomidl/objex.acf | 9 | ||||
-rw-r--r-- | private/dcomidl/objex.idl | 85 | ||||
-rw-r--r-- | private/dcomidl/odeth.acf | 14 | ||||
-rw-r--r-- | private/dcomidl/odeth.idl | 99 | ||||
-rw-r--r-- | private/dcomidl/orcb.acf | 6 | ||||
-rw-r--r-- | private/dcomidl/orcb.idl | 27 | ||||
-rw-r--r-- | private/dcomidl/propstm.cxx | 7971 | ||||
-rw-r--r-- | private/dcomidl/propvar.cxx | 3281 | ||||
-rw-r--r-- | private/dcomidl/remact.acf | 5 | ||||
-rw-r--r-- | private/dcomidl/remact.idl | 65 | ||||
-rw-r--r-- | private/dcomidl/remunk.idl | 61 | ||||
-rw-r--r-- | private/dcomidl/sources | 82 | ||||
-rw-r--r-- | private/dcomidl/stgvarb.cxx | 1206 |
24 files changed, 16437 insertions, 0 deletions
diff --git a/private/dcomidl/comhndl.h b/private/dcomidl/comhndl.h new file mode 100644 index 000000000..b5052418a --- /dev/null +++ b/private/dcomidl/comhndl.h @@ -0,0 +1,52 @@ +//+------------------------------------------------------------------- +// +// File: comhndl.h +// +// Contents: Implicit COM parameters on raw RPCcalls +// +// History: 24 Apr 95 AlexMit Created +// +//-------------------------------------------------------------------- +#ifndef _COMHNDL_H_ +#define _COMHNDL_H_ + +// Define the implicit COM RPC parameters. + +#ifdef RAW + #define COM_HANDLE \ + [in] handle_t rpc, \ + [in, ref] ORPCTHIS *orpcthis, \ + [in, ref] LOCALTHIS *localthis, \ + [out, ref] ORPCTHAT *orpcthat, +#else + #define COM_HANDLE +#endif + +// Define some extra stuff. + +#ifdef DO_NO_IMPORTS + #define IMPORT_OBASE +#else + #define IMPORT_OBASE import "obase.idl"; +#endif + +#ifdef DO_NO_IMPORTS + #define IMPORT_UNKNOWN +#else + #define IMPORT_UNKNOWN import "unknwn.idl"; +#endif + + // These dummy members adjust the procedure number. + // Since these exist on the raw side, the names have to be + // unique in all interfaces. +#ifdef RAW + #define COM_DEFINES(X) \ + IMPORT_OBASE \ + HRESULT DummyQueryInterface##X( COM_HANDLE [in] DWORD dummy ); \ + HRESULT DummyAddRef##X( COM_HANDLE [in] DWORD dummy ); \ + HRESULT DummyRelease##X( COM_HANDLE [in] DWORD dummy ); +#else + #define COM_DEFINES(X) IMPORT_UNKNOWN +#endif + +#endif // _COMHNDL_H_ diff --git a/private/dcomidl/daytona.inc b/private/dcomidl/daytona.inc new file mode 100644 index 000000000..fd33a1c92 --- /dev/null +++ b/private/dcomidl/daytona.inc @@ -0,0 +1,39 @@ +# This is the global include file for the daytona version of CairOLE. +# It is included by all project sources files. + +C_DEFINES= \ + $(C_DEFINES) \ + -DFLAT \ + -DWIN32=100 \ + -D_NT1X_=100 \ + -DUNICODE \ + -D_UNICODE \ + -DINC_OLE2 \ + -DNOEXCEPTIONS \ + -DCAIROLE_DOWNLEVEL \ + $(TRACELOG) + +# DECLSPEC_IMPORT control (see objbase.h) +!if "$(MINORCOMP)"=="com" || "$(MINORCOMP)"=="stg" || "$(MINORCOMP)"=="ole232" +C_DEFINES= \ + $(C_DEFINES) \ + -D_OLE32_ +!endif + + +BLDCRT= 1 + +# For the Daytona build, we do not want statically linked compiler runtimes, +# so leave this commented out. +# +# USE_LIBCMT= 1 + + +USE_CRTDLL=1 + +MSC_WARNING_LEVEL=/W3 /WX + +NTLEGO=1 + +GPCH_BUILD=daytona + diff --git a/private/dcomidl/dce.idl b/private/dcomidl/dce.idl new file mode 100644 index 000000000..c95353ea7 --- /dev/null +++ b/private/dcomidl/dce.idl @@ -0,0 +1,17 @@ +[ + uuid(06ba4670-5275-101b-bbcc-00aa0021347a) +] +interface DceBaseTypes +{ +#pragma midl_echo("#ifndef uuid_t") + + typedef struct tag_uuid_t + { + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; + } uuid_t; + +#pragma midl_echo("#endif // uuid_t") +} diff --git a/private/dcomidl/iface.idl b/private/dcomidl/iface.idl new file mode 100644 index 000000000..5269da0a7 --- /dev/null +++ b/private/dcomidl/iface.idl @@ -0,0 +1,60 @@ +//+--------------------------------------------------------------------------- +// +// Copyright (C) 1992, Microsoft Corporation. +// +// File: iface.idl +// +// Contents: Public definitions used for xmiting interfaces via RPC +// +// History: 28-Jan-93 Ricksa Created. +// +// Notes: These definitions are used for transmitting interfaces +// via RPC. +// +//---------------------------------------------------------------------------- +[ uuid(5C0EB534-BF9F-101A-8818-02608C4D2359), + version(0.1), + pointer_default(unique) +] + +interface XmitDefs +{ + import "wtypes.idl"; + + typedef struct + { + DWORD callcat; // CALLCATEGORY + DWORD dwClientThread; + } LOCALTHIS; + + // CALLCATEGORY is used internally and represents the categories of calls + // that can be made. + + // DCOMWORK - Get rid of the unused internal types. + typedef enum tagCALLCATEGORY + { + CALLCAT_NOCALL = 0, // no call in progress + CALLCAT_SYNCHRONOUS = 1, // normal sychornous call + CALLCAT_ASYNC = 2, // asynchronous call + CALLCAT_INPUTSYNC = 3, // input-synchronous call + CALLCAT_INTERNALSYNC = 4, // internal ssync call + CALLCAT_INTERNALINPUTSYNC = 5, // internal inputssync call + CALLCAT_SCMCALL = 6 // important scm call + } CALLCATEGORY; + + + // wire representation of an entire interface. used for wrapping a + // marshalled interface in a stream representation CXmitRpcStream. + typedef struct tagInterfaceData + { + ULONG ulCntData; // size of data + [length_is(ulCntData)] BYTE abData[1024]; // data BUGBUG: sizeis() + } InterfaceData; + + typedef [unique] InterfaceData * PInterfaceData; + +#pragma midl_echo("// BUGBUG: until the length_is midl option is fixed, we ") +#pragma midl_echo("// have a different computation for the size of the IFD.") +#pragma midl_echo("#define IFD_SIZE(pIFD) (sizeof(InterfaceData) + pIFD->ulCntData - 1024)") +} +
\ No newline at end of file diff --git a/private/dcomidl/lclor.acf b/private/dcomidl/lclor.acf new file mode 100644 index 000000000..cc726a251 --- /dev/null +++ b/private/dcomidl/lclor.acf @@ -0,0 +1,22 @@ +interface ILocalObjectExporter +{ + typedef [allocate(dont_free)] STATIC_ARRAY; + typedef [allocate(dont_free)] STATIC_BYTE_ARRAY; + typedef [allocate(dont_free)] STATIC_STRING; + + [comm_status, fault_status] Connect(); + + [comm_status, fault_status] AllocateReservedIds(); + + [comm_status, fault_status] BulkUpdateOIDs(); + + [comm_status, fault_status] ClientResolveOXID(); + + [comm_status, fault_status] ServerAllocateOXIDAndOIDs(); + + [comm_status, fault_status] ServerAllocateOIDs(); + + [comm_status, fault_status] ServerFreeOXIDAndOIDs(); +} + + diff --git a/private/dcomidl/lclor.idl b/private/dcomidl/lclor.idl new file mode 100644 index 000000000..c5d17f81c --- /dev/null +++ b/private/dcomidl/lclor.idl @@ -0,0 +1,199 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1995. +// +// File: lclor.idl +// +// Synopsis: This is the local interface to the object exporter. +// +// Clients can +// Register object ids to be pinged. +// Deregister object ids that no longer need to be pinged. +// Lookup an OXID and register an object id for that OXID. +// +// Servers can +// Register their OXID. +// Register an object id so they receive a death notification. +// +//-------------------------------------------------------------------------- +[ + uuid(e60c73e6-88f9-11cf-9af1-0020af6e72f4), + version(2.0) +] + +interface ILocalObjectExporter +{ + import "obase.idl"; + + typedef [context_handle] void *PHPROCESS; + + ////////////////////////////////////////////////// + ////////// Shared client and server APIs + + // Client and Server both call this to establish a context handle + // in the local oxid resolver/ping process. + + typedef USHORT *STATIC_ARRAY; + typedef unsigned char *STATIC_BYTE_ARRAY; + typedef [string] wchar_t *STATIC_STRING; + + // Flags for use in Connect. + const unsigned long CONNECT_DISABLEDCOM = 0x1; + const unsigned long CONNECT_MUTUALAUTH = 0x2; + const unsigned long CONNECT_SECUREREF = 0x4; + + error_status_t Connect + ( + [in] handle_t hServer, + [out] PHPROCESS *pProcess, + [out] ULONG *pdwTimeoutInSeconds, + [out] DUALSTRINGARRAY **ppdsaOrBindings, + [out] MID *pLocalMid, + [in] long cIdsToReserve, + [out, ref] ID *pidReservedBase, + [out] DWORD *pfConnectFlags, + [out] STATIC_STRING *pLegacySecurity, + [out] DWORD *pAuthnLevel, + [out] DWORD *pImpLevel, + [out] DWORD *pcServerSvc, + [out, size_is(,*pcServerSvc)] STATIC_ARRAY *aServerSvc, + [out] DWORD *pcClientSvc, + [out, size_is(,*pcClientSvc)] STATIC_ARRAY *aClientSvc, + [out] DWORD * pProcessID, + [out] DWORD * pScmProcessID, + [out] DWORD * pSignature + ); + + // Called by local clients to reserve a range of IDs which will + // not conflict with any other local IDs. + error_status_t AllocateReservedIds + ( + [in] handle_t hServer, + [in] long cIdsToReserve, // Max of ten will be returned + [out, ref] ID *pidReservedBase + ); + + // Clients and server of call this to update the state of OID + // they are using. + // Clients may add OIDs to the set of OID in use by their process + // Clients may remove OIDs from those in use by their process + // Servers may free OIDs they allocated but are no longer using. + // Clients may free OXIDs they are no longer using. + typedef struct + { + MID mid; + OXID oxid; + unsigned long refs; + } OXID_REF; + + typedef struct + { + MID mid; + OID oid; + } OID_MID_PAIR; + + typedef struct + { + MID mid; + OXID oxid; + OID oid; + } OXID_OID_PAIR; + + // Returned if at least one OID was not successful added/removed. + const long OR_PARTIAL_UPDATE = 1003L; + + error_status_t + BulkUpdateOIDs + ( + [in] handle_t hServer, + [in] PHPROCESS phProcess, + + [in] unsigned long cOidsToBeAdded, // can be zero + [in, size_is(cOidsToBeAdded)] OXID_OID_PAIR aOidsToBeAdded[], + [out, size_is(cOidsToBeAdded)] long aStatusOfAdds[], + + [in] unsigned long cOidsToBeRemoved, // can be zero + [in, size_is(cOidsToBeRemoved)] OID_MID_PAIR aOidsToBeRemoved[], + + [in] unsigned long cServerOidsToFree, // can be zero + [in, size_is(cServerOidsToFree)] OID aServerOids[], + + [in] unsigned long cOxidsToFree, // can be zero + [in, size_is(cOxidsToFree)] OXID_REF aOxidsToFree[] + ); + + ////////////////////////////////////////////////// + ////////// Client specific APIs + + // Clients to remote OXIDs call this to lookup the OXID_INFO for an oxid. + + // psaRemoteOrBindings - compressed string bindings from the long + // form of the marshalled interface (if any). + + // pwstrBindingToServer - expanded string binding to the client which + // passed the IN interface pointer to the server. + + error_status_t + ClientResolveOXID + ( + [in] handle_t hServer, + [in] PHPROCESS phProcess, + [in, ref] OXID *poxidServer, + // [in, ref] MID *pMidHint, + [in, unique] DUALSTRINGARRAY *pssaServerObjectResolverBindings, + [in] long fApartment, + [out, ref] OXID_INFO *poxidInfo, // Contains a single expanded stringbinding + security bindings + [out] MID *pLocalMidOfRemote + ); + + ////////////////////////////////////////////////// + //////////////// Server specific APIs + + // Called to register an OXID and associated information. The server may + // simultaneously register a number of OIDs. + + error_status_t + ServerAllocateOXIDAndOIDs + ( + [in] handle_t hServer, + [in] PHPROCESS phProcess, + [out, ref] OXID *poxidServer, + [in] long fApartment, + [in] unsigned long cOids, + [out, size_is(cOids)] OID aOid[], + [out] unsigned long *pcOidsAllocated, + [in, ref] OXID_INFO *poxidInfo, // no strings + [in, unique] DUALSTRINGARRAY *pdsaStringBindings, // Expanded, NULL if not changed + [in, unique] DUALSTRINGARRAY *pdsaSecurityBindings // Compressed, NULL if not first OXID for process. + ); + + + // Server calls this to register additional OIDs with the ping server. + error_status_t + ServerAllocateOIDs + ( + [in] handle_t hServer, + [in] PHPROCESS phProcess, + [in, ref] OXID *poxidServer, + [in] unsigned long cOids, + [out, size_is(cOids)] OID aOid[], + [out] unsigned long *pcOidsAllocated + ); + + // Called when a particular OXID is being removed. This is only + // necessary if the server may continue running but is destroying + // a thead (apartment model) or is unloading com. + // The unused oids are used as a hint for cleaning up object resolver + // faster. + error_status_t ServerFreeOXIDAndOIDs + ( + [in] handle_t hServer, + [in] PHPROCESS phProcess, + [in] OXID oxidServer, + [in] unsigned long cOids, + [in, size_is(cOids)] OID aOids[] + ); +} + + diff --git a/private/dcomidl/makefile b/private/dcomidl/makefile new file mode 100644 index 000000000..1d3728d41 --- /dev/null +++ b/private/dcomidl/makefile @@ -0,0 +1,10 @@ +############################################################################ +# +# Copyright (C) 1992, Microsoft Corporation. +# +# All rights reserved. +# +############################################################################ + +!include $(NTMAKEENV)\makefile.def + diff --git a/private/dcomidl/makefile.inc b/private/dcomidl/makefile.inc new file mode 100644 index 000000000..0a5789c10 --- /dev/null +++ b/private/dcomidl/makefile.inc @@ -0,0 +1,74 @@ +!ifndef MIDL +MIDL = midl.exe +!endif + +MIDL_FLAGS= \ + -Zp8 \ + -I$(INCLUDES) \ + -Oi2 \ + -oldnames \ + -char unsigned \ + -error allocation \ + -error bounds_check \ + -error stub_data \ + -ms_ext -c_ext \ + -DMIDL_PASS \ + $(C_DEFINES) \ + -cpp_cmd $(TARGET_CPP) \ + -DMIDL_PASS $(C_DEFINES) -I$(INCLUDES) + +SSWITCH=-prefix sstub _ + +obj\dce.h: dce.idl + $(MIDL) $(MIDL_FLAGS) -header obj\dce.h dce.idl + +obj\obase.h: obase.idl + $(MIDL) $(MIDL_FLAGS) -header obj\obase.h obase.idl + +obj\objex.h: objex.idl obase.idl objex.acf + $(MIDL) $(SSWITCH) $(MIDL_FLAGS) -header obj\objex.h objex.idl + +obj\remact.h: remact.idl obase.idl remact.acf + $(MIDL) $(SSWITCH) $(MIDL_FLAGS) -header obj\remact.h remact.idl + +obj\lclor.h: lclor.idl obase.idl lclor.acf + $(MIDL) $(SSWITCH) $(MIDL_FLAGS) -header obj\lclor.h lclor.idl + +obj\orcb.h: orcb.idl obase.idl orcb.acf + $(MIDL) $(SSWITCH) $(MIDL_FLAGS) -header obj\orcb.h orcb.idl + +# first MIDL invocation makes the object header +# the object proxy and stub are made with mega.idl +# second MIDL invocation makes the server and the '_' prefixed client +obj\odeth.h : odeth.idl iface.idl obase.idl remunk.idl comhndl.h odeth.acf + $(MIDL) $(MIDL_FLAGS) -header obj\odeth.h \ + -client none -server none odeth.idl + +obj\rawodeth.h : odeth.idl iface.idl obase.idl remunk.idl comhndl.h odeth.acf + $(MIDL) $(MIDL_FLAGS) -header obj\rawodeth.h \ + $(SSWITCH) -D RAW odeth.idl + +# only a header file generated +obj\iface.h: iface.idl + $(MIDL) $(MIDL_FLAGS) -header obj\iface.h iface.idl + +obj\remunk.h : remunk.idl obase.idl + $(MIDL) $(MIDL_FLAGS) -header obj\remunk.h remunk.idl + + +DEST_TREE=daytona + +allidl: obj\dce.h obj\obase.h obj\objex.h obj\remact.h obj\lclor.h \ + obj\odeth.h obj\rawodeth.h obj\iface.h obj\orcb.h obj\remunk.h + +clean: + -erase obj\dce.h >NUL 2>NUL + -erase obj\obase.h >NUL 2>NUL + -erase obj\lclor.h >NUL 2>NUL + -erase obj\objex.h >NUL 2>NUL + -erase obj\remact.h >NUL 2>NUL + -erase obj\odeth.h >NUL 2>NUL + -erase obj\rawodeth.h >NUL 2>NUL + -erase obj\remunk.h >NUL 2>NUL + -erase obj\iface.h >NUL 2>NUL + -erase obj\orcb.h >NUL 2>NUL diff --git a/private/dcomidl/ntprop.cxx b/private/dcomidl/ntprop.cxx new file mode 100644 index 000000000..451eff4d9 --- /dev/null +++ b/private/dcomidl/ntprop.cxx @@ -0,0 +1,409 @@ +//+-------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1993 +// +// File: ntprop.cxx +// +// Contents: OLE Appendix B property set support. +// +// History: 28-Nov-94 vich created +// 15-Jul-96 MikeHill - PropSetNames: WCHAR=>OLECHAR, byte-swapping. +// - Added special-cases for PictureIt! propsets. +// +//--------------------------------------------------------------------------- + +#include <pch.cxx> +#include <olechar.h> + +//#include <iofs.h> +//#include <stgprop.h> + +// These optionally-compiled directives tell the compiler & debugger +// where the real file, rather than the copy, is located. +#ifdef _ORIG_FILE_LOCATION_ +#if __LINE__ != 25 +#error File heading has change size +#else +#line 29 "\\nt\\private\\dcomidl\\ntprop.cxx" +#endif +#endif + +#define CCH_MAP (1 << CBIT_CHARMASK) // 32 +#define CHARMASK (CCH_MAP - 1) // 0x1f + +// we use static array instead of string literals because some systems +// have 4 bytes string literals, and would not produce the correct result +// for REF's 2 byte Unicode convention +// +OLECHAR aocMap[CCH_MAP + 1] = {'a','b','c','d','e','f','g', + 'h','i','j','k','l','m','n', + 'o','p','q','r','s','t','u', + 'v','w','x','y','z', + '0','1','2','3','4','5','\0'}; + +#define CALPHACHARS (1 + (OLECHAR)'z' - (OLECHAR)'a') + +GUID guidSummary = + { 0xf29f85e0, + 0x4ff9, 0x1068, + { 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9 } }; + +OLECHAR oszSummary[] = {'S','u','m','m','a','r','y', + 'I','n','f','o','r','m','a','t','i','o','n','\0'}; + +GUID guidDocumentSummary = + { 0xd5cdd502, + 0x2e9c, 0x101b, + { 0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae } }; + +OLECHAR oszDocumentSummary[] = {'D','o','c','u','m','e','n','t', + 'S','u','m','m','a','r','y', + 'I','n','f','o','r','m','a','t','i','o','n', + '\0'}; + +// Note that user defined properties are placed in section 2 with the below +// GUID as the FMTID -- alas, we did not expect Office95 to actually use it. + +GUID guidDocumentSummarySection2 = + { 0xd5cdd505, + 0x2e9c, 0x101b, + { 0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae } }; + +// *Global Info* + +OLECHAR oszGlobalInfo[] = {'G','l','o','b','a','l',' ','I','n','f','o','\0'}; + +GUID guidGlobalInfo = + { 0x56616F00, + 0xC154, 0x11ce, + { 0x85, 0x53, 0x00, 0xAA, 0x00, 0xA1, 0xF9, 0x5B } }; + +// *Image Contents* + +OLECHAR oszImageContents[] = {'I','m','a','g','e',' ', + 'C','o','n','t','e','n','t','s','\0'}; + +GUID guidImageContents = + { 0x56616400, + 0xC154, 0x11ce, + { 0x85, 0x53, 0x00, 0xAA, 0x00, 0xA1, 0xF9, 0x5B } }; + +// *Image Info* + +OLECHAR oszImageInfo[] = {'I','m','a','g','e',' ','I','n','f','o','\0'}; + +GUID guidImageInfo = + { 0x56616500, + 0xC154, 0x11ce, + { 0x85, 0x53, 0x00, 0xAA, 0x00, 0xA1, 0xF9, 0x5B } }; + + +__inline OLECHAR +MapChar(IN ULONG i) +{ + return((OLECHAR) aocMap[i & CHARMASK]); +} + + +//+-------------------------------------------------------------------------- +// Function: RtlGuidToPropertySetName +// +// Synopsis: Map property set GUID to null-terminated UNICODE name string. +// +// The awcname parameter is assumed to be a buffer with room for +// CWC_PROPSETSZ (28) UNICODE characters. The first character +// is always WC_PROPSET0 (0x05), as specified by the OLE Appendix +// B documentation. The colon character normally used as an NT +// stream name separator is not written to the caller's buffer. +// +// No error is possible. +// +// Arguments: IN GUID *pguid -- pointer to GUID to convert +// OUT OLECHAR aocname[] -- output string buffer +// +// Returns: count of non-NULL characters in the output string buffer +//--------------------------------------------------------------------------- + +ULONG PROPSYSAPI PROPAPI +RtlGuidToPropertySetName( + IN GUID const *pguid, + OUT OLECHAR aocname[]) +{ + ULONG cbitRemain = CBIT_BYTE; + OLECHAR *poc = aocname; + + BYTE *pb; + BYTE *pbEnd; + + *poc++ = OC_PROPSET0; + + // ----------------------- + // Check for special-cases + // ----------------------- + + // Note: CCH_PROPSET includes the OC_PROPSET0, and sizeof(osz...) + // includes the trailing '\0', so sizeof(osz...) is ok because the + // OC_PROPSET0 character compensates for the trailing NULL character. + + // Is this the SummaryInformation propset? + PROPASSERT(CCH_PROPSET >= sizeof(oszSummary)/sizeof(OLECHAR)); + + if (*pguid == guidSummary) + { + RtlCopyMemory(poc, oszSummary, sizeof(oszSummary)); + return(sizeof(oszSummary)/sizeof(OLECHAR)); + } + + // Is this The DocumentSummaryInformation or User-Defined propset? + PROPASSERT(CCH_PROPSET >= sizeof(oszDocumentSummary)/sizeof(OLECHAR)); + + if (*pguid == guidDocumentSummary || *pguid == guidDocumentSummarySection2) + { + RtlCopyMemory(poc, oszDocumentSummary, sizeof(oszDocumentSummary)); + return(sizeof(oszDocumentSummary)/sizeof(OLECHAR)); + } + + // Is this the Global Info propset? + PROPASSERT(CCH_PROPSET >= sizeof(oszGlobalInfo)/sizeof(OLECHAR)); + if (*pguid == guidGlobalInfo) + { + RtlCopyMemory(poc, oszGlobalInfo, sizeof(oszGlobalInfo)); + return(sizeof(oszGlobalInfo)/sizeof(OLECHAR)); + } + + // Is this the Image Contents propset? + PROPASSERT(CCH_PROPSET >= sizeof(oszImageContents)/sizeof(OLECHAR)); + if (*pguid == guidImageContents) + { + RtlCopyMemory(poc, oszImageContents, sizeof(oszImageContents)); + return(sizeof(oszImageContents)/sizeof(OLECHAR)); + } + + // Is this the Image Info propset? + PROPASSERT(CCH_PROPSET >= sizeof(oszImageInfo)/sizeof(OLECHAR)); + if (*pguid == guidImageInfo) + { + RtlCopyMemory(poc, oszImageInfo, sizeof(oszImageInfo)); + return(sizeof(oszImageInfo)/sizeof(OLECHAR)); + } + + + // ------------------------------ + // Calculate the string-ized GUID + // ------------------------------ + + // If this is a big-endian system, we need to convert + // the GUID to little-endian for the conversion. + +#if BIGENDIAN + GUID guidByteSwapped = *pguid; + PropByteSwap( &guidByteSwapped ); + pguid = &guidByteSwapped; +#endif + + // Point to the beginning and ending of the GUID + pb = (BYTE*) pguid; + pbEnd = pb + sizeof(*pguid); + + // Walk 'pb' through each byte of the GUID. + + while (pb < pbEnd) + { + ULONG i = *pb >> (CBIT_BYTE - cbitRemain); + + if (cbitRemain >= CBIT_CHARMASK) + { + *poc = MapChar(i); + if (cbitRemain == CBIT_BYTE && *poc >= (OLECHAR)'a' + && *poc <= ((OLECHAR)'z')) + { + *poc += (OLECHAR) ( ((OLECHAR)'A') - ((OLECHAR)'a') ); + } + poc++; + cbitRemain -= CBIT_CHARMASK; + if (cbitRemain == 0) + { + pb++; + cbitRemain = CBIT_BYTE; + } + } + else + { + if (++pb < pbEnd) + { + i |= *pb << cbitRemain; + } + *poc++ = MapChar(i); + cbitRemain += CBIT_BYTE - CBIT_CHARMASK; + } + } // while (pb < pbEnd) + + *poc = OLESTR( '\0' ); + return(CCH_PROPSET); + +} + + +//+-------------------------------------------------------------------------- +// Function: RtlPropertySetNameToGuid +// +// Synopsis: Map non null-terminated UNICODE string to a property set GUID. +// +// If the name is not properly formed as per +// RtlGuidToPropertySetName(), STATUS_INVALID_PARAMETER is +// returned. The pguid parameter is assumed to point to a buffer +// with room for a GUID structure. +// +// Arguments: IN ULONG cocname -- count of OLECHARs in string to convert +// IN OLECHAR aocname[] -- input string to convert +// OUT GUID *pguid -- pointer to buffer for converted GUID +// +// Returns: NTSTATUS +//--------------------------------------------------------------------------- + +NTSTATUS PROPSYSAPI PROPAPI +RtlPropertySetNameToGuid( + IN ULONG cocname, + IN OLECHAR const aocname[], + OUT GUID *pguid) +{ + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + OLECHAR const *poc = aocname; + + if (poc[0] == OC_PROPSET0) + { + // ----------------------- + // Check for Special-Cases + // ----------------------- + + // Note: cocname includes the OC_PROPSET0, and sizeof(osz...) + // includes the trailing OLESTR('\0'), but the comparison excludes both + // the leading OC_PROPSET0 and the trailing '\0'. + + // Is this SummaryInformation? + if (cocname == sizeof(oszSummary)/sizeof(OLECHAR) && + ocsnicmp(&poc[1], oszSummary, cocname - 1) == 0) + { + *pguid = guidSummary; + return(STATUS_SUCCESS); + } + + // Is this DocumentSummaryInformation? + if (cocname == sizeof(oszDocumentSummary)/sizeof(OLECHAR) && + ocsnicmp(&poc[1], oszDocumentSummary, cocname - 1) == 0) + { + *pguid = guidDocumentSummary; + return(STATUS_SUCCESS); + } + + // Is this Global Info? + if (cocname == sizeof(oszGlobalInfo)/sizeof(OLECHAR) && + ocsnicmp(&poc[1], oszGlobalInfo, cocname - 1) == 0) + { + *pguid = guidGlobalInfo; + return(STATUS_SUCCESS); + } + + // Is this Image Info? + if (cocname == sizeof(oszImageInfo)/sizeof(OLECHAR) && + ocsnicmp(&poc[1], oszImageInfo, cocname - 1) == 0) + { + *pguid = guidImageInfo; + return(STATUS_SUCCESS); + } + + // Is this Image Contents? + if (cocname == sizeof(oszImageContents)/sizeof(OLECHAR) && + ocsnicmp(&poc[1], oszImageContents, cocname - 1) == 0) + { + *pguid = guidImageContents; + return(STATUS_SUCCESS); + } + + // ------------------ + // Calculate the GUID + // ------------------ + + // None of the special-cases hit, so we must calculate + // the GUID from the name. + + if (cocname == CCH_PROPSET) + { + ULONG cbit; + BYTE *pb = (BYTE *) pguid - 1; + + RtlZeroMemory(pguid, sizeof(*pguid)); + for (cbit = 0; cbit < CBIT_GUID; cbit += CBIT_CHARMASK) + { + ULONG cbitUsed = cbit % CBIT_BYTE; + ULONG cbitStored; + OLECHAR oc; + + if (cbitUsed == 0) + { + pb++; + } + + oc = *++poc - (OLECHAR)'A'; // assume upper case + // for wchar (unsigned) -ve values becomes a large number + // but for char, which is signed, -ve is -ve + if (oc > CALPHACHARS || oc < 0) + { + // oops, try lower case + oc += (OLECHAR) ( ((OLECHAR)'A') - ((OLECHAR)'a')); + if (oc > CALPHACHARS || oc < 0) + { + // must be a digit + oc += ((OLECHAR)'a') - ((OLECHAR)'0') + CALPHACHARS; + if (oc > CHARMASK) + { + goto Exit; // invalid character + } + } + } + *pb |= (BYTE) (oc << cbitUsed); + + cbitStored = min(CBIT_BYTE - cbitUsed, CBIT_CHARMASK); + + // If the translated bits wouldn't all fit in the current byte + + if (cbitStored < CBIT_CHARMASK) + { + oc >>= CBIT_BYTE - cbitUsed; + + if (cbit + cbitStored == CBIT_GUID) + { + if (oc != 0) + { + goto Exit; // extra bits + } + break; + } + pb++; + + *pb |= (BYTE) oc; + } + } // for (cbit = 0; cbit < CBIT_GUID; cbit += CBIT_CHARMASK) + + Status = STATUS_SUCCESS; + + // If byte-swapping is necessary, do so now on the calculated + // GUID. + + PropByteSwap( pguid ); + + } // if (cocname == CCH_PROPSET) + } // if (poc[0] == OC_PROPSET0) + + + // ---- + // Exit + // ---- + +Exit: + + return(Status); +} + diff --git a/private/dcomidl/ntpropb.cxx b/private/dcomidl/ntpropb.cxx new file mode 100644 index 000000000..ce7cffd81 --- /dev/null +++ b/private/dcomidl/ntpropb.cxx @@ -0,0 +1,2378 @@ +//+-------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1994 +// +// File: ntpropb.cxx +// +// Contents: Nt property set implementation based on OLE Appendix B. +// +// History: 5-Dec-94 vich created +// 09-May-96 MikeHill Use the 'boolVal' member of PropVariant, +// rather than the member named 'bool' +// (which is a reserved keyword). +// 22-May-96 MikeHill - Get the OSVersion during a CreatePropSet. +// - Let CPropSetStm allocate prop name buffers. +// 07-Jun-96 MikeHill - Correct ClipData.cbSize to include +// sizeof(ulClipFmt). +// - Removed unnecessary Flushes. +// - Take the psstm lock on RtlClosePropSet. +// 12-Jun-96 MikeHill - Fix locking in RtlClosePropertySet. +// - VT_I1 support (under ifdefs) +// 25-Jul-96 MikeHill - Removed Win32 SEH. +// - BSTRs & prop names: WCHAR => OLECHAR. +// - Added RtlOnMappedStreamEvent +// - Enabled for use in "iprop.dll". +// 08-Nov-96 MikeHIll Corrected new/delete implementation. +// +//--------------------------------------------------------------------------- + +#include <pch.cxx> +#include "propvar.h" +#include <olechar.h> + +// These optionally-compiled directives tell the compiler & debugger +// where the real file, rather than the copy, is located. +#ifdef _ORIG_FILE_LOCATION_ +#if __LINE__ != 36 +#error File heading has change size +#else +#line 40 "\\nt\\private\\dcomidl\\ntpropb.cxx" +#endif +#endif + + +#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 + +extern "C" UNICODECALLOUTS UnicodeCallouts = +{ +#ifdef WINNT + NULL +#else + WIN32_UNICODECALLOUTS +#endif +}; + + +#if defined(WINNT) && !defined(IPROPERTY_DLL) + +GUID guidStorage = PSGUID_STORAGE; + +// ----------- Allocation routines are not used on X86 DosWindows Platform --- + +//+--------------------------------------------------------------------------- +// Function: new, public +// +// Synopsis: Allocate memory +// +// Arguments: [cb] -- size +// +// Returns: pointer to memory, NULL if not available +//--------------------------------------------------------------------------- + + +void* _CRTAPI1 +operator new(size_t cb) +{ +#if DBG + cAlloc++; +#endif + return(RtlAllocateHeap(RtlProcessHeap(), 0, cb)); +} + +//+--------------------------------------------------------------------------- +// Function: delete, public +// +// Synopsis: Free memory +// +// Arguments: [pv] -- pointer to memory to be freed +// +// Returns: pointer to memory, NULL if not available +//--------------------------------------------------------------------------- + +void _CRTAPI1 +operator delete(void *pv) +{ +#if DBG + cFree++; +#endif + RtlFreeHeap(RtlProcessHeap(), 0, pv); +} + +#endif // #if defined(WINNT) && !defined(IPROPERTY_DLL) + + +//+--------------------------------------------------------------------------- +// Function: UnLock, private +// +// Synopsis: Unlock a PropertySetStream, and return the +// more severe of two NTSTATUSs; the result of +// the Unlock, or the one passed in by the caller. +// +// Arguments: [ppsstm] -- The CPropertySetStream to unlock +// [Status] -- NTSTATUS +// +// Returns: NTSTATUS +//--------------------------------------------------------------------------- + +inline NTSTATUS +Unlock( CPropertySetStream *ppsstm, NTSTATUS Status ) +{ + NTSTATUS StatusT = ppsstm->Unlock(); + + // Note that the statement below preserves + // success codes in the original Status unless + // there was an error in the Unlock. + + if( NT_SUCCESS(Status) && !NT_SUCCESS(StatusT) ) + Status = StatusT; + + return( Status ); +} + +//+--------------------------------------------------------------------------- +// 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: RtlCreateMappedStream, public +// +// Synopsis: Allocate and initialize a property set context +// +// Arguments: [h] -- property set handle +// [Flags] -- [CMS_WRITE] | [CMS_TRANSACTED] +// [pms] -- pointer to returned mapped stream context +// +// Returns: Status code +//--------------------------------------------------------------------------- + +#ifdef _CAIRO_ +NTSTATUS PROPSYSAPI PROPAPI +RtlCreateMappedStream( + IN HANDLE h, // property set handle + IN ULONG Flags, // [CMS_WRITE] | [CMS_TRANSACTED] + OUT NTMAPPEDSTREAM *pms) // pointer to return mapped stream +{ + NTSTATUS Status = STATUS_INVALID_PARAMETER; + + DebugTrace(0, Dbg, ( + "RtlCreateMappedStream(h=%x, %s%s)\n", + h, + (Flags & CMS_WRITE)? "Write" : "Read", + (Flags & CMS_TRANSACTED)? " Transacted" : "")); + if ((Flags & ~(CMS_WRITE | CMS_TRANSACTED)) == 0) + { + *pms = NULL; + __try + { + Status = STATUS_SUCCESS; + *pms = (NTMAPPEDSTREAM) new CNtMappedStream(h, Flags); + if (*pms == NULL) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } +#if DBGPROP + else if (DebugLevel & (Dbg | DEBTRACE_CREATESTREAM)) + { +#define CB_NAMEINFOx (sizeof(FILE_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR)) + FILE_NAME_INFORMATION *pfni; + LONGLONG fni[QuadAlign(CB_NAMEINFOx)/sizeof(LONGLONG)]; + IO_STATUS_BLOCK isb; + + pfni = (FILE_NAME_INFORMATION *) fni; + Status = NtQueryInformationFile( + h, + &isb, + pfni, + sizeof(fni), + FileNameInformation); + if (NT_SUCCESS(Status)) + { + DebugTrace(0, Dbg | DEBTRACE_CREATESTREAM, ( + "%x.%x: RtlCreateMappedStream(%.*ws, %s%s) ==> ms=%x, s=%x\n", + (ULONG) NtCurrentTeb()->ClientId.UniqueProcess, + (ULONG) NtCurrentTeb()->ClientId.UniqueThread, + pfni->FileNameLength/sizeof(WCHAR), + pfni->FileName, + (Flags & CMS_WRITE)? "Write" : "Read", + (Flags & CMS_TRANSACTED)? " Transacted" : "", + *pms, + Status)); + } + } +#endif // if DBGPROP + } + __except(ExceptionFilter(GetExceptionInformation())) + { + Status = GetExceptionCode(); + PROPASSERT(!NT_SUCCESS(Status)); + DebugTrace(0, DEBTRACE_ERROR, ( + "RtlCreateMappedStream() ==> except=%x\n", Status)); + } + } + DebugTrace(0, DbgS(Status), ( + "RtlCreateMappedStream() ==> ms=%x, s=%x\n--------\n", + *pms, + Status)); + return(Status); +} +#endif // ifdef _CAIRO_ + + +//+--------------------------------------------------------------------------- +// Function: RtlCloseMappedStream, public +// +// Synopsis: Delete an Nt Mapped Stream +// +// Arguments: [ms] -- Nt Mapped Stream +// +// Returns: Status code +//--------------------------------------------------------------------------- + +#ifdef _CAIRO_ +NTSTATUS PROPSYSAPI PROPAPI +RtlCloseMappedStream( + IN NTMAPPEDSTREAM ms) // Nt Mapped Stream +{ + NTSTATUS Status = STATUS_SUCCESS; + + DebugTrace(0, Dbg, ("RtlCloseMappedStream(ms=%x)\n", ms)); + __try + { + CNtMappedStream *pmstm = (CNtMappedStream *) ms; + + PROPASSERT(pmstm->IsNtMappedStream()); + delete pmstm; + } + __except(ExceptionFilter(GetExceptionInformation())) + { + Status = GetExceptionCode(); + PROPASSERT(!NT_SUCCESS(Status)); + DebugTrace(0, DEBTRACE_ERROR, ( + "RtlCloseMappedStream() ==> except=%x\n", Status)); + } + DebugTrace(0, DbgS(Status), ("RtlCloseMappedStream() ==> s=%x\n--------\n", Status)); + return(Status); +} +#endif // ifdef _CAIRO_ + + +//+--------------------------------------------------------------------------- +// 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 fLocked = FALSE; + 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: + +#if defined(WINNT) && !defined(IPROPERTY_DLL) + if (pguid != NULL && + RtlCompareMemory( + (VOID *) pguid, // BUGBUG: const + &guidStorage, + sizeof(guidStorage)) == sizeof(guidStorage)) + { + DebugTrace(0, DbgS(Status), ( + "RtlCreatePropertySet(%x, guid=guidStorage) ==> OBSOLETE!\n", + ms)); + goto Exit; + } + if (pclsid != NULL && + RtlCompareMemory( + (VOID *) pclsid, // BUGBUG: const + &guidStorage, + sizeof(guidStorage)) == sizeof(guidStorage)) + { + DebugTrace(0, DbgS(Status), ( + "RtlCreatePropertySet(%x, clsid=guidStorage) ==> OBSOLETE!\n", + ms)); + goto Exit; + } + // FALLTHROUGH +#endif + 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; + } + + Status = pmstm->Lock((Flags & CREATEPROP_MODEMASK) != CREATEPROP_READ); + if( !NT_SUCCESS(Status) ) goto Exit; + fLocked = TRUE; + + 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; + +#ifdef _CAIRO_ + ppsstm->Flush(); +#endif + + } + + // ---- + // Exit + // ---- + +Exit: + + if (fLocked) + { + Status = Unlock( ppsstm, Status ); + } + + // If we were successfull with everything, set the + // out-parameters. + + if( NT_SUCCESS(Status) ) + { + // pOSVersion has already been set. + *pCodePage = ppsstm->GetCodePage(); + *pnp = (NTPROP) ppsstm; + } + + // Otherwise, 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 call. + + else if( NULL != ppsstm ) + { + 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; + BOOL fLocked = FALSE; + CPropertySetStream *ppsstm = (CPropertySetStream *) np; + + DebugTrace(0, Dbg, ("RtlClosePropertySet(np=%x)\n", np)); + + // Lock the mapped stream, because this close + // may trigger a Write. + + Status = ppsstm->Lock(TRUE); + if( !NT_SUCCESS(Status) ) goto Exit; + fLocked = TRUE; + + ppsstm->Close(&Status); + if( !NT_SUCCESS(Status) ) goto Exit; + + // ---- + // Exit + // ---- + +Exit: + + if (fLocked) + Status = Unlock( ppsstm, Status ); + + delete ppsstm; + + DebugTrace(0, DbgS(Status), ("RtlClosePropertySet() ==> s=%x\n", Status)); + return(Status); +} + + +//+--------------------------------------------------------------------------- +// 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; + BOOL fLocked = FALSE; + + DebugTrace(0, Dbg, ("RtlFlushPropertySet(np=%x)\n", np)); + + Status = ppsstm->Lock(TRUE); + if( !NT_SUCCESS(Status) ) goto Exit; + fLocked = TRUE; + + 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; + } + +Exit: + + if( fLocked ) + Status = Unlock( ppsstm, Status ); + + DebugTrace(0, DbgS(Status), ("RtlFlushPropertySet() ==> s=%x\n--------\n", Status)); + 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; + + OLECHAR aocName[CCH_MAXPROPNAMESZ]; + ULONG cbName; + + *pstatus = STATUS_SUCCESS; + + PROPASSERT(aprs[iprop].ulKind == PRSPEC_LPWSTR); + poszName = aprs[iprop].lpwstr; + PROPASSERT(IsOLECHARString( poszName, MAXULONG )); + + // Starting with the caller-provided PID, search sequentially + // until we find a PID we can use. + + for (pid = pidStart; ; pid++) + { + ULONG i; + + // 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 the 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. + + cbName = sizeof(aocName); + 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 QueryPropertyNameBuf? + 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) + { + // There was no property value corresponding to this PID. + + 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, &cbName, pstatus)) + +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 +// [pcIndirect] -- output count of indirect properties +// +// Returns: None +// +// Note: If pcIndirect is NULL, +//--------------------------------------------------------------------------- + +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 ULONG *pcIndirect, // output count of indirect properties + OUT NTSTATUS *pstatus ) +{ + *pstatus = STATUS_SUCCESS; + + USHORT CodePage = ppsstm->GetCodePage(); + PROPID pidStart = pidNameFirst; + ULONG iprop; + + if (pcIndirect != NULL) + { + *pcIndirect = 0; + } + + 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) + { + RtlConvertVariantToPropertyNoEH( + &avar[iprop], + CodePage, + NULL, + &cbprop, + pid, + FALSE, + pcIndirect, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + PROPASSERT(cbprop == DwordAlign(cbprop)); + } + apinfo[iprop].cbprop = cbprop; + apinfo[iprop].pid = pid; + } + + // ---- + // Exit + // ---- + +Exit: + + return; +} + + +//+--------------------------------------------------------------------------- +// Function: BuildIndirectIndexArray, private +// +// Synopsis: Set property values for a property set +// +// Arguments: [cprop] -- count of properties in avar +// [cAlloc] -- max count of indirect properties +// [cIndirect] -- count of indirect properties in avar +// [avar] -- array of PROPVARIANTs +// [ppip] -- ptr to ptr to Indirect property structures +// +// Returns: None +//--------------------------------------------------------------------------- + +VOID +BuildIndirectIndexArray( + IN ULONG cprop, // count of properties in avar + IN ULONG cAlloc, // max count of indirect properties + IN ULONG cIndirect, // count of indirect properties in avar + IN PROPVARIANT const avar[],// array of properties+values + OPTIONAL OUT INDIRECTPROPERTY **ppip, // pointer to returned pointer to + // MAXULONG terminated array of Indirect + // properties w/indexes into aprs & avar + OUT NTSTATUS *pstatus) +{ + *pstatus = STATUS_SUCCESS; + + PROPASSERT(cIndirect > 0); + PROPASSERT(cAlloc >= cIndirect); + PROPASSERT(cprop >= cAlloc); + + if (ppip != NULL) + { + INDIRECTPROPERTY *pip; + ULONG iprop; + + if (cprop == 1) + { + pip = (INDIRECTPROPERTY *) ppip; + } + else + { + pip = (INDIRECTPROPERTY *) new INDIRECTPROPERTY[cAlloc + 1]; + if (pip == NULL) + { + *pstatus = STATUS_INSUFFICIENT_RESOURCES; + goto Exit; + } + *ppip = pip; + } + for (iprop = 0; iprop < cprop; iprop++) + { + if (IsIndirectVarType(avar[iprop].vt)) + { + PROPASSERT(cprop == 1 || (ULONG) (pip - *ppip) < cIndirect); + pip->Index = iprop; + pip->poszName = NULL; + pip++; + } + } + if (cprop > 1) + { + pip->Index = MAXULONG; + PROPASSERT((ULONG) (pip - *ppip) == cIndirect); + } + } + + // ---- + // 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 +// [ppip] -- ptr to ptr to Indirect property structures +// [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 OUT INDIRECTPROPERTY **ppip, // pointer to returned pointer to + // MAXULONG terminated array of Indirect + // properties w/indexes into aprs & avar + OPTIONAL IN PROPVARIANT const avar[]) // array of properties+values +{ + CPropertySetStream *ppsstm = (CPropertySetStream *) np; + NTSTATUS Status = STATUS_SUCCESS; + BOOL fLocked = FALSE; + + PROPERTY_INFORMATION apinfoStack[6]; + PROPERTY_INFORMATION *apinfo = apinfoStack; + ULONG cIndirect = 0; + + DebugTrace(0, Dbg, ( + "RtlSetProperties(np=%x, cprop=%x, pidNameFirst=%x, aprs=%x, apid=%x, ppip=%x)\n", + np, + cprop, + pidNameFirst, + aprs, + apid, + ppip)); + + // Initialize the INDIRECTPROPERTY structure. + + if (ppip != NULL) + { + *ppip = NULL; + + // If cprop is 1, ppip is actually pip (one level + // of indirection). + + if (cprop == 1) + { + // Default the index. + ((INDIRECTPROPERTY *) ppip)->Index = MAXULONG; + } + } + + // Lock the property set. + Status = ppsstm->Lock(TRUE); + if( !NT_SUCCESS(Status) ) goto Exit; + fLocked = TRUE; + + // 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, + ppip == NULL? NULL : &cIndirect, + &Status); + if( !NT_SUCCESS(Status) ) goto Exit; + + // If the caller wants to know about indirect streams and + // storages (and if there were any), allocate memory for a + // MAXULONG terminated array of indexes to the indirect + // variant structures, and fill it in. + + ppsstm->SetValue(cprop, ppip, 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 (apinfo != apinfoStack) + { + delete [] apinfo; + } + + if (!NT_SUCCESS(Status)) + { + if (ppip != NULL) + { + if (cprop == 1) + { + ((INDIRECTPROPERTY *) ppip)->Index = MAXULONG; + } + else if (*ppip != NULL) + { + delete [] *ppip; + *ppip = NULL; + } + } + } + + if (fLocked) + Status = Unlock( ppsstm, Status ); + + DebugTrace(0, DbgS(Status), ( + "RtlSetProperties() ==> *ppip=%x, s=%x\n--------\n", + ppip == NULL? NULL : *ppip, + 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 +// [ppip] -- ptr to ptr to Indirect property structures +// [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 + OPTIONAL OUT INDIRECTPROPERTY **ppip, // pointer to returned pointer to + // MAXULONG terminated array of Indirect + // properties w/indexes into aprs & avar + 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 cIndirect = 0; + ULONG iprop; + BOOL fLocked = FALSE; + + DebugTrace(0, Dbg, ( + "RtlQueryProperties(np=%x, cprop=%x, aprs=%x, apid=%x, ppip=%x)\n", + np, + cprop, + aprs, + apid, + ppip)); + + // Initialize the variant array enough to allow it to be cleaned up + // by the caller (even on partial failure). + + *pcpropFound = 0; + if (ppip != NULL) + { + *ppip = NULL; + if (cprop == 1) + { + ((INDIRECTPROPERTY *) ppip)->Index = MAXULONG; + } + } + + // 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])); + + + Status = ppsstm->Lock(FALSE); + if( !NT_SUCCESS(Status) ) goto Exit; + fLocked = TRUE; + + 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++) + { + PROPID pid; + ULONG cbprop; + + switch(aprs[iprop].ulKind) + { + case PRSPEC_LPWSTR: + pid = ppsstm->QueryPropid(aprs[iprop].lpwstr, &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) + { + BOOL fIndirect; + + (*pcpropFound)++; + fIndirect = RtlConvertPropertyToVariantNoEH( + pprop, + ppsstm->GetCodePage(), + &avar[iprop], + ppsstm->GetAllocator(), + &Status); + if( !NT_SUCCESS(Status) ) goto Exit; + + if( fIndirect ) + { + cIndirect++; + } + } + if (apid != NULL) + { + apid[iprop] = pid; + } + } // for (iprop = 0; iprop < cprop; iprop++) + + // If the caller wants to know about indirect streams and + // storages (and if there were any), allocate memory for a + // MAXULONG terminated array of indexes to the indirect + // variant structures, and fill it in. + + if (cIndirect != 0) + { + BuildIndirectIndexArray( + cprop, + cIndirect, + cIndirect, + avar, + ppip, + &Status); + if( !NT_SUCCESS(Status) ) goto Exit; + } + + ppsstm->Validate(&Status); + if( !NT_SUCCESS(Status) ) goto Exit; + + // ---- + // Exit + // ---- + +Exit: + + if( !NT_SUCCESS(Status) ) + { + if (ppip != NULL) + { + if (cprop == 1) + { + ((INDIRECTPROPERTY *) ppip)->Index = MAXULONG; + } + else if (*ppip != NULL) + { + delete [] *ppip; + *ppip = NULL; + } + } + CleanupVariants(avar, cprop, ppsstm->GetAllocator()); + } + + if (fLocked) + Status = Unlock( ppsstm, Status ); + + DebugTrace(0, DbgS(Status), ( + "RtlQueryProperties() ==> *ppip=%x, s=%x\n--------\n", + ppip == NULL? NULL : *ppip, + 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; + BOOL fLocked = FALSE; + + 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])); + } + + Status = ppsstm->Lock(FALSE); + if( !NT_SUCCESS(Status) ) goto Exit; + fLocked = TRUE; + + + 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: + + if (fLocked) + Status = Unlock( ppsstm, Status ); + + 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 WCHAR 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; + BOOL fLocked = FALSE; + + DebugTrace(0, Dbg, ( + "RtlQueryPropertyNames(np=%x, cprop=%x, apid=%x, apwsz=%x)\n", + np, + cprop, + apid, + aposz)); + + RtlZeroMemory(aposz, cprop * sizeof(aposz[0])); + + Status = ppsstm->Lock(FALSE); + if( !NT_SUCCESS(Status) ) goto Exit; + fLocked = TRUE; + + ppsstm->ReOpen(&Status); // Reload header/size info + if( !NT_SUCCESS(Status) ) goto Exit; + + ppsstm->Validate(&Status); + if( !NT_SUCCESS(Status) ) goto Exit; + + // We'll 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: + + if (fLocked) + Status = Unlock( ppsstm, Status ); + + 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; + BOOL fLocked = FALSE; + + DebugTrace(0, Dbg, ( + "RtlSetPropertyNames(np=%x, cprop=%x, apid=%x, apwsz=%x)\n", + np, + cprop, + apid, + aposz)); + + Status = ppsstm->Lock(TRUE); + if( !NT_SUCCESS(Status) ) goto Exit; + fLocked = TRUE; + + 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: + + if (fLocked) + Status = Unlock( ppsstm, Status ); + + 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; + BOOL fLocked = FALSE; + + DebugTrace(0, Dbg, ("RtlSetPropertySetClassId(np=%x)\n", np)); + + Status = ppsstm->Lock(TRUE); + if( !NT_SUCCESS(Status) ) goto Exit; + fLocked = TRUE; + + 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: + + if (fLocked) + Status = Unlock( ppsstm, Status ); + + 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; + BOOL fLocked = FALSE; + CPropertySetStream *ppsstm = (CPropertySetStream *) np; + + DebugTrace(0, Dbg, ("RtlQueryPropertySet(np=%x, pspss=%x)\n", np, pspss)); + RtlZeroMemory(pspss, sizeof(*pspss)); + + Status = ppsstm->Lock(FALSE); + if( !NT_SUCCESS(Status) ) goto Exit; + fLocked = TRUE; + + 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: + + if (fLocked) + Status = Unlock( ppsstm, Status ); + + DebugTrace(0, DbgS(Status), ("RtlQueryPropertySet() ==> s=%x\n--------\n", Status)); + return(Status); + +} // RtlQueryPropertySet() + + +//+--------------------------------------------------------------------------- +// Function: RtlEnumeratePropertySets, public +// +// Synopsis: Enumerate property sets on a structured storage +// +// Arguments: [hstg] -- structured storage handle +// [pcspss] -- pointer to count of STATPROPSETSTGs +// [pkey] -- bookmark; caller set to 0 before 1st call +// [pspss] -- array of STATPROPSETSTGs +// +// Returns: Status code +//--------------------------------------------------------------------------- + +#ifdef _CAIRO_ +NTSTATUS PROPSYSAPI PROPAPI +RtlEnumeratePropertySets( + IN HANDLE hstg, // structured storage handle + IN BOOLEAN fRestart, // TRUE --> restart scan + IN OUT ULONG *pcspss, // pointer to count of STATPROPSETSTGs + IN OUT GUID *pkey, // bookmark + OUT STATPROPSETSTG *pspss) // array of STATPROPSETSTGs +{ + NTSTATUS Status; + LONGLONG abufStack[2048/sizeof(LONGLONG)]; + ULONG cspss = 0; + GUID *pguidLast; + IO_STATUS_BLOCK isb; + BOOLEAN fFoundKey, fRestartOleEnum; +#ifndef USE_OLE_DIR_FILE + UNICODE_STRING strProp; + UNICODE_STRING *pstr; +#endif + + DebugTrace(0, Dbg, ( + "RtlEnumeratePropertySets(h=%x, fRestart=%x, cspss=%x, key=%x, pspss=%x)\n", + hstg, + fRestart, + *pcspss, + *pkey, + pspss)); + + if (*pcspss == 0) + { + Status = STATUS_INVALID_PARAMETER; + goto exit; + } + + fFoundKey = fRestart; // assume start at first propset AFTER *pkey. + +#ifndef USE_OLE_DIR_FILE + strProp.Length = strProp.MaximumLength = 2*sizeof(WCHAR); + strProp.Buffer = L"\1*"; +#endif + +forcerestart: +#ifndef USE_OLE_DIR_FILE + pstr = &strProp; +#endif + fRestartOleEnum = TRUE; + + while (TRUE) + { + FILE_OLE_DIR_INFORMATION *pfodiBase; + FILE_OLE_DIR_INFORMATION *pfodi, *pfodiEnd; + ULONG NextOffset; + +#ifndef USE_OLE_DIR_FILE +#define NtQueryOleDirectoryFile NtQueryDirectoryFile +#endif + pfodiBase = (FILE_OLE_DIR_INFORMATION *) abufStack; + + Status = NtQueryOleDirectoryFile( + hstg, + NULL, + NULL, + NULL, + &isb, + pfodiBase, + sizeof(abufStack), + FileOleDirectoryInformation, + FALSE, +#ifndef USE_OLE_DIR_FILE + pstr, +#else + NULL, +#endif + fRestartOleEnum); + if (!NT_SUCCESS(Status)) + { + break; + } +#ifndef USE_OLE_DIR_FILE + pstr = NULL; +#endif + fRestartOleEnum = FALSE; + + // copy out requested info + + pfodi = pfodiBase; + pfodiEnd = (FILE_OLE_DIR_INFORMATION *) + Add2Ptr(pfodiBase, isb.Information); + + do + { + GUID guid; + + if ((pfodi->FileAttributes & FILE_ATTRIBUTE_PROPERTY_SET) && + NT_SUCCESS(RtlPropertySetNameToGuid( + pfodi->FileNameLength/sizeof(WCHAR), + pfodi->FileName, + &guid))) + { + if (fFoundKey) + { + if (cspss >= *pcspss) + { + break; // return success + } + pspss->fmtid = guid; + pguidLast = &pspss->fmtid; + + CopyFileTime(&pspss->mtime, &pfodi->LastWriteTime); + + if (pfodi->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + pspss->grfFlags = PROPSETFLAG_NONSIMPLE; + CopyFileTime(&pspss->ctime, &pfodi->CreationTime); + CopyFileTime(&pspss->atime, &pfodi->LastAccessTime); + pspss->clsid = pfodi->OleClassId; + } + else + { + pspss->grfFlags = PROPSETFLAG_DEFAULT; + ZeroFileTime(&pspss->ctime); + ZeroFileTime(&pspss->atime); + RtlZeroMemory(&pspss->clsid, sizeof(pspss->clsid)); + } + pspss++; + cspss++; + } + else if (RtlCompareMemory( + pkey, + &guid, + sizeof(guid)) == sizeof(guid)) + { + fFoundKey = TRUE; + } + } + NextOffset = pfodi->NextEntryOffset; + pfodi = (FILE_OLE_DIR_INFORMATION *) Add2Ptr(pfodi, NextOffset); + } while (NextOffset != 0 && pfodi < pfodiEnd); + } + if (Status == STATUS_NO_SUCH_FILE || Status == STATUS_NO_MORE_FILES) + { + Status = STATUS_SUCCESS; + } + if (NT_SUCCESS(Status)) + { + // if we didn't find the search key, it must have been deleted. + // Loop back and start with the first 'real' property set. + // 'Tis better to return duplicates, than to skip entries. + + if (!fFoundKey) + { + PROPASSERT(cspss == 0); + fFoundKey = TRUE; + goto forcerestart; + } + if (cspss == 0) + { + Status = STATUS_PROPSET_NOT_FOUND; + } + else + { + *pkey = *pguidLast; + } + } + +exit: + *pcspss = cspss; // return actual count + + DebugTrace( + 0, + Status == STATUS_PROPSET_NOT_FOUND? Dbg : DbgS(Status), ( + "RtlEnumeratePropertySets() ==> cspss=%x, pkey=%x, s=%x\n--------\n", + *pcspss, + pkey, + Status)); + + return(Status); +} +#endif // ifdef _CAIRO_ + + + +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) + { + fSame = ( pclipdata1->cbSize == pclipdata2->cbSize + && + pclipdata1->ulClipFmt == pclipdata2->ulClipFmt ); + + if (fSame) + { + 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). + // Note that CBPCLIPDATA(*pclipdata1)==CBPCLIPDATA(*pclipdata2). + + 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 +//--------------------------------------------------------------------------- + +#ifdef _MAC +EXTERN_C // The Mac linker doesn't seem to be able to export with C++ decorations +#endif + +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.HighPart == pvar2->hVal.HighPart + && + pvar1->hVal.LowPart == pvar2->hVal.LowPart; + break; + + case VT_CLSID: + fSame = memcmp(pvar1->puuid, pvar2->puuid, sizeof(CLSID)) == 0; + break; + + case VT_BLOB: + case VT_BLOB_OBJECT: + fSame = ( pvar1->blob.cbSize == pvar2->blob.cbSize ); + if (fSame) + { + 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) + { + fSame = ( BSTRLEN(pvar1->bstrVal) == BSTRLEN(pvar2->bstrVal) ); + if (fSame) + { + 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_STREAM: + case VT_STREAMED_OBJECT: + case VT_STORAGE: + case VT_STORED_OBJECT: + 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: + fSame = ( pvar1->caub.cElems == pvar2->caub.cElems ); + if (fSame) + { + 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: + fSame = ( pvar1->cai.cElems == pvar2->cai.cElems ); + if (fSame) + { + fSame = memcmp( + pvar1->cai.pElems, + pvar2->cai.pElems, + pvar1->cai.cElems * sizeof(pvar1->cai.pElems[0])) == 0; + } + break; + + case VT_VECTOR | VT_BOOL: + fSame = ( pvar1->cabool.cElems == pvar2->cabool.cElems ); + if (fSame) + { + 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: + fSame = ( pvar1->cal.cElems == pvar2->cal.cElems ); + if (fSame) + { + 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: + fSame = ( pvar1->cah.cElems == pvar2->cah.cElems ); + if (fSame) + { + fSame = memcmp( + pvar1->cah.pElems, + pvar2->cah.pElems, + pvar1->cah.cElems * + sizeof(pvar1->cah.pElems[0])) == 0; + } + break; + + case VT_VECTOR | VT_CLSID: + fSame = ( pvar1->cauuid.cElems == pvar2->cauuid.cElems ); + if (fSame) + { + fSame = memcmp( + pvar1->cauuid.pElems, + pvar2->cauuid.pElems, + pvar1->cauuid.cElems * + sizeof(pvar1->cauuid.pElems[0])) == 0; + } + break; + + case VT_VECTOR | VT_CF: + fSame = ( pvar1->caclipdata.cElems == pvar2->caclipdata.cElems ); + if (fSame) + { + 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: + fSame = ( pvar1->cabstr.cElems == pvar2->cabstr.cElems ); + if (fSame) + { + for (i = 0; i < pvar1->cabstr.cElems; i++) + { + if (pvar1->cabstr.pElems[i] != NULL && + pvar2->cabstr.pElems[i] != NULL) + { + fSame = ( BSTRLEN(pvar1->cabstr.pElems[i]) + == + BSTRLEN(pvar2->cabstr.pElems[i]) ); + if (fSame) + { + 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: + fSame = ( pvar1->calpstr.cElems == pvar2->calpstr.cElems ); + if (fSame) + { + 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: + fSame = ( pvar1->calpwstr.cElems == pvar2->calpwstr.cElems ); + if (fSame) + { + 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: + fSame = ( pvar1->capropvar.cElems == pvar2->capropvar.cElems ); + if (fSame) + { + 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); +} diff --git a/private/dcomidl/obase.idl b/private/dcomidl/obase.idl new file mode 100644 index 000000000..b30117224 --- /dev/null +++ b/private/dcomidl/obase.idl @@ -0,0 +1,266 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1995. +// +// File: obase.idl +// +// Synopsis: this file contain the base definitions for object +// interface references. +// +//+------------------------------------------------------------------------- +[ + uuid(99fcfe60-5260-101b-bbcb-00aa0021347a), + pointer_default(unique) +] + +interface ObjectRpcBaseTypes +{ +#ifndef DO_NO_IMPORTS + import "wtypes.idl"; +#endif + + //////////////////////////////////////////////////////////// + // + // Identifier Definitions + // + //////////////////////////////////////////////////////////// + + typedef unsigned hyper ID; + typedef ID MID; // Machine Identifier + typedef ID OXID; // Object Exporter Identifier + typedef ID OID; // Object Identifer + typedef ID SETID; // Ping Set Identifier + typedef GUID IPID; // Interface Pointer Identifier + typedef GUID CID; // Causality Identifier + + typedef REFGUID REFIPID; + + + ////////////////////////////////////////////////////////////////// + // + // ORPC Call Packet Format + // + ////////////////////////////////////////////////////////////////// + + const unsigned short COM_MAJOR_VERSION = 5; + const unsigned short COM_MINOR_VERSION = 1; + + // Component Object Model version number + typedef struct tagCOMVERSION + { + unsigned short MajorVersion; // Major version number + unsigned short MinorVersion; // Minor version number + } COMVERSION; + + + // enumeration of additional information present in the call packet. + // Should be an enum but DCE IDL does not support sparse enumerators. + + const unsigned long ORPCF_NULL = 0; // no additional info in packet + const unsigned long ORPCF_LOCAL = 1; // call is local to this machine + const unsigned long ORPCF_RESERVED1 = 2; // reserved for local use + const unsigned long ORPCF_RESERVED2 = 4; // reserved for local use + const unsigned long ORPCF_RESERVED3 = 8; // reserved for local use + const unsigned long ORPCF_RESERVED4 = 16; // reserved for local use + + + // Extension to implicit parameters. + typedef struct tagORPC_EXTENT + { + GUID id; // Extension identifier. + unsigned long size; // Extension size. + [size_is((size+7)&~7)] byte data[]; // Extension data. + } ORPC_EXTENT; + + + // Array of extensions. + typedef struct tagORPC_EXTENT_ARRAY + { + unsigned long size; // Num extents. + unsigned long reserved; // Must be zero. + [size_is((size+1)&~1,), unique] ORPC_EXTENT **extent; // extents + } ORPC_EXTENT_ARRAY; + + + // implicit 'this' pointer which is the first [in] parameter on + // every ORPC call. + typedef struct tagORPCTHIS + { + COMVERSION version; // COM version number + unsigned long flags; // ORPCF flags for presence of other data + unsigned long reserved1; // set to zero + CID cid; // causality id of caller + + // Extensions. + [unique] ORPC_EXTENT_ARRAY *extensions; + } ORPCTHIS; + + + // implicit 'that' pointer which is the first [out] parameter on + // every ORPC call. + typedef struct tagORPCTHAT + { + unsigned long flags; // ORPCF flags for presence of other data + + // Extensions. + [unique] ORPC_EXTENT_ARRAY *extensions; + } ORPCTHAT; + + + ////////////////////////////////////////////////////////////////// + // + // Marshaled COM Interface Wire Format + // + ////////////////////////////////////////////////////////////////// + + // DUALSTRINGARRAYS are the return type for arrays of network addresses, + // arrays of endpoints and arrays of both used in many ORPC interfaces + + const unsigned short NCADG_IP_UDP = 0x08; + const unsigned short NCACN_IP_TCP = 0x07; + const unsigned short NCADG_IPX = 0x0E; + const unsigned short NCACN_SPX = 0x0C; + const unsigned short NCACN_NB_NB = 0x12; + const unsigned short NCACN_NB_IPX = 0x0D; + const unsigned short NCACN_DNET_NSP = 0x04; + const unsigned short NCALRPC = 0x10; + // const unsigned short MSWMSG = 0x01; // note: not a real tower id. + + // This is the return type for arrays of string bindings or protseqs + // used by many ORPC interfaces. + + // Not used for DCOM: + // ncacn_np + // ncacn_nb_tcp + // ncacn_nb_ipx + // ncacn_nb_xns + // ncacn_at_dsp + // ncadg_at_ddp + // ncacn_vns_spp + + typedef struct tagSTRINGBINDING + { + unsigned short wTowerId; // Cannot be zero. + unsigned short aNetworkAddr; // Zero terminated. + } STRINGBINDING; + + const unsigned short COM_C_AUTHZ_NONE = 0xffff; + + typedef struct tagSECURITYBINDING + { + unsigned short wAuthnSvc; // Cannot be zero. + unsigned short wAuthzSvc; // Must not be zero. + unsigned short aPrincName; // Zero terminated. + } SECURITYBINDING; + + typedef struct tagDUALSTRINGARRAY + { + unsigned short wNumEntries; // Number of entries in array. + unsigned short wSecurityOffset; // Offset of security info. + + // The array contains two parts, a set of STRINGBINDINGs + // and a set of SECURITYBINDINGs. Each set is terminated by an + // extra zero. The shortest array contains four zeros. + + [size_is(wNumEntries)] unsigned short aStringArray[]; + } DUALSTRINGARRAY; + + // signature value for OBJREF (object reference, actually the + // marshaled form of a COM interface). + const unsigned long OBJREF_SIGNATURE = 0x574f454d; // 'MEOW' + + // flag values for OBJREF + const unsigned long OBJREF_STANDARD = 0x1; // standard marshaled objref + const unsigned long OBJREF_HANDLER = 0x2; // handler marshaled objref + const unsigned long OBJREF_CUSTOM = 0x4; // custom marshaled objref + + // Flag values for a STDOBJREF (standard part of an OBJREF). + // SORF_OXRES1 - SORF_OXRES8 are reserved for the object exporters + // use only, object importers must ignore them and must not enforce MBZ. + + const unsigned long SORF_OXRES1 = 0x1; // reserved for exporter + const unsigned long SORF_OXRES2 = 0x20; // reserved for exporter + const unsigned long SORF_OXRES3 = 0x40; // reserved for exporter + const unsigned long SORF_OXRES4 = 0x80; // reserved for exporter + const unsigned long SORF_OXRES5 = 0x100;// reserved for exporter + const unsigned long SORF_OXRES6 = 0x200;// reserved for exporter + const unsigned long SORF_OXRES7 = 0x400;// reserved for exporter + const unsigned long SORF_OXRES8 = 0x800;// reserved for exporter + + const unsigned long SORF_NULL = 0x0; // convenient for initializing SORF + const unsigned long SORF_NOPING = 0x1000;// Pinging is not required + + + // standard object reference + typedef struct tagSTDOBJREF + { + unsigned long flags; // STDOBJREF flags (see above) + unsigned long cPublicRefs; // count of references passed + OXID oxid; // oxid of server with this oid + OID oid; // oid of object with this ipid + IPID ipid; // ipid of Interface + } STDOBJREF; + + + // OBJREF is the format of a marshaled interface pointer. + typedef struct tagOBJREF + { + unsigned long signature; // must be OBJREF_SIGNATURE + unsigned long flags; // OBJREF flags (see above) + GUID iid; // interface identifier + + [switch_is(flags), switch_type(unsigned long)] union + { + [case(OBJREF_STANDARD)] struct + { + STDOBJREF std; // standard objref + DUALSTRINGARRAY saResAddr; // resolver address + } u_standard; + + [case(OBJREF_HANDLER)] struct + { + STDOBJREF std; // standard objref + CLSID clsid; // Clsid of handler code + DUALSTRINGARRAY saResAddr; // resolver address + } u_handler; + + [case(OBJREF_CUSTOM)] struct + { + CLSID clsid; // Clsid of unmarshaling code + unsigned long cbExtension;// size of extension data + unsigned long size; // size of data that follows + [size_is(size), ref] byte *pData; // extension + class specific data + } u_custom; + + } u_objref; + + } OBJREF; + + + // wire representation of a marshalled interface pointer + typedef struct tagMInterfacePointer + { + ULONG ulCntData; // size of data + [size_is(ulCntData)] BYTE abData[]; // data (OBJREF) + } MInterfacePointer; + + typedef [unique] MInterfacePointer * PMInterfacePointer; + + ////////////////////////////////////////////////////////////////// + // + // OXID Resolution + // + ////////////////////////////////////////////////////////////////// + + // OXID Resolver information associated with each OXID. + typedef struct tagOXID_INFO + { + DWORD dwTid; // thread id of object exporter + DWORD dwPid; // process id of object exporter + IPID ipidRemUnknown; // IRemUnknown IPID for object exporter + DWORD dwAuthnHint; + [unique] DUALSTRINGARRAY *psa; // protocol and security info + } OXID_INFO; +} + diff --git a/private/dcomidl/objex.acf b/private/dcomidl/objex.acf new file mode 100644 index 000000000..2cc217a47 --- /dev/null +++ b/private/dcomidl/objex.acf @@ -0,0 +1,9 @@ + + +[ implicit_handle(handle_t any_handle) ] interface IObjectExporter +{ + [comm_status, fault_status] ResolveOxid(); + [comm_status, fault_status] SimplePing(); + [comm_status, fault_status] ComplexPing(); + [comm_status, fault_status] ServerAlive(); +} diff --git a/private/dcomidl/objex.idl b/private/dcomidl/objex.idl new file mode 100644 index 000000000..1e05c537e --- /dev/null +++ b/private/dcomidl/objex.idl @@ -0,0 +1,85 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1995. +// +// File: objex.idl +// +// Synopsis: Interface implemented by object exporters. +// +// This is the interface that needs to be supported by hosts that export +// objects. Only one instance of this interface can be exported by the host. +// +// An object exporter needs to be able to: +// 1. return string bindings that can be used to talk to objects it +// has exported +// 2. receive pings from object importers to keep the objects alive +// +// Note that changes to this interface can effect IActivation because the +// RemoteActivation method of IActivation includes information otherwise +// retrieved from ResolveOxid. +// +//-------------------------------------------------------------------------- +[ + uuid(99fcfec4-5260-101b-bbcb-00aa0021347a), + pointer_default(unique) +] + +interface IObjectExporter +{ + import "obase.idl"; + + // Method to get the protocol sequences, string bindings and machine id + // for an object server given its OXID. + + [idempotent] error_status_t ResolveOxid + ( + [in] handle_t hRpc, + [in] OXID *pOxid, + [in] unsigned short cRequestedProtseqs, + [in, ref, size_is(cRequestedProtseqs)] + unsigned short arRequestedProtseqs[], + [out, ref] DUALSTRINGARRAY **ppdsaOxidBindings, + [out, ref] IPID *pipidRemUnknown, + [out, ref] DWORD *pAuthnHint + ); + + // Simple ping is used to ping a Set. Client machines use this to inform + // the object exporter that it is still using the members of the set. + // Returns S_TRUE if the SetId is known by the object exporter, + // S_FALSE if not. + + [idempotent] error_status_t SimplePing + ( + [in] handle_t hRpc, + [in] SETID *pSetId // Must not be zero + ); + + // Complex ping is used to create sets of OIDs to ping. The whole set + // can subsequently be pinged using SimplePing, thus reducing network + // traffic. + + [idempotent] error_status_t ComplexPing + ( + [in] handle_t hRpc, + [in, out] SETID *pSetId, // In of 0 on first call for new set. + [in] unsigned short SequenceNum, + [in] unsigned short cAddToSet, + [in] unsigned short cDelFromSet, + [in, unique, size_is(cAddToSet)] OID AddToSet[], // add these OIDs to the set + [in, unique, size_is(cDelFromSet)] OID DelFromSet[], // remove these OIDs from the set + [out] unsigned short *pPingBackoffFactor // 2^factor = multipler + ); + + // In some cases the client maybe unsure that a particular binding will + // reach the server. (For example, when the oxid bindings have more then + // one TCP/IP binding) This call can be used to validate the binding + // from the client. + + [idempotent] error_status_t ServerAlive + ( + [in] handle_t hRpc + ); + +} + diff --git a/private/dcomidl/odeth.acf b/private/dcomidl/odeth.acf new file mode 100644 index 000000000..389bdd75a --- /dev/null +++ b/private/dcomidl/odeth.acf @@ -0,0 +1,14 @@ +interface IRundown +{ +#ifdef RAW + [nocode] DummyQueryInterfaceIOSCM(); + [nocode] DummyAddRefIOSCM(); + [nocode] DummyReleaseIOSCM(); + [nocode] DummyRemQuery(); + [nocode] DummyRemAddRef(); + [nocode] DummyRemRelease(); + [nocode] DummyRemChangeRef(); + + [comm_status, fault_status] RawRundownOid(); +#endif +} diff --git a/private/dcomidl/odeth.idl b/private/dcomidl/odeth.idl new file mode 100644 index 000000000..7be904a26 --- /dev/null +++ b/private/dcomidl/odeth.idl @@ -0,0 +1,99 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1996. +// +// File: odeath.idl +// +//+------------------------------------------------------------------------- + +//+------------------------------------------------------------------------- +// +// Interface: IRemUnknown2 +// +// Synopsys: This is a local interface used to convert strong references +// to weak references and vice versa. It is needed for the +// container/link/embedding aspects of OLE. +// +// Notes: Weak references are not permitted remotely. +// +//+------------------------------------------------------------------------- +[ + object, + uuid(0000013C-0000-0000-C000-000000000046) +] + +interface IRemUnknown2 : IRemUnknown +{ +#ifndef DO_NO_IMPORTS + import "remunk.idl"; +#endif + const unsigned long IRUF_CONVERTTOWEAK = 0x01;// make refs weak + const unsigned long IRUF_CONVERTTOSTRONG = 0x02;// make refs strong + const unsigned long IRUF_DISCONNECTIFLASTSTRONG = 0x04;// disconnect the + // object if last strong ref + HRESULT RemChangeRef + ( + [in] unsigned long flags, + [in] unsigned short cInterfaceRefs, + [in, size_is(cInterfaceRefs)] REMINTERFACEREF InterfaceRefs[] + ); +} + + +//+------------------------------------------------------------------------- +// +// Interface: IRundown +// +// Synopsys: This is the local OID rundown interface. The object exporter +// calls it to notify the server which OIDs are no longer being +// pinged. +// +// Notes: This inherits from IRemUnknown and IRemUnknown2 so that we +// can use the same IPID for all three interfaces. This is +// strictly a local decision and does not affect remote protocol. +// +//+------------------------------------------------------------------------- +[ + uuid(00000134-0000-0000-C000-000000000046) +#ifndef RAW + , object +#endif + ] + +interface IRundown +#ifndef RAW + : IRemUnknown2 +#endif + +{ +#ifndef DO_NO_IMPORTS + import "iface.idl"; + import "obase.idl"; + import "remunk.idl"; +#endif + #include "comhndl.h" + + COM_DEFINES(IOSCM) + +#ifdef RAW + // Define the functions in IRemUnknown2 + HRESULT DummyRemQuery( handle_t handle ); + HRESULT DummyRemAddRef( handle_t handle ); + HRESULT DummyRemRelease( handle_t handle ); + HRESULT DummyRemChangeRef( handle_t handle ); + + error_status_t + RawRundownOid +#else + HRESULT + RundownOid +#endif + ( + COM_HANDLE + [in] ULONG cOid, + [in, size_is(cOid)] OID aOid[], + [out, size_is(cOid)] unsigned char afOkToRundown[] + ); +} + diff --git a/private/dcomidl/orcb.acf b/private/dcomidl/orcb.acf new file mode 100644 index 000000000..5c9cf69ec --- /dev/null +++ b/private/dcomidl/orcb.acf @@ -0,0 +1,6 @@ + + +[ implicit_handle(handle_t any_handle) ] interface IOrCallback +{ + [comm_status, fault_status] UseProtseq(); +} diff --git a/private/dcomidl/orcb.idl b/private/dcomidl/orcb.idl new file mode 100644 index 000000000..079ba149e --- /dev/null +++ b/private/dcomidl/orcb.idl @@ -0,0 +1,27 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1996. +// +// File: orcb.idl +// +// Local interface used by the object resolver to force the server process +// to start listening to an additional RPC protocol sequence. +// +//+------------------------------------------------------------------------- +[ + uuid(18f70770-8e64-11cf-9af1-0020af6e72f4) +] + +interface IOrCallback +{ + import "obase.idl"; + + error_status_t UseProtseq( + [in] handle_t hRpc, + [in, string] wchar_t *pwstrProtseq, + [out] DUALSTRINGARRAY **ppdsaNewBindings, // Null on failure + [out] DUALSTRINGARRAY **ppdsaNewSecurity + ); +} + diff --git a/private/dcomidl/propstm.cxx b/private/dcomidl/propstm.cxx new file mode 100644 index 000000000..2b2f3b260 --- /dev/null +++ b/private/dcomidl/propstm.cxx @@ -0,0 +1,7971 @@ + +//+-------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1993 +// +// File: propstm.cxx +// +// Contents: property set value extraction code +// +// History: 15-Jul-94 brianb created +// 12-Aug-94 SethuR Included Assertions for # of sections +// split PropertySet class into +// CPropertySetStream & CPropertySetStorage +// Included Update methods on the property +// stream. +// 22-Feb-96 MikeHill DWORD-align the dictionary entries, +// & use char-counts for dict entries. +// 29-Feb-96 MikeHill Moved _DictionaryEntryLength and _NextDictionaryEntry +// inlines here from propstm.hxx. +// 09-May-96 MikeHill - Keep the dictionary in the UserDef propset +// immediately after the last entry in the PID/Offset +// array (for Office95 compatibility). +// - Create an empty dictionary in the UD propset +// when it is created. If we wait till later, +// we can't make the dictionary the first property, +// which is required by Office95. +// - Provide compatibility with Publisher95 (which doesn't +// DWORD-align the section/stream size). +// - Provide compatibility with PowerPoint 4.0 (which +// over-pads some properties, and under-pads others). +// - Don't try to unpack the DocParts and HeadingPair +// DocSumInfo properties in Ansi property sets. +// 22-May-96 MikeHill - Return the OSVersion on an Open. +// - Use the PropSet's code page, not the system's. +// 11-Jun-96 MikeHill - Initialize all members in the constructor. +// 25-Jul-96 MikeHill - Removed usage of Win32 SEH. +// - BSTRs & prop names: WCHAR => OLECHAR. +// - Added big-endian support. +// - Determine the OSVer at run-time. +// - Fix for Excel 5.0a compatibility. +// 26-Nov-96 MikeHill Handle invalid oSection values. +// +// Notes: +// +// The OLE 2.0 Appendix B property set specifies multiple sections in the +// property stream specification. Multiple sections were intended to allow +// the schema associated with the property set to evolve over a period of time, +// but there is no reason that new PROPIDs cannot serve the same purpose. The +// current implementation of the property stream is limited to one section, +// except for the Office DocumentSummaryInformation property set's specific use +// of a second section. Other property sets with multiple sections can only be +// accessed in read-only mode, and then only for the first property section. +// +// The current implementation of property set stream is built around a class +// called CPropertySetStream. The various details of the OLE property spec is +// confined to this class. Since the property set streams need to be parsed +// in the kernel mode (OFS driver) as well as the user mode, this class +// encapsulates a stream implementation (CMappedStream). This is different +// from other stream implementations in that the fundamental mechanism provided +// for acessing the contents is Map/Unmap rather than Read/Write. There are +// two user mode implementations of this CMappedStream interface, one for +// docfile streams, and another for native streams. There is one +// implementation in kernel mode for the OFS driver. For more details, +// refer to propstm.hxx. +//--------------------------------------------------------------------------- + +#include <pch.cxx> + +#include <olechar.h> + +#if DBGPROP +#include <stdio.h> // for sprintf/strcpy +#endif +#include "propvar.h" + + +#define Dbg DEBTRACE_PROPERTY + +#define szX "x" // allows radix change for offsets & sizes +//#define szX "d" // allows radix change for offsets & sizes + +#ifndef newk +#define newk(Tag, pCounter) new +#endif + +#ifndef IsDwordAligned +#define IsDwordAligned(p) (((ULONG) (p) & (sizeof(ULONG) - 1)) == 0) +#endif + +#ifndef DwordRemain +#define DwordRemain(cb) \ + ((sizeof(ULONG) - ((cb) % sizeof(ULONG))) % sizeof(ULONG)) +#endif + + +// Information for the the OS Version field of the +// property set header. + +#if !defined(IPROPERTY_DLL) +# define PROPSETVER_CURRENT MAKEPSVER(OSKIND_WIN32, WINVER >> 8, WINVER & 0xff) +#endif + +#define PROPSETVER_WIN310 MAKEPSVER(OSKIND_WINDOWS, 3, 10) +#define PROPSETVER_WIN333 MAKEPSVER(OSKIND_WIN32, 3, 0x33) + + + +extern GUID guidSummary; +extern GUID guidDocumentSummary; +extern GUID guidDocumentSummarySection2; + +#define CP_DEFAULT_NONUNICODE 1252 // ANSI Latin1 (US, Western Europe) +#ifdef KERNEL +#define CP_CREATEDEFAULT(state) \ + ((state & CPSS_PROPHEADER)? CP_DEFAULT_NONUNICODE : CP_WINUNICODE) +#else +extern "C" UNICODECALLOUTS UnicodeCallouts; +#define CP_CREATEDEFAULT(state) (*UnicodeCallouts.pfnGetACP)() +#endif + +#if DBGPROP +#define StatusCorruption(pstatus, szReason) \ + _StatusCorruption(szReason " ", pstatus) +#else +#define StatusCorruption(pstatus, szReason) \ + _StatusCorruption(pstatus) +#endif + + +#ifndef KERNEL +VOID RtlpConvertToUnicode( + IN CHAR const *pch, + IN ULONG cb, + IN USHORT CodePage, + OUT WCHAR **ppwc, + OUT ULONG *pcb, + OUT NTSTATUS *pstatus); +VOID RtlpConvertToMultiByte( + IN WCHAR const *pwc, + IN ULONG cb, + IN USHORT CodePage, + OUT CHAR **ppch, + OUT ULONG *pcb, + OUT NTSTATUS *pstatus); +#endif + + + +// +// Re-direct RtlEqual[Unicode]String routines +// +// These macros redirect two NTDLL routines which don't exist in +// the IProperty DLL. They are redirected to CRT calls. +// +// Note: These redirections assume that the Length and +// MaximumLength fields, on both String structures, are the +// same (e.g. s1.len == s1.maxlen == s2.len == s2.maxlen). +// + +#ifdef IPROPERTY_DLL + + #define RtlEqualString(String1,String2,fCaseInSensitive) \ + fCaseInSensitive \ + ? ( !_strnicmp( (String1)->Buffer, \ + (String2)->Buffer, \ + (String1)->MaximumLength) ) \ + : ( !strncmp( (String1)->Buffer, \ + (String2)->Buffer, \ + (String1)->MaximumLength) ) + + #define RtlEqualUnicodeString(String1,String2,fCaseInSensitive) \ + fCaseInSensitive \ + ? ( !_wcsnicmp( (String1)->Buffer, \ + (String2)->Buffer, \ + (String1)->MaximumLength / sizeof(WCHAR) )) \ + : ( !wcsncmp( (String1)->Buffer, \ + (String2)->Buffer, \ + (String1)->MaximumLength / sizeof(WCHAR) )) + +#endif // #ifdef IPROPERTY_DLL + + +#if DBGPROP + +#define CB_VALUEDISPLAY 8 // Number of bytes to display +#define CB_VALUESTRING (CB_VALUEDISPLAY * 3 + 3) // "xx xx xx xx...\0" + +char * +ValueToString(SERIALIZEDPROPERTYVALUE const *pprop, ULONG cbprop, char buf[]) +{ + char *p = buf; + BYTE const *pb = pprop->rgb; + BOOLEAN fOverflow = FALSE; + static char szDots[] = "..."; + + if (cbprop >= FIELD_OFFSET(SERIALIZEDPROPERTYVALUE, rgb)) + { + cbprop -= FIELD_OFFSET(SERIALIZEDPROPERTYVALUE, rgb); + if (cbprop > CB_VALUEDISPLAY) + { + cbprop = CB_VALUEDISPLAY; + fOverflow = TRUE; + } + while (cbprop-- > 0) + { + if (p != buf) + { + *p++ = ' '; + } + p += PropSprintfA( p, "%02.2x", *pb++ ); + } + } + *p = '\0'; + PROPASSERT(p - buf + sizeof(szDots) <= CB_VALUESTRING); + if (fOverflow) + { + strcpy(p, szDots); + } + return(buf); +} + + +#define CB_VARIANT_TO_STRING 35 + +char * +VariantToString(PROPVARIANT const &var, char buf[], ULONG cbprop) +{ + char *p = buf; + + PROPASSERT( cbprop >= CB_VARIANT_TO_STRING ); + + + // Add the VT to the output buffer. + + p += PropSprintfA( p, "vt=%04.4x", var.vt ); + p += PropSprintfA( p, ", val=(%08.8x, %08.8x)", var.uhVal.LowPart, var.uhVal.HighPart ); + + *p = '\0'; + PROPASSERT( (p - buf) == CB_VARIANT_TO_STRING); + return(buf); +} + +#endif + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_DictionaryEntryLength +// +// Synopsis: Calculate the length of an entry in the +// dictionary. This is non-trivial because +// it is codepage-dependent. +// +// Arguments: [pent] -- pointer to a dictionary entry. +// +// Returns: The entry's length. +//+-------------------------------------------------------------------------- + + +inline ULONG +CPropertySetStream::_DictionaryEntryLength( + IN ENTRY UNALIGNED const * pent + ) const +{ + // If this is a Unicode property set, it should be DWORD-aligned. + PROPASSERT( _CodePage != CP_WINUNICODE + || + IsDwordAligned( (ULONG) pent )); + + // The size consists of the length of the + // PROPID and character count ... + + ULONG ulSize = CB_ENTRY; + + // Plus the length of the string ... + + ulSize += PropByteSwap( pent->cch ) + * + ( _CodePage == CP_WINUNICODE ? sizeof( WCHAR ) + : sizeof( CHAR ) + ); + + // Plus, possibly, padding to make the entry DWORD-aligned + // (for Unicode property sets). + + if( _CodePage == CP_WINUNICODE ) + { + ulSize = DwordAlign( ulSize ); + } + + return( ulSize ); + +} + + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_NextDictionaryEntry +// +// Synopsis: Given a pointer to an entry in the dictionary, +// create a pointer to the next entry. +// +// Arguments: [pent] -- pointer to a dictionary entry. +// +// Returns: Pointer to the next entry. If the input +// points to the last entry in the dictionary, +// then return a pointer to just beyond the +// end of the dictionary. +//+-------------------------------------------------------------------------- + + +inline ENTRY UNALIGNED * +CPropertySetStream::_NextDictionaryEntry( + IN ENTRY UNALIGNED const * pent + ) const +{ + + return (ENTRY UNALIGNED *) + Add2Ptr( pent, _DictionaryEntryLength( pent )); + +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_SignalCorruption +// +// Synopsis: possibly PROPASSERT and return data corrupt error +// +// Arguments: [szReason] -- string explanation (DBGPROP only) +// [pstatus] -- NTSTATUS code. +// +// Returns: None +//+-------------------------------------------------------------------------- + +VOID +CPropertySetStream::_StatusCorruption( +#if DBGPROP + char *szReason, +#endif + OUT NTSTATUS *pstatus + ) const +{ +#if DBGPROP + DebugTrace(0, DEBTRACE_ERROR, ( + "_StatusCorruption(%s, psstm=%lx, mapstm=%lx, %s, flags=%x)\n", + szReason, + this, + KERNELSELECT(&_mstm, _pmstm), + KERNELSELECT("Kernel", _MSTM(IsNtMappedStream)()? "Nt" : "DocFile"), + _Flags)); + +#ifdef KERNEL + if ((_Flags & CREATEPROP_MODEMASK) != CREATEPROP_READ) +#endif + { + PROPASSERTMSG(szReason, FALSE); + DebugTrace(0, DEBTRACE_WARN, ( + "_StatusCorruption(%s, psstm=%lx, mapstm=%lx, %s, flags=%x)\n", + szReason, + this, + KERNELSELECT(&_mstm, _pmstm), + KERNELSELECT("Kernel", _MSTM(IsNtMappedStream)()? "Nt" : "DocFile"), + _Flags)); + if (DebugLevel & DEBTRACE_WARN) + { + PROPASSERTMSG(szReason, FALSE); + } + } +#endif + + *pstatus = STATUS_INTERNAL_DB_CORRUPTION; + return; +} + + +//+-------------------------------------------------------------------------- +// Function: _PropMoveMemory +// +// Synopsis: call DebugTrace and RtlMoveMemory +// +// Arguments: [pszReason] -- string explanation (Debug only) +// [pvSection] -- base of section (Debug only) +// [pvDst] -- destination +// [pvSrc] -- source +// [cbMove] -- byte count to move +// +// Returns: None +//+-------------------------------------------------------------------------- + +#if DBGPROP +#define PropMoveMemory(pszReason, pvSection, pvDst, pvSrc, cbMove) \ + _PropMoveMemory(pszReason, pvSection, pvDst, pvSrc, cbMove) +#else +#define PropMoveMemory(pszReason, pvSection, pvDst, pvSrc, cbMove) \ + _PropMoveMemory(pvDst, pvSrc, cbMove) +#endif + +inline VOID +_PropMoveMemory( +#if DBGPROP + char *pszReason, + VOID *pvSection, +#endif + VOID *pvDst, + VOID const *pvSrc, + ULONG cbMove) +{ + DebugTrace(0, Dbg, ( + "%s: Moving Dst=%lx(%l" szX ") Src=%lx(%l" szX ") Size=%l" szX "\n", + pszReason, + pvDst, + (BYTE *) pvDst - (BYTE *) pvSection, + pvSrc, + (BYTE *) pvSrc - (BYTE *) pvSection, + cbMove)); + RtlMoveMemory(pvDst, pvSrc, cbMove); +} + + +inline BOOLEAN +IsReadOnlyPropertySet(BYTE flags, BYTE state) +{ + return( + (flags & CREATEPROP_MODEMASK) == CREATEPROP_READ || + (state & CPSS_USERDEFINEDDELETED) || + (state & (CPSS_MULTIPLESECTIONS | CPSS_DOCUMENTSUMMARYINFO)) == + CPSS_MULTIPLESECTIONS); +} + + +inline BOOLEAN +IsReadOnlyPropid(PROPID pid) +{ + return( + pid == PID_DICTIONARY || + pid == PID_CODEPAGE || + pid == PID_LOCALE || + pid == PID_MODIFY_TIME || + pid == PID_SECURITY); +} + + +//+-------------------------------------------------------------------------- +// Member: CStreamChunkList::CStreamChunkList +// +// Synopsis: constructor +// +// Arguments: [cChunks] -- count of chunks that will be needed +// +// Returns: None +//+-------------------------------------------------------------------------- + +CStreamChunkList::CStreamChunkList( + ULONG cChunks, + CStreamChunk *ascnk) : + _cMaxChunks(cChunks), + _cChunks(0), + _ascnk(ascnk), + _fDelete(FALSE) +{ +} + + +//+-------------------------------------------------------------------------- +// Member: CStreamChunkList::Delete +// +// Synopsis: destructor +// +// Arguments: None +// +// Returns: None +//+-------------------------------------------------------------------------- + +inline +VOID +CStreamChunkList::Delete(VOID) +{ + if (_fDelete) + { + delete [] _ascnk; + } +#if DBGPROP + _cMaxChunks = _cChunks = 0; + _ascnk = NULL; + _fDelete = FALSE; +#endif +} + + +//+-------------------------------------------------------------------------- +// Member: CStreamChunkList::GetChunk +// +// Synopsis: retrieves a chunk given the index +// +// Arguments: [i] -- index of the chunk to retrieve +// +// Returns: specified chunk pointer +//+-------------------------------------------------------------------------- + +inline +CStreamChunk const * +CStreamChunkList::GetChunk(ULONG i) const +{ + PROPASSERT(i < _cChunks); + PROPASSERT(i < _cMaxChunks); + PROPASSERT(_ascnk != NULL); + return(&_ascnk[i]); +} + + +//+-------------------------------------------------------------------------- +// Member: CStreamChunkList::Count +// +// Synopsis: returns the count of chunks +// +// Arguments: None +// +// Returns: the number of chunks. +//+-------------------------------------------------------------------------- + +inline ULONG +CStreamChunkList::Count(VOID) const +{ + return(_cChunks); +} + + +//+-------------------------------------------------------------------------- +// Member: CStreamChunkList::GetFreeChunk +// +// Synopsis: gets a unused chunk descriptor +// +// Arguments: [pstatus] -- NTSTATUS code +// +// Returns: a ptr to a stream chunk descriptor. +// This will be NULL if there was an +// error. +//+-------------------------------------------------------------------------- + +CStreamChunk * +CStreamChunkList::GetFreeChunk(OUT NTSTATUS *pstatus) +{ + CStreamChunk *pscnk = NULL; + + *pstatus = STATUS_SUCCESS; + + PROPASSERT(_cChunks < _cMaxChunks); + if (_ascnk == NULL) + { + PROPASSERT(_cChunks == 0); + _ascnk = newk(mtPropSetStream, NULL) CStreamChunk[_cMaxChunks]; + if (_ascnk == NULL) + { + StatusNoMemory(pstatus, "GetFreeChunk"); + goto Exit; + } + _fDelete = TRUE; + } + + pscnk = &_ascnk[_cChunks++]; + + // ---- + // Exit + // ---- + +Exit: + + return( pscnk ); +} + + +//+-------------------------------------------------------------------------- +// Member: CStreamChunkList::AssertCbChangeTotal +// +// Synopsis: make sure the computed cbChangeTotal is correct for the chunk +// +// Arguments: None +// +// Returns: Nothing +//+-------------------------------------------------------------------------- + +#if DBGPROP +VOID +CStreamChunkList::AssertCbChangeTotal( + CStreamChunk const *pscnk, + ULONG cbChangeTotal) const +{ + ULONG cb = 0; + ULONG i; + + for (i = 0; i < Count(); i++) + { + CStreamChunk const *pscnkT = GetChunk(i); + + cb += pscnkT->cbChange; + if (pscnk == pscnkT) + { + PROPASSERT(cb == cbChangeTotal); + return; + } + } + PROPASSERT(i < Count()); +} +#endif + + +//+-------------------------------------------------------------------------- +// Member: fnChunkCompare +// +// Synopsis: qsort helper to compare chunks in the chunk list. +// +// Arguments: [pscnk1] -- pointer to chunk1 +// [pscnk2] -- pointer to chunk2 +// +// Returns: difference +//+-------------------------------------------------------------------------- + +INT _CRTAPI1 +fnChunkCompare(VOID const *pscnk1, VOID const *pscnk2) +{ + return(((CStreamChunk const *) pscnk1)->oOld - + ((CStreamChunk const *) pscnk2)->oOld); +} + + +//+-------------------------------------------------------------------------- +// Member: CStreamChunkList::SortByStartAddress +// +// Synopsis: sort all the chunks that are being modified in a stream in the +// ascending order. +// +// Arguments: None +// +// Returns: None +//+-------------------------------------------------------------------------- + +VOID +CStreamChunkList::SortByStartAddress(VOID) +{ + DebugTrace(0, Dbg, ("Sorting %l" szX " Chunks @%lx\n", _cChunks, _ascnk)); + + qsort(_ascnk, _cChunks, sizeof(_ascnk[0]), fnChunkCompare); + +#if DBGPROP + LONG cbChangeTotal; + ULONG i; + + cbChangeTotal = 0; + for (i = 0; i < _cChunks; i++) + { + cbChangeTotal += _ascnk[i].cbChange; + + DebugTrace(0, Dbg, ( + "Chunk[%l" szX "] oOld=%l" szX " cbChange=%s%l" szX + " cbChangeTotal=%s%l" szX "\n", + i, + _ascnk[i].oOld, + _ascnk[i].cbChange < 0? "-" : "", + _ascnk[i].cbChange < 0? -_ascnk[i].cbChange : _ascnk[i].cbChange, + cbChangeTotal < 0? "-" : "", + cbChangeTotal < 0? -cbChangeTotal : cbChangeTotal)); + } +#endif +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_GetFormatidOffset +// +// Synopsis: Get a pointer to the (first) section header +// +// Arguments: None +// +// Returns: pointer to section header +//+-------------------------------------------------------------------------- + +inline FORMATIDOFFSET * +CPropertySetStream::_GetFormatidOffset(ULONG iSection) const +{ + return(&((FORMATIDOFFSET *) Add2Ptr(_pph, sizeof(*_pph)))[iSection]); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_GetSectionHeader +// +// Synopsis: Get a pointer to the (first) section header +// +// Arguments: None +// +// Returns: pointer to section header +//+-------------------------------------------------------------------------- + +inline PROPERTYSECTIONHEADER * +CPropertySetStream::_GetSectionHeader(VOID) const +{ + return((PROPERTYSECTIONHEADER *) Add2Ptr(_pph, _oSection)); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_GetSectionHeader +// +// Synopsis: Get a pointer to the specified section header +// +// Arguments: [iSection] -- section number +// [pstatus] -- Pointer to NTSTATUS code. +// +// Returns: pointer to specified section header +//+-------------------------------------------------------------------------- + +PROPERTYSECTIONHEADER * +CPropertySetStream::_GetSectionHeader(ULONG iSection, OUT NTSTATUS *pstatus) +{ + *pstatus = STATUS_SUCCESS; + PROPERTYSECTIONHEADER *psh = NULL; + + ULONG oSection = 0; // Assume no header + ULONG cbstm = _MSTM(GetSize)(pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // Don't assume *any* class variables (except _pph) are loaded yet! + + PROPASSERT(iSection < _pph->reserved ); + + // Get the section offset, after verifying that we can read all + // of the FmtID/Offset table. + + if (cbstm >= CB_PROPERTYSETHEADER + (iSection + 1) * CB_FORMATIDOFFSET) + oSection = _GetFormatidOffset(iSection)->dwOffset; + else + StatusCorruption (pstatus, "GetSectionHeader(i): stream size too short to read section offset"); + + // Create a pointer to the section header, after verifying that we can + // read all of the section header. We don't verify that we can actually + // read the whole section (using cbSection), the caller must be responsible + // for this. + + // We have to check oSection first, then oSection+cb_psh, because oSection + // could be a negative number (such as 0xffffffff), so adding it to cb_psh + // could make it look valid. + + if (cbstm >= oSection + && + cbstm >= oSection + CB_PROPERTYSECTIONHEADER) + { + psh = (PROPERTYSECTIONHEADER *) Add2Ptr(_pph, oSection); + } + else + StatusCorruption (pstatus, "GetSectionHeader(i): stream size too short to read section header"); + + // Finally, ensure that the section is 32 bit aligned. We handle several + // compatibility problems in the _Fix* routines, but not a misaligned + // section header. + + if( !IsDwordAligned( psh )) + StatusCorruption( pstatus, "GetSectionHeader(i): section header is misaligned" ); + + + // ---- + // Exit + // ---- + +Exit: + + return(psh); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_SearchForCodePage, private +// +// Synopsis: Searches a section of a property set for the code page. +// +// This routine searches for the code page by iterating +// through the PID/Offset array in search of +// PID_CODEPAGE. The difference between calling +// this routine, and calling GetValue(PID_CODEPAGE), +// is that this routine does not assume that the +// property set is formatted correctly; it only assumes +// that the PID/Offset array is correct. +// +// Note that this routine is like a specialized _LoadProperty(), +// the important difference is that this routine must use +// unaligned pointers, since it cannot assume that the +// property set is aligned properly. +// +// Pre-Conditions: +// The PID/Offset array is correct. +// && +// _oSection & _cSection are set correctly. +// +// Post-Conditions: +// If PID_CODEPAGE exists, it is put into _CodePage. +// If it doesn't exist, _CodePage is left unchanged. +// +// Arguments: [pstatus] -- Pointer to NTSTATUS code. +// +// Notes: We do *not* assume that the property set's +// cbSection field is valid (this was added to handle a +// special-case compatibility problem). +// +// Returns: None. +//+-------------------------------------------------------------------------- + +VOID +CPropertySetStream::_SearchForCodePage( OUT NTSTATUS *pstatus ) +{ + + PROPERTYSECTIONHEADER UNALIGNED *psh; + PROPERTYIDOFFSET UNALIGNED *ppo; + PROPERTYIDOFFSET UNALIGNED *ppoMax; + + ULONG cbstm; + + *pstatus = STATUS_SUCCESS; + + // Verify the pre-conditions. + + PROPASSERT( _oSection != 0 ); + PROPASSERT( _cSection != 0 ); + + // It's invalid to call any function on a deleted + // DocSumInfo user-defined (section section) section. + + if (_State & CPSS_USERDEFINEDDELETED) + { + StatusAccessDenied(pstatus, "GetValue: deleted"); + goto Exit; + } + + // Get the section's header. + + psh = _GetSectionHeader(); + + // Ensure that we can at least read the section header and + // PID/Offset table. + + cbstm = _MSTM(GetSize)(pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + if (cbstm < _oSection + CB_PROPERTYSECTIONHEADER + || + cbstm < _oSection + CB_PROPERTYSECTIONHEADER + + psh->cProperties * CB_PROPERTYIDOFFSET + ) + { + StatusCorruption(pstatus, "_SearchForCodePage: stream too short to read section header"); + goto Exit; + } + + // Calculate the first & last PID/Offset pointers. + // We can't use _LoadPropertyOffsetPointers, because it assumes + // alignment. + + ppo = psh->rgprop; + ppoMax = psh->rgprop + psh->cProperties; + + // Search the PID/Offset array for PID_CODEPAGE + + for ( ; ppo < ppoMax; ppo++) + { + if (ppo->propid == PID_CODEPAGE) + { + SERIALIZEDPROPERTYVALUE UNALIGNED *pprop; + + // Get the real address of serialized property. + + pprop = (SERIALIZEDPROPERTYVALUE *) + _MapOffsetToAddress( ppo->dwOffset ); + + // Check for corruption. + + if ( ( (_oSection + ppo->dwOffset + CB_SERIALIZEDPROPERTYVALUE + sizeof(DWORD)) + > + cbstm + ) + || + PropByteSwap(pprop->dwType) != VT_I2 + ) + { + StatusCorruption(pstatus, "_SearchForCodePage"); + goto Exit; + } + + // Set the member code page from the serialized property. + // (The codepage is an I2). + + _CodePage = PropByteSwap( *(UNALIGNED USHORT *) &pprop->rgb ); + break; + + } // if (ppo->propid == PID_CODEPAGE) + } // for ( ; ppo < ppoMax; ppo++) + + // ---- + // Exit + // ---- + +Exit: + + return; + +} // CPropertySetStream::_SearchForCodePage() + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_MapOffsetToAddress, private +// +// Synopsis: maps an offset to an address +// +// Arguments: [Offset] -- the offset in the section +// +// Returns: ptr to the offset mapped +//+-------------------------------------------------------------------------- + +inline VOID * +CPropertySetStream::_MapOffsetToAddress(ULONG Offset) const +{ + PROPASSERT(_cSection != 0); + + return(Add2Ptr(_GetSectionHeader(), Offset)); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_MapAddressToOffset, private +// +// Synopsis: maps an address to an offset +// +// Arguments: [pvAddr] -- the address in the section +// +// Returns: section-relative offset for passed pointer +//+-------------------------------------------------------------------------- + +inline ULONG +CPropertySetStream::_MapAddressToOffset(VOID const *pvAddr) const +{ + PROPASSERT(_cSection != 0); + + // Get a ptr to the section header. + VOID const *pvSectionHeader = _GetSectionHeader(); + + PROPASSERT((BYTE const *) pvAddr >= (BYTE const *) pvSectionHeader); + return((BYTE const *) pvAddr - (BYTE const *) pvSectionHeader); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_MapAbsOffsetToAddress, private +// +// Synopsis: maps an address to an offset +// +// Arguments: [oAbsolute] -- the absolute offset +// +// Returns: a ptr to the offset mapped +//+-------------------------------------------------------------------------- + +inline VOID * +CPropertySetStream::_MapAbsOffsetToAddress(ULONG oAbsolute) const +{ + return(Add2Ptr(_pph, oAbsolute)); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_MapAddressToAbsOffset, private +// +// Synopsis: maps an address to an offset +// +// Arguments: [pvAddr] -- the address +// +// Returns: the absolute offset +//+-------------------------------------------------------------------------- + +inline ULONG +CPropertySetStream::_MapAddressToAbsOffset(VOID const *pvAddr) const +{ + PROPASSERT((BYTE const *) pvAddr >= (BYTE *) _pph); + return((BYTE const *) pvAddr - (BYTE *) _pph); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::CPropertySetStream +// +// Synopsis: constructor for property set class +// +// Arguments:UK [Flags] -- NONSIMPLE|*1* of READ/WRITE/CREATE/CREATEIF/DELETE +// K [pscb] -- SCB for property stream +// K [pirpc] -- pointer to Irp Context +// K [State] -- CPSS_PROPHEADER +// U [pmstm] -- mapped stream implementation +// U [pma] -- caller's memory allocator +// +// Returns: None +//--------------------------------------------------------------------------- + +CPropertySetStream::CPropertySetStream( + IN USHORT Flags, // NONSIMPLE|*1* of READ/WRITE/CREATE/CREATEIF/DELETE +#ifdef KERNEL + IN SCB *pscb, + IN IRPCONTEXT *pirpc, + IN BYTE State +#else + IN CMappedStream *pmstm, // mapped stream impelementation + IN PMemoryAllocator *pma // caller's memory allocator +#endif + ) : + _Flags((BYTE) Flags), +#ifdef KERNEL + _State(State), + _mstm( + pscb, + pirpc, + (Flags & CREATEPROP_MODEMASK) == CREATEPROP_READ? + KMS_PROPSET : (KMS_PROPSET | KMS_WRITE)), +#else + _State(0), + _pmstm(pmstm), + _pma(pma), +#endif + _pph(NULL) +{ + _CodePage = CP_CREATEDEFAULT(_State); // Default if not present + PROPASSERT(_Flags == Flags); // Should fit in a byte +#ifdef KERNEL + PROPASSERT((_State & ~CPSS_PROPHEADER) == 0); +#endif + + _oSection = 0; + _cSection = 0; + _cbTail = 0; + +} + + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::Close +// +// Synopsis: shutdown property set prior to calling destructor +// +// Arguments: [pstatus] -- Pointer to NTSTATUS code. +// +// Returns: None +//--------------------------------------------------------------------------- + +VOID +CPropertySetStream::Close(OUT NTSTATUS *pstatus) +{ + *pstatus = STATUS_SUCCESS; + + // Validate the byte-order (_pph could be NULL in certain + // close scenarios, e.g. an RtlCreatePropertySet fails). + PROPASSERT(NULL == _pph || PROPSET_BYTEORDER == _pph->wByteOrder); + + PROPASSERT( + (_Flags & CREATEPROP_MODEMASK) != CREATEPROP_READ || + !IsModified()); + + _MSTM(Unmap)(IsModified(), (VOID **) &_pph); + + _MSTM(Close)(pstatus); +// if( !NT_SUCCESS(*pstatus) ) goto Exit; + +//Exit: + + return; +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::Open +// +// Synopsis: Open property set image +// +// Arguments: None +// +// Returns: None +//--------------------------------------------------------------------------- + +VOID +CPropertySetStream::Open( + IN GUID const *pfmtid, // property set fmtid + OPTIONAL IN GUID const *pclsid, // CLASSID of propset code (create only) + IN ULONG LocaleId, // Locale Id (create only) + OPTIONAL OUT ULONG *pOSVersion, // OS Version from header + IN USHORT CodePage, // CodePage of property set (create only) + OUT NTSTATUS *pstatus + ) +{ + *pstatus = STATUS_SUCCESS; + LOADSTATE LoadState; + PROPASSERT(!_IsMapped()); + + if( pOSVersion != NULL ) + *pOSVersion = PROPSETHDR_OSVERSION_UNKNOWN; + + // Open the underlying stream which holds the property set. + // We give it a callback pointer so that it can call + // RtlOnMappedStreamEvent. + + _MSTM(Open)(this, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // Load the header, including fixing the in-memory image of + // poorly-formatted property sets. + + LoadState = _LoadHeader(pfmtid, _Flags & CREATEPROP_MODEMASK, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + if (LoadState != LOADSTATE_DONE) + { + switch (_Flags & CREATEPROP_MODEMASK) + { + case CREATEPROP_READ: + case CREATEPROP_WRITE: + if (LoadState == LOADSTATE_FAIL) + { + StatusCorruption(pstatus, "Open: _LoadHeader"); + goto Exit; + } + PROPASSERT( + LoadState == LOADSTATE_BADFMTID || + LoadState == LOADSTATE_USERDEFINEDNOTFOUND); + DebugTrace(0, DEBTRACE_ERROR, ( + "_LoadHeader: LoadState=%x\n", LoadState)); + + *pstatus = STATUS_PROPSET_NOT_FOUND; + goto Exit; + } + + _Create( + pfmtid, + pclsid, + LocaleId, + CodePage, + LoadState, + pstatus + ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + } // if (LoadState != LOADSTATE_DONE) + + PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder); + + if (_HasPropHeader() && + (_pph->dwOSVer == PROPSETVER_WIN310 || + _pph->dwOSVer == PROPSETVER_WIN333)) + { + DebugTrace(0, DEBTRACE_PROPPATCH, ( + "Open(%s) downlevel: %x\n", + (_Flags & CREATEPROP_MODEMASK) == CREATEPROP_READ? "Read" : "Write", + _Flags)); + _State |= CPSS_DOWNLEVEL; + } + + if ((_Flags & CREATEPROP_MODEMASK) != CREATEPROP_READ) + { + if (_State & CPSS_PACKEDPROPERTIES) + { + StatusAccessDenied(pstatus, "Open: writing Unaligned propset"); + goto Exit; + } + if ((_State & (CPSS_MULTIPLESECTIONS | CPSS_DOCUMENTSUMMARYINFO)) == + CPSS_MULTIPLESECTIONS) + { + StatusAccessDenied(pstatus, "Open: writing unknown multiple section propset"); + goto Exit; + } + } + + // Return the OS Version to the caller. + + if( pOSVersion != NULL ) + *pOSVersion = _pph->dwOSVer; + + // ---- + // Exit + // ---- + +Exit: + + return; +} + + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::ReOpen +// +// Synopsis: ReOpen property set image +// +// Arguments: [pstatus] -- Pointer to NSTATUS code. +// +// Returns: Number of properties. +//--------------------------------------------------------------------------- + +ULONG +CPropertySetStream::ReOpen(OUT NTSTATUS *pstatus) +{ + LOADSTATE LoadState; + PROPERTYSECTIONHEADER const *psh; + ULONG cProperties = 0; + + *pstatus = STATUS_SUCCESS; + + PROPASSERT(_IsMapped()); + + _MSTM(ReOpen)((VOID **) &_pph, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + if (_State & CPSS_USERDEFINEDDELETED) + { + goto Exit; + } + + LoadState = _LoadHeader(NULL, + CREATEPROP_READ, // all we need is !create + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + if (LoadState != LOADSTATE_DONE) + { + DebugTrace(0, DEBTRACE_ERROR, ( + "ReOpen: LoadState=%lx\n", + LoadState)); + StatusCorruption(pstatus, "ReOpen: _LoadHeader"); + goto Exit; + } + + PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder); + + psh = _GetSectionHeader(); + PROPASSERT(psh != NULL); + + cProperties = psh->cProperties; + + // ---- + // Exit + // ---- + +Exit: + + return( cProperties ); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_InitSection +// +// Synopsis: Initialize a section header and the default properties. +// +// Arguments: [pfo] -- pointer to section info +// [LocaleId] -- Locale Id +// +// Returns: None +//--------------------------------------------------------------------------- + + // Serialized Code-Page size +#define CB_CODEPAGE (sizeof(ULONG) + DwordAlign(sizeof(USHORT))) + + // Serialized Locale ID (LCID) size. +#define CB_LOCALE (sizeof(ULONG) + sizeof(ULONG)) + + // Minimum section size (minimum has Code Page & LCID) +#define CB_MINSECTIONSIZE (CB_PROPERTYSECTIONHEADER \ + + 2 * CB_PROPERTYIDOFFSET \ + + CB_CODEPAGE \ + + CB_LOCALE) + + // Minimum serialized dictionary size (a dict with no entries). +#define CB_EMPTYDICTSIZE (sizeof(DWORD)) // Entry count + + // Minimum User-Defined section size (in DocumentSummaryInformation propset). + // (Must include an empty dictionary & a PID/Offset for it.) +#define CB_MINUSERDEFSECTIONSIZE \ + (CB_MINSECTIONSIZE \ + + \ + CB_PROPERTYIDOFFSET \ + + \ + CB_EMPTYDICTSIZE) + +VOID +CPropertySetStream::_InitSection( + IN FORMATIDOFFSET *pfo, + IN ULONG LocaleId, + IN BOOL fCreateDictionary // Create an empty dictionary? + ) +{ + PROPERTYSECTIONHEADER *psh; + + ULONG ulPropIndex; // Index into the PID/Offset array. + DWORD dwPropValOffset; // The offset to where the next prop val will be written. + // Pointer to a serialized property value. + SERIALIZEDPROPERTYVALUE *pprop; + + psh = (PROPERTYSECTIONHEADER *) _MapAbsOffsetToAddress(pfo->dwOffset); + + // Set the property count and section size in the section header. + // This must account for the Code Page and Locale ID properties, and + // might need to account for an empty dictionary property. + // dwPropValOffset identifies the location of the next property value + // to be written. + + if( fCreateDictionary ) + { + // Three properties: Code Page, LCID, and Dictionary. + + psh->cProperties = 3; + dwPropValOffset = CB_PROPERTYSECTIONHEADER + 3 * CB_PROPERTYIDOFFSET; + psh->cbSection = CB_MINUSERDEFSECTIONSIZE; + } + else + { + // Two properties: Code Page and LCID (no dictionary). + + psh->cProperties = 2; + dwPropValOffset = CB_PROPERTYSECTIONHEADER + 2 * CB_PROPERTYIDOFFSET; + psh->cbSection = CB_MINSECTIONSIZE; + } + + + ulPropIndex = 0; + + // If requested by the caller, create a dictionary property, but + // leave the dictionary empty. We always create this first. It shouldn't + // matter where it's located, but Office95 requires it + // and it doesn't do any harm to put it there. + + if( fCreateDictionary ) + { + // Fill in the PID/Offset table. + + psh->rgprop[ ulPropIndex ].propid = PID_DICTIONARY; + psh->rgprop[ ulPropIndex ].dwOffset = dwPropValOffset; + + // Fill in the property value. + + pprop = (SERIALIZEDPROPERTYVALUE *) Add2Ptr( psh, dwPropValOffset ); + pprop->dwType = 0L; // For the dictonary, this is actually the entry count. + + // Advance the table & value indices. + + ulPropIndex++; + dwPropValOffset += CB_EMPTYDICTSIZE; + + } // if( fCreateDictionary ) + + + // Write the code page. We write a zero first to initialize + // the padding bytes. + + psh->rgprop[ ulPropIndex ].propid = PID_CODEPAGE; + psh->rgprop[ ulPropIndex ].dwOffset = dwPropValOffset; + + pprop = (SERIALIZEDPROPERTYVALUE *) Add2Ptr( psh, dwPropValOffset ); + pprop->dwType = PropByteSwap((DWORD) VT_I2); + *(DWORD *) pprop->rgb = 0; // Zero out extra two bytes. + *(WORD *) pprop->rgb = PropByteSwap( _CodePage ); + + ulPropIndex++; + dwPropValOffset += CB_CODEPAGE; + + + // Write the Locale ID. + + psh->rgprop[ ulPropIndex ].propid = PID_LOCALE; + psh->rgprop[ ulPropIndex ].dwOffset = dwPropValOffset; + + pprop = (SERIALIZEDPROPERTYVALUE *) Add2Ptr(psh, dwPropValOffset ); + pprop->dwType = PropByteSwap( (DWORD) VT_UI4 ); + *(DWORD *) pprop->rgb = PropByteSwap( (DWORD) LocaleId ); + +} + + + + +//+--------------------------------------------------------------------------- +// Member: _MultiByteToWideChar, private +// +// Synopsis: Convert a MultiByte string to a Unicode string, +// using the _pma memory allocator if necessary. +// +// Arguments: [pch] -- pointer to MultiByte string +// [cb] -- byte length of MultiByte string +// (-1 if null terminated) +// [CodePage] -- Codepage of input string. +// [ppwc] -- pointer to pointer to converted string +// (if *ppwc is NULL, it will be alloced, +// if non-NULL, *ppwc must be *pcb bytes long). +// [pcb] -- IN: byte length of *ppwc +// OUT: byte length of Unicode string. +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: Nothing +//--------------------------------------------------------------------------- + +VOID +CPropertySetStream::_MultiByteToWideChar( + IN CHAR const *pch, + IN ULONG cb, + IN USHORT CodePage, + OUT WCHAR **ppwc, + OUT ULONG *pcb, + OUT NTSTATUS *pstatus) +{ + // ------ + // Locals + // ------ + + // Did we allocate *ppwc? + BOOL fAlloc = FALSE; + + // -------------- + // Initialization + // -------------- + + *pstatus = STATUS_SUCCESS; + + PROPASSERT(pch != NULL); + PROPASSERT(ppwc != NULL); + PROPASSERT(pcb != NULL); + + PROPASSERT(IsAnsiString(pch, ((ULONG)-1 == cb ) ? MAXULONG : cb)); + + PROPASSERT(NULL != *ppwc || 0 == *pcb); + PROPASSERT(UnicodeCallouts.pfnMultiByteToWideChar != NULL); + + // ------------------ + // Convert the String + // ------------------ + + // We will pass through this loop once (if the caller provided a buffer + // or twice (otherwise). + + while (TRUE) + { + // Attempt to convert the string. + + *pcb = (*UnicodeCallouts.pfnMultiByteToWideChar)( + CodePage, // Source codepage + 0, // Flags + pch, // Source string + cb, // Source string length + *ppwc, // Target string + *pcb); // Size of target string buffer + + // The converted length should never be zero. + if (0 == *pcb) + { + // If we alloced a buffer, free it now. + if( fAlloc ) + { + _pma->Free( *ppwc ); + *ppwc = NULL; + } + + // If there was an error, assume that it was a code-page + // incompatibility problem. + + StatusError(pstatus, "_MultiByteToWideChar error", + STATUS_UNMAPPABLE_CHARACTER); + goto Exit; + } + + // There was no error. If we provided a non-NULL buffer, + // then the conversion was performed and we're done. + + *pcb *= sizeof(WCHAR); // cch => cb + if (*ppwc != NULL) + { + DebugTrace(0, DEBTRACE_PROPERTY, ( + "_MultiByteToWideChar: pch='%s'[%x] pwc='%ws'[%x->%x]\n", + pch, + cb, + *ppwc, + *pcb, + *pcb * sizeof(WCHAR))); + break; + } + + // We haven't actually the string yet. Now that + // we know the length, we can allocate a buffer and try the + // conversion for real. + + *ppwc = (WCHAR *) _pma->Allocate( *pcb ); + if (NULL == *ppwc) + { + StatusNoMemory(pstatus, "_MultiByteToWideChar: no memory"); + goto Exit; + } + fAlloc = TRUE; + + } // while(TRUE) + + // ---- + // Exit + // ---- + +Exit: + + return; + +} // CPropertySetStream::_MultiByteToWideChar + + + +//+--------------------------------------------------------------------------- +// Member: _WideCharToMultiByte, private +// +// Synopsis: Convert a Unicode string to a MultiByte string, +// using the _pma memory allocator if necessary. +// +// Arguments: [pwc] -- pointer to Unicode string +// [cch] -- character length of Unicode string +// (-1 if null terminated) +// [CodePage] -- codepage of target string +// [ppch] -- pointer to pointer to converted string +// (if *ppch is NULL, it will be alloced, +// if non-NULL, *ppch must be *pcb bytes long). +// [pcb] -- IN: byte length of *ppch +// OUT: byte length of MultiByte string +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: Nothing +//--------------------------------------------------------------------------- + +VOID +CPropertySetStream::_WideCharToMultiByte( + IN WCHAR const *pwc, + IN ULONG cch, + IN USHORT CodePage, + OUT CHAR **ppch, + OUT ULONG *pcb, + OUT NTSTATUS *pstatus) +{ + // ------ + // Locals + // ------ + + // Did we allocate *ppch? + BOOL fAlloc = FALSE; + + // -------------- + // Initialization + // -------------- + + *pstatus = STATUS_SUCCESS; + + PROPASSERT(pwc != NULL); + PROPASSERT(ppch != NULL); + PROPASSERT(pcb != NULL); + + PROPASSERT(IsUnicodeString(pwc, ((ULONG)-1 == cch ) ? MAXULONG : cch*sizeof(WCHAR))); + + PROPASSERT(NULL != *ppch || 0 == *pcb); + PROPASSERT(UnicodeCallouts.pfnWideCharToMultiByte != NULL); + + // ------------------ + // Convert the String + // ------------------ + + // We will pass through this loop once (if the caller provided a buffer + // or twice (otherwise). + + while (TRUE) + { + // Attempt the conversion. + *pcb = (*UnicodeCallouts.pfnWideCharToMultiByte)( + CodePage, // Codepage to convert to + 0, // Flags + pwc, // Source string + cch, // Size of source string + *ppch, // Target string + *pcb, // Size of target string buffer + NULL, // lpDefaultChar + NULL); // lpUsedDefaultChar + + // A converted length of zero indicates an error. + if (0 == *pcb) + { + // If we allocated a buffer in this routine, free it. + if( fAlloc ) + { + _pma->Free( *ppch ); + *ppch = NULL; + } + + // If there was an error, assume that it was a code-page + // incompatibility problem. + + StatusError(pstatus, "_WideCharToMultiByte: WideCharToMultiByte error", + STATUS_UNMAPPABLE_CHARACTER); + goto Exit; + } + + // If we have a non-zero length, and we provided a buffer, + // then we're done (successfully). + + if (*ppch != NULL) + { + DebugTrace(0, DEBTRACE_PROPERTY, ( + "_WideCharToMultiByte: pwc='%ws'[%x] pch='%s'[%x->%x]\n", + pwc, + cch, + *ppch, + *pcb, + *pcb)); + break; + } + + // There were no errors, but we need to allocate a buffer + // to do the actual conversion. + + *ppch = (CHAR*) _pma->Allocate( *pcb ); + if (*ppch == NULL) + { + StatusNoMemory(pstatus, "_WideCharToMultiByte: no memory"); + goto Exit; + } + fAlloc = TRUE; + + } // while (TRUE) + + + // ---- + // Exit + // ---- + +Exit: + + return; + +} // CPropertySetStream::_WideCharToMultiByte + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::ByteSwapHeaders +// +// Synopsis: Byte-swap the headers of a property set header +// (both the propset header and any section headers). +// +// Arguments: [PROPERTYSETHEADER*] pph +// Pointer to the beginning of the property set. +// [ULONG] cbstm +// Total size of the property stream. +// [NTSTATUS*] pstatus +// Pointer to NTSTATUS code. +// +// Pre-Conditions: +// There are no more than two sections. +// +// Note that this routine does not assume anything +// about the current state of the CPropertySetStream +// (it accesses no member variables). +// +// Post-Conditions: +// If the property set headers are valid, the +// propset and section headers are byte-swapped. +// Note that if the property set is invalid, this +// routine may only partially swap it. Therefore, +// the caller must ensure in this case that no +// attempt is made to use the property set. +// +// Returns: None. *pstatus will only be non-successful +// if the Stream was too small for the property set +// (i.e, the property set is corrupt). If the caller +// knows this not to be the case, then it can assume +// that this routine will return STATUS_SUCCESS. +// +//--------------------------------------------------------------------------- + +VOID +CPropertySetStream::ByteSwapHeaders( IN PROPERTYSETHEADER *pph, + IN DWORD cbstm, + OUT NTSTATUS *pstatus ) +{ +#if LITTLEENDIAN + + *pstatus = STATUS_SUCCESS; + return; + +#else + + // ------ + // Locals + // ------ + + ULONG cSections; + ULONG ulIndex, ulSectionIndex; + + // pfoPropSet points into pph, pfoReal is a local copy + // in the system's endian-ness. + FORMATIDOFFSET *pfoPropSet, pfoReal[2]; + + // Pointers into pph. + PROPERTYSECTIONHEADER *psh = NULL; + PROPERTYIDOFFSET *po = NULL; + + // Are we converting *to* the system's endian-ness? + BOOL fToSystemEndian; + + // ---------- + // Initialize + // ---------- + + *pstatus = STATUS_SUCCESS; + PROPASSERT( NULL != pph ); + PROPASSERT(PROPSET_BYTEORDER == pph->wByteOrder + || + PROPSET_BYTEORDER == ByteSwap( pph->wByteOrder ) + ); + + + // ---------------------------- + // Swap the Property Set header + // ---------------------------- + + // Validate the stream length. + if( sizeof(*pph) > cbstm ) + { + StatusCorruption(pstatus, "CPropertySetStream::ByteSwapHeaders: PropertySet header size"); + goto Exit; + } + + // Swap the fields in place. + PropByteSwap( &pph->wByteOrder ); + PropByteSwap( &pph->wFormat ); + PropByteSwap( &pph->dwOSVer ); + PropByteSwap( &pph->clsid ); + PropByteSwap( &pph->reserved ); + + // Are we converting to little-endian? + if( PROPSET_BYTEORDER == pph->wByteOrder) + fToSystemEndian = TRUE; + else + { + fToSystemEndian = FALSE; + PROPASSERT( PROPSET_BYTEORDER == PropByteSwap(pph->wByteOrder) ); + } + + // Get the correctly-endianed section count and validate. + + cSections = fToSystemEndian ? pph->reserved + : PropByteSwap( pph->reserved ); + + if( cSections > 2 ) + { + StatusCorruption(pstatus, "CPropertySetStream::ByteSwapHeaders: PropertySet header size"); + goto Exit; + } + + // ------------------------- + // Swap the per-section data + // ------------------------- + + pfoPropSet = (FORMATIDOFFSET*) ((BYTE*) pph + sizeof(*pph)); + + for( ulSectionIndex = 0; ulSectionIndex < cSections; ulSectionIndex++ ) + { + ULONG cbSection, cProperties; + + // ------------------------------ + // Swap the FormatID/Offset entry + // ------------------------------ + + // Is the Stream long enough for the array? + if( cbstm < (ULONG) &pfoPropSet[ulSectionIndex] + + sizeof(*pfoPropSet) + - (ULONG) pph ) + { + StatusCorruption(pstatus, + "CPropertySetStream::_ByteSwapHeaders: FormatID/Offset size"); + goto Exit; + } + + // Get a local copy of this FMTID/Offset array entry + // If it is propset-endian format, swap to make usable. + + pfoReal[ ulSectionIndex ].fmtid = pfoPropSet[ulSectionIndex].fmtid; + pfoReal[ ulSectionIndex ].dwOffset = pfoPropSet[ulSectionIndex].dwOffset; + + if( fToSystemEndian ) + { + PropByteSwap( &pfoReal[ulSectionIndex].fmtid ); + PropByteSwap( &pfoReal[ulSectionIndex].dwOffset ); + } + + // Swap this FMTID/Offset entry in place. + PropByteSwap( &pfoPropSet[ulSectionIndex].fmtid ); + PropByteSwap( &pfoPropSet[ulSectionIndex].dwOffset ); + + + // ----------------------- + // Swap the section header + // ----------------------- + + // Locate the section header and the first entry in the + // PID/Offset table. + + psh = (PROPERTYSECTIONHEADER*) + ( (BYTE*) pph + pfoReal[ ulSectionIndex ].dwOffset ); + + po = (PROPERTYIDOFFSET*) + ( (BYTE*) psh + sizeof(psh->cbSection) + sizeof(psh->cProperties) ); + + // Validate that we can see up to the PID/Offset table. + if( cbstm < (ULONG) ((BYTE*) po - (BYTE*) pph) ) + { + StatusCorruption(pstatus, + "CPropertySetStream::ByteSwapHeaders: Section header size"); + goto Exit; + } + + // Get local copies of the section & property counts. + // Again we may need to swap them from propset-endian format + // in order to make them usable. + + cbSection = psh->cbSection; + cProperties = psh->cProperties; + + if( fToSystemEndian) + { + PropByteSwap( &cbSection ); + PropByteSwap( &cProperties ); + } + + // Swap the two fields at the top of the section header. + + PropByteSwap( &psh->cbSection ); + PropByteSwap( &psh->cProperties ); + + // ------------------------- + // Swap the PID/Offset table + // ------------------------- + + // Validate that we can see the whole table. + if( cbstm < (BYTE*) po - (BYTE*) pph + cProperties * sizeof(*po) ) + { + StatusCorruption(pstatus, + "CPropertySetStream::ByteSwapHeaders: Section header size"); + goto Exit; + } + + // Swap each of the array entries. + for( ulIndex = 0; ulIndex < cProperties; ulIndex++ ) + { + PropByteSwap( &po[ulIndex].propid ); + PropByteSwap( &po[ulIndex].dwOffset ); + } + + } // for( ulSectionIndex = 0; ulSectionIndex < cSections, ulIndex++ ) + + // ---- + // Exit + // ---- + +Exit: + + return; + +#endif // #if LITTLEENDIAN ... #else + +} // CPropertySetStream::ByteSwapHeaders + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_CreateUserDefinedSection +// +// Synopsis: Create second property section +// +// Arguments: [LoadState] -- _LoadHeader returned state +// [LocaleId] -- Locale Id +// [pstatus] -- Pointer to NTSTATUS code. +// +// Returns: TRUE if LoadState handled successfully. If TRUE, +// *pstatus will be STATUS_SUCCESS. +//--------------------------------------------------------------------------- + +#ifndef KERNEL +BOOLEAN +CPropertySetStream::_CreateUserDefinedSection( + IN LOADSTATE LoadState, + IN ULONG LocaleId, + OUT NTSTATUS *pstatus) +{ + BOOL fSuccess = FALSE; + FORMATIDOFFSET *pfo; + ULONG cbstmNew; + PROPERTYSECTIONHEADER *psh; + + *pstatus = STATUS_SUCCESS; + + PROPASSERT(_State & CPSS_USERDEFINEDPROPERTIES); + switch (_Flags & CREATEPROP_MODEMASK) + { + case CREATEPROP_CREATEIF: + case CREATEPROP_CREATE: + if (LoadState == LOADSTATE_USERDEFINEDNOTFOUND) + { + ULONG cbmove; + + PROPASSERT(_cSection == 1); + pfo = _GetFormatidOffset(0); + PROPASSERT(pfo->fmtid == guidDocumentSummary); + PROPASSERT(IsDwordAligned(pfo->dwOffset)); + + // Get a pointer to the first section header, using the + // FmtID/Offset array. + + psh = (PROPERTYSECTIONHEADER *) _MapAbsOffsetToAddress(pfo->dwOffset); + + // Determine if we need to move the first section back in order + // to make room for this new entry in the FmtID/Offset array. + + cbmove = 0; + if (pfo->dwOffset < CB_PROPERTYSETHEADER + 2 * CB_FORMATIDOFFSET) + { + cbmove = CB_PROPERTYSETHEADER + 2*CB_FORMATIDOFFSET - pfo->dwOffset; + } + + // How big should the Stream be? + + cbstmNew = pfo->dwOffset // The offset of the first section + + + cbmove // Room for new FormatID/Offset array entry + + // Size of first section + DwordAlign(psh->cbSection) + + // Size of User-Defined section. + CB_MINUSERDEFSECTIONSIZE; + + // Set the stream size. + + _MSTM(SetSize)(cbstmNew, TRUE, (VOID **) &_pph, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // reload all pointers into mapped image: + + pfo = _GetFormatidOffset(0); + psh = (PROPERTYSECTIONHEADER *) _MapAbsOffsetToAddress(pfo->dwOffset); + + if (cbmove != 0) + { + // Move section back to make room for new FORMATIDOFFSET entry + + PropMoveMemory( + "_AddSection", + psh, + Add2Ptr(psh, cbmove), + psh, + psh->cbSection); + + pfo->dwOffset += cbmove; + PROPASSERT(IsDwordAligned(pfo->dwOffset)); + } + + psh->cbSection = DwordAlign(psh->cbSection); + + PROPASSERT(_oSection == 0); + PROPASSERT(_cSection == 1); + PROPASSERT(_pph->reserved == 1); + + _cSection++; + _pph->reserved++; + + _oSection = pfo->dwOffset + psh->cbSection; + pfo = _GetFormatidOffset(1); + pfo->fmtid = guidDocumentSummarySection2; + pfo->dwOffset = _oSection; + _InitSection(pfo, + LocaleId, + TRUE ); // Create an empty dictionary. + + fSuccess = TRUE; + } + break; + + case CREATEPROP_DELETE: + PROPASSERT( + LoadState == LOADSTATE_USERDEFINEDDELETE || + LoadState == LOADSTATE_USERDEFINEDNOTFOUND); + if (LoadState == LOADSTATE_USERDEFINEDDELETE) + { + PROPASSERT(_cSection == 2); + PROPASSERT(_pph->reserved == 2); + pfo = _GetFormatidOffset(1); + RtlZeroMemory(pfo, sizeof(*pfo)); + + _cSection--; + _pph->reserved--; + pfo = _GetFormatidOffset(0); + PROPASSERT(pfo->fmtid == guidDocumentSummary); + PROPASSERT(IsDwordAligned(pfo->dwOffset)); + psh = (PROPERTYSECTIONHEADER *) + _MapAbsOffsetToAddress(pfo->dwOffset); + psh->cbSection = DwordAlign(psh->cbSection); + cbstmNew = pfo->dwOffset + psh->cbSection; + + _MSTM(SetSize)(cbstmNew, TRUE, (VOID **) &_pph, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + } + _State |= CPSS_USERDEFINEDDELETED; + + fSuccess = TRUE; + break; + + default: + PROPASSERT(!"_Flags: bad open mode"); + } + + // ---- + // Exit + // ---- + +Exit: + + return( fSuccess ); +} +#endif + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_Create +// +// Synopsis: Create property set image +// +// Arguments: [pfmtid] -- format id +// [pclsid] -- class id +// [LocaleId] -- Locale Id +// [CodePage] -- CodePage +// [LoadState] -- _LoadHeader returned state +// +// Returns: None +//--------------------------------------------------------------------------- + +VOID +CPropertySetStream::_Create( + IN GUID const *pfmtid, + OPTIONAL IN GUID const *pclsid, + IN ULONG LocaleId, // Locale Id (create only) + IN USHORT CodePage, + IN LOADSTATE LoadState, + OUT NTSTATUS *pstatus + ) +{ + ULONG cb; + FORMATIDOFFSET *pfo; + + *pstatus = STATUS_SUCCESS; + + _SetModified(); + + // Set the size of the stream to correspond to the header for the + // property set as well as the section. + + _CodePage = CodePage; + ULONG cSectionT = 1; + + // Are we creating the UserDefined property set + // (the second section of the DocumentSummaryInformation + // property set)? + + if (_State & CPSS_USERDEFINEDPROPERTIES) + { + // Create the UD propset, and set the cSection. + // If this routine returns TRUE, it means that + // the first section already existed, and we're done. + // Otherwise, we must continue and create the first section. + + if (_CreateUserDefinedSection(LoadState, LocaleId, pstatus)) + { + // If we get here, we know that *pstatus is Success. + + if (pclsid != NULL) + { + _pph->clsid = *pclsid; + } + goto Exit; + } + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + cSectionT = 2; + } + + // Calculate the exact size of the Stream (we know exactly + // what it will be because we only initialize the set(s) with + // fixed size data). + + PROPASSERT( 1 <= cSectionT && cSectionT <= 2 ); + cb = CB_PROPERTYSETHEADER // The size of the propset header. + + // The size of the FmtID/Offset array + cSectionT * CB_FORMATIDOFFSET + + + CB_MINSECTIONSIZE // The size of the first section + + // Maybe the size of the User-Defined section + ( cSectionT <= 1 ? 0 : CB_MINUSERDEFSECTIONSIZE ); + + + DebugTrace(0, Dbg, ("SetSize(%x) init\n", cb)); + + // Set the size of the stream + _MSTM(SetSize)(cb, TRUE, (VOID **) &_pph, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // And get a mapping of the Stream. + _MSTM(Map)(TRUE, (VOID **) &_pph); + RtlZeroMemory(_pph, cb); // Zeros classid, fmtid(s), etc + + // Initialize the OS Version in the header. + // Getting the current OS version depends on the OS. + +#if defined(_MAC) + + { + // Get the Mac System Version (e.g., 7.53). If we get an API error, + // we won't treat it as fatal, we'll just set the version to 0. + + OSErr oserr; + SysEnvRec theWorld; + oserr = SysEnvirons( curSysEnvVers, &theWorld ); + PROPASSERT( noErr == oserr ); + + if( noErr == oserr ) + { + _pph->dwOSVer = MAKEPSVER( OSKIND_MACINTOSH, + HIBYTE(theWorld.systemVersion), // Major + LOBYTE(theWorld.systemVersion) );// Minor + } + else + { + _pph->dwOSVer = MAKEPSVER( OSKIND_MACINTOSH, 0, 0 ); + } + + } + +#elif defined(IPROPERTY_DLL) + + { + // Get the Windows version. + DWORD dwWinVersion = GetVersion(); + + // Use it to set the OSVersion + _pph->dwOSVer = MAKEPSVER( OSKIND_WIN32, + LOBYTE(LOWORD( dwWinVersion )), // Major + HIBYTE(LOWORD( dwWinVersion )) ); // Minor + } + +#else // #if defined(_MAC) ... #elif defined(IPROPERTY_DLL) + + // Since we're part of the system, we can hard-code the OSVersion, + // and save the expense of an API call. + + _pph->dwOSVer = PROPSETVER_CURRENT; + +#endif // #if defined(_MAC) ... #elif ... #else + + // Initialize the rest of the header. + + _pph->wByteOrder = 0xfffe; + //_pph->wFormat = 0; // RtlZeroMemory does this + PROPASSERT(_pph->wFormat == 0); + + if (pclsid != NULL) + { + _pph->clsid = *pclsid; + } + _pph->reserved = cSectionT; + + // Initialize the format id offset for the section(s). + + pfo = _GetFormatidOffset(0); + pfo->dwOffset = CB_PROPERTYSETHEADER + cSectionT * CB_FORMATIDOFFSET; + + // Are we creating the second section of the DocSumInfo property set? + + if (cSectionT == 2) + { + // We need to initialize any empty first section. + + pfo->fmtid = guidDocumentSummary; + + _InitSection(pfo, + LocaleId, + FALSE); // Don't create an empty dictionary. + + // Advance the FmtID/Offset table pointer to the second entry, + // and set it's offset to just beyond the first section. + + pfo = _GetFormatidOffset(1); + pfo->dwOffset = CB_PROPERTYSETHEADER + + cSectionT * CB_FORMATIDOFFSET + + CB_MINSECTIONSIZE; + } + + // Initialize the requested property set. + + PROPASSERT(pfmtid != NULL); + pfo->fmtid = *pfmtid; + _InitSection(pfo, + LocaleId, + // TRUE => Create an empty dictionary + pfo->fmtid == guidDocumentSummarySection2 ); + + _cSection = cSectionT; + _oSection = pfo->dwOffset; + + + // ---- + // Exit + // ---- + +Exit: + + return; + +} // CPropertySetStream::_Create + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_LoadHeader +// +// Synopsis: verify header of a property set and read the code page +// +// Arguments: [pfmtid] -- format id +// [Mode] -- open mode +// [pstatus] -- Pointer to NTSTATUS code. +// +// Returns: LOADSTATE +//--------------------------------------------------------------------------- + +LOADSTATE +CPropertySetStream::_LoadHeader( + OPTIONAL IN GUID const *pfmtid, + IN BYTE Mode, + OUT NTSTATUS *pstatus) +{ + LOADSTATE loadstate = LOADSTATE_FAIL; + ULONG cbstm, cbMin; + PROPERTYSECTIONHEADER *psh; + FORMATIDOFFSET const *pfo; + BOOLEAN fSummaryInformation = FALSE; +#if DBGPROP + BOOLEAN fFirst = _pph == NULL; +#endif + + *pstatus = STATUS_SUCCESS; + + PROPASSERT((_State & CPSS_USERDEFINEDDELETED) == 0); + + // If this is one of the DocSumInfo property sets, + // we need to set some _State bits. If this is an + // Open, rather than a Create, pfmtid may be NULL. + // In that case, we'll set these bits after the open + // (since we can then get the fmtid from the header). + + if( pfmtid != NULL && *pfmtid == guidDocumentSummary ) + { + _State |= CPSS_DOCUMENTSUMMARYINFO; + } + + if (pfmtid != NULL && *pfmtid == guidDocumentSummarySection2) + { + _State |= CPSS_USERDEFINEDPROPERTIES; + } + else + { + // If this isn't the UD property set, the Mode + // better not be "Delete" (all other property sets + // are deleted simply be deleting the underlying + // stream). + + if (Mode == CREATEPROP_DELETE) + { + DebugTrace(0, Dbg, ("_LoadHeader: CREATEPROP_DELETE\n")); + StatusInvalidParameter(pstatus, "_LoadHeader: CREATEPROP_DELETE"); + goto Exit; + } + if (Mode == CREATEPROP_CREATE) + { + goto Exit; // We're going to overwrite it anyway + } + } + + // Get the size of the underlying stream. + cbstm = _MSTM(GetSize)(pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // Map the serialized property set to a pointer. + _MSTM(Map)(FALSE, (VOID **) &_pph); + + // Compute the minimum size of this property set, as specified + // by the property set header and the section headers. This call + // will fail if any part of these headers is beyond the end of the + // the stream (as determined from cbstm). It will *not* fail if + // a section's cbSection indicates that the section goes beyond the + // end of the stream. + + cbMin = _ComputeMinimumSize(cbstm, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // The following assert should technically ASSERT equality. However, + // to avoid unmapping and closing sections for every property operation, + // we allow shrinks to fail when other instances of the same property + // set are active. So we on occasion will legitimately see streams larger + // than necessary. The wasted space will be cleaned up when the property + // set is next modified. + //PROPASSERT(cbMin == cbstm); + + // The following assert should be valid, but it isn't for some + // older property sets which we fix in the _Fix* routines, which + // are called below. + //PROPASSERT(cbMin <= cbstm); + + DebugTrace(0, KERNELSELECT(Dbg, Dbg | DEBTRACE_CREATESTREAM), ( + "ComputeMinimumSize: cbMin=%l" szX " cbstm=%l" szX " cbUnused=%l" szX "\n", + cbMin, + cbstm, + cbstm - cbMin)); + + _oSection = 0; + _cSection = 1; + _cbTail = 0; +#ifdef KERNEL + _CodePage = CP_WINUNICODE; +#endif + + if (_HasPropHeader()) + { + // The first expression must be TRUE before we can dereference _pph + // for the second expression. + + if (cbstm < CB_PROPERTYSETHEADER + CB_FORMATIDOFFSET || + cbstm < CB_PROPERTYSETHEADER + _pph->reserved * CB_FORMATIDOFFSET || + _pph->wByteOrder != 0xfffe || + _pph->wFormat != 0 || + _pph->reserved < 1) + { + _cSection = 0; // Mark property set invalid + DebugTrace(0, cbstm != 0? DEBTRACE_ERROR : Dbg, ( + "_LoadHeader: %s (ver=%lx)\n", + cbstm == 0? "Empty Stream" : + cbstm < CB_PROPERTYSETHEADER + CB_FORMATIDOFFSET? + "Stream too small for header" : + _pph->wByteOrder != 0xfffe? "Bad wByteOrder field" : + _pph->wFormat != 0? "Bad wFormat field" : + _pph->reserved < 1? "Bad reserved field" : + "Bad dwOSVer field", + _pph != NULL? _pph->dwOSVer : 0)); + goto Exit; + } + + // Now that we've loaded the property set, check again + // to see if this is a SumInfo or DocSumInfo set. + + pfo = _GetFormatidOffset(0); + if (pfo->fmtid == guidDocumentSummary) + { + _State |= CPSS_DOCUMENTSUMMARYINFO; + } + else if (pfo->fmtid == guidSummary) + { + fSummaryInformation = TRUE; + } + + // If what we're after is the property set in the + // second section, verify that it's there. + + if (_State & CPSS_USERDEFINEDPROPERTIES) + { + // Ensure that this is the second section of + // the DocSumInfo property set; that's the only + // two-section property set we support. + + if ((_State & CPSS_DOCUMENTSUMMARYINFO) == 0) + { + DebugTrace(0, DEBTRACE_ERROR, ("Not DocumentSummaryInfo 1st FMTID\n")); + goto Exit; + } + + // Verify that this property set has two sections, and that + // the second section is the UD propset. + + if (_pph->reserved < 2 || + (pfo = _GetFormatidOffset(1))->fmtid != guidDocumentSummarySection2) + { + DebugTrace( + 0, + _pph->reserved < 2? Dbg : DEBTRACE_ERROR, + ("Bad/missing 2nd section FMTID\n")); + loadstate = LOADSTATE_USERDEFINEDNOTFOUND; + goto Exit; + } + } + else if (pfmtid != NULL) + { + // This isn't the UserDefined property set, so it + // should be the first section, so it should match + // the caller-requested format ID. + + if (*pfmtid != pfo->fmtid) + { + // The propset's FmtID doesn't match, but maybe that's + // because it's a MacWord6 SumInfo property set, in which + // the FmtID isn't byte-swapped. Otherwise, it's a problem. + + if( OSKIND_MACINTOSH == PROPSETHDR_OSVER_KIND(_pph->dwOSVer) + && + guidSummary == *pfmtid + && + IsEqualFMTIDByteSwap( *pfmtid, pfo->fmtid ) + ) + { + fSummaryInformation = TRUE; + } + else + { + _cSection = 0; + DebugTrace(0, DEBTRACE_ERROR, ("Bad FMTID\n")); + loadstate = LOADSTATE_BADFMTID; + goto Exit; + } + } // if (*pfmtid != pfo->fmtid) + } // else if (pfmtid != NULL) + + _oSection = pfo->dwOffset; + _cSection = _pph->reserved; + + } // if (_HasPropHeader()) + + psh = _GetSectionHeader(); + + // Scan the property set for a code page, and set _CodePage. + + _SearchForCodePage( pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // If we have multiple sections, record the tail length + // (the size of the property set beyond this section). + + if (_cSection > 1) + { + _State |= CPSS_MULTIPLESECTIONS; + _cbTail = cbMin - (_oSection + psh->cbSection); + DebugTrace(0, Dbg, ("_LoadHeader: cbTail=%x\n", _cbTail)); + } + + + // Fix all header-related problems in the in-memory representation. + // The only header-related problems we fix are with SummaryInformation + // property sets. + + if (fSummaryInformation) + { + _FixSummaryInformation(&cbstm, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + + + // Now that, to the best of our ability, the headers are good, + // let's validate them against the actual stream size. + + if (cbstm < _oSection + CB_PROPERTYSECTIONHEADER || + psh->cbSection < CB_PROPERTYSECTIONHEADER + + psh->cProperties * CB_PROPERTYIDOFFSET || + cbstm < _oSection + CB_PROPERTYSECTIONHEADER + + psh->cProperties * CB_PROPERTYIDOFFSET || + cbstm < _oSection + psh->cbSection) + { + _cSection = 0; + DebugTrace(0, Dbg, ("_LoadHeader: too small for section\n")); + goto Exit; + } + + // Now we know the headers are OK, so let's see if there are any + // problems in the properties themselves that we know how + // to fix. + + if (fSummaryInformation || (_State & CPSS_DOCUMENTSUMMARYINFO)) + { + _FixPackedPropertySet( pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + + if (Mode == CREATEPROP_DELETE) + { + loadstate = LOADSTATE_USERDEFINEDDELETE; + goto Exit; + } + + // ---- + // Exit + // ---- + + loadstate = LOADSTATE_DONE; + +Exit: + + return( loadstate ); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_FixSummaryInformation +// +// Synopsis: Fix up the memory image of a SummaryInformation propset, +// except for packing or padding problems (which are fixed +// in _FixPackedPropertySet). +// +// Arguments: [pcbstm] - The size of the mapped stream. This may +// be updated by this routine. +// [pstatus] - Pointer to NTSTATUS code. +// +// Returns: None +// +//--------------------------------------------------------------------------- + +#define PID_THUMBNAIL 0x00000011 // SummaryInformation thumbnail property + +VOID +CPropertySetStream::_FixSummaryInformation(IN OUT ULONG *pcbstm, + OUT NTSTATUS *pstatus) +{ + PROPERTYSECTIONHEADER *psh; + PROPERTYIDOFFSET *ppo, *ppoMax; + + *pstatus = STATUS_SUCCESS; + + // If this property set has multiple sections, then it's not one + // of the ones we know how to fix in this routine. + + if (1 != _cSection) goto Exit; + + // Load pointers to the section header and the PID/Offset array. + psh = _LoadPropertyOffsetPointers(&ppo, &ppoMax, pstatus); + if( !NT_SUCCESS(*pstatus) || NULL == psh ) goto Exit; + + // Look for the MS Publisher problem. Pub only writes + // a Thumbnail, but it sets the section size too short (by 4 bytes). + // Pub95 has the additional problem that it doesn't DWORD-align the + // section and stream size. We fix both of these problems below. + + if (*pcbstm == _oSection + psh->cbSection + sizeof(ULONG)) + { + // Look for the thumbnail property. + + for ( ; ppo < ppoMax; ppo++) + { + if (ppo->propid == PID_THUMBNAIL) + { + SERIALIZEDPROPERTYVALUE const *pprop; + + // If this property isn't properly aligned, then ignore it. + + if (ppo->dwOffset & (sizeof(DWORD) - 1)) + { + break; + } + + // Get a pointer to the property. + + pprop = (SERIALIZEDPROPERTYVALUE *) + _MapOffsetToAddress(ppo->dwOffset); + + // Look specifically for the Publisher's Thumbnail property. + // If this is a Publisher set, the lengths won't add + // up correctly. For the lengths to add up correctly, + // the offset of the property, plus + // the length of the thumbnail, plus the size of the VT + // DWORD and the size of the length DWORD should be the + // size of the Section. But In the case of Publisher, + // the section length is 4 bytes short. + + if (PropByteSwap(pprop->dwType) == VT_CF // It's in a clipboard format + && // For Windows + *(ULONG *) &pprop->rgb[sizeof(ULONG)] == PropByteSwap((ULONG)MAXULONG) + && + ppo->dwOffset + // And the lengths don't add up + PropByteSwap( *(ULONG *) pprop->rgb ) + + (3 - 2) * sizeof(ULONG) == psh->cbSection) + { + // We've found the Publisher problem. + + // For Pub95 files, we must dword-align the section + // and stream size. We don't change the size of the underlying + // stream, however, just the mapping. This is because if the caller + // doesn't make any explicit changes, we don't want the mapped Stream + // to be modified. We do this step before fixing the section-size + // problem, so if it should fail we haven't touched anything. + + if( !IsDwordAligned( *pcbstm )) + { + // Increase the size of the buffer, and reload the + // psh pointer. + + *pcbstm += DwordRemain(*pcbstm); + _MSTM(SetSize)(*pcbstm, // The new size + FALSE, // Don't update the underlying stream + (VOID **) &_pph, // The new mapping + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + psh = _LoadPropertyOffsetPointers(&ppo, &ppoMax, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // Align the section size. + + psh->cbSection += DwordRemain(psh->cbSection); + } + + // Now correct the section size. + + DebugTrace(0, DEBTRACE_PROPPATCH, ( + "_FixSummaryInformation: Patch section size: %x->%x\n", + psh->cbSection, + psh->cbSection + sizeof(ULONG))); + + psh->cbSection += sizeof(ULONG); + + } // if (pprop->dwType == VT_CF ... + + break; + + } // if (ppo->propid == PID_THUMBNAIL) + } // for ( ; ppo < ppoMax; ppo++) + } // if (cbstm == _oSection + psh->cbSection + sizeof(ULONG)) + + // Look for the Excel 5.0a problem. + // Excel 5.0a set the cbSection field to be 4 bytes too + // high. This code handles the more general case where the + // cbSection is too long for the stream. In such cases, if + // all the properties actually fit within the stream, the + // cbSection field is corrected. + + if (*pcbstm < _oSection + psh->cbSection) + { + // We'll fix this problem by adjusting the cbSection + // value. We have to be careful, though, + // that the entire section fits within this new cbSection + // value. For efficiency, we'll just find the property + // which is at the highest offset, and verify that it's + // within the new section size. + + // Get what we think is the actual section length. + ULONG cbSectionActual = *pcbstm - _oSection; + + ULONG dwHighestOffset = 0; + ULONG cbProperty; + + // Find the property with the highest offset. + + for ( ; ppo < ppoMax; ppo++) + { + if( ppo->dwOffset > dwHighestOffset ) + dwHighestOffset = ppo->dwOffset; + } + + // How long is this property? + + cbProperty = PropertyLengthNoEH( + // Pointer to property + (SERIALIZEDPROPERTYVALUE *) + _MapOffsetToAddress(dwHighestOffset), + // Bytes between above ptr & end of stream + *pcbstm - _oSection - dwHighestOffset, + 0, // Flags + pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // Does this property fit within the section? If so, then fix this + // property set. + + if( dwHighestOffset + DwordAlign(cbProperty) <= cbSectionActual ) + { + psh->cbSection = dwHighestOffset + DwordAlign(cbProperty); + } + else + { + StatusCorruption(pstatus, "SumInfo cbSection is too long for the Stream."); + } + + } // if (*pcbstm < _oSection + psh->cbSection) + + + // ---- + // Exit + // ---- + +Exit: + + return; + +} // CPropertySetStream::_FixSummaryInformation() + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_FixPackedPropertySet +// +// Synopsis: Align the memory image of a propset. +// +// Algorithm: We need to move the properties within the +// property set so that they are properly aligned, +// and we need to adjust the PID/Offset array accordingly. +// This is complicated by the fact that we may have to +// grow some propertes (which are not properly padded +// for alignement) and at the same time we may have to +// shrink some properties (which are over-padded). +// +// To handle these two constraints, and to +// avoid growing the underlying stream any more +// than necessary, we process the property set in +// two phases. In the Compaction phase, we shrink +// properties which are over-padded. In the Expansion +// phase, we grow properties which are under-padded. +// For example, say we have a property set with 3 +// properties, all of which should be 4 bytes. But +// say they are currently 2, 4, and 6 bytes. Thus +// we must grow the first property, hold the second +// constant, and shrink the third property. In this +// example, after the Compaction phase, the 3 properties +// will be 2, 4, and 4 bytes. After the Expansion phase, +// the properties will be 4, 4, and 4 bytes. +// +// To do all of this, we make a copy of the PID/Offset +// array (apoT) and sort it. We then proceed to make +// two arrays of just offsets (no PIDs) - aopropShrink +// and aopropFinal. aopropShrink holds the offset for +// each property after the Compaction phase. aopropFinal +// holds the offset for each property after the +// Expansion phase. (Note that each of these phases +// could be skipped if they aren't necessary.) +// +// Finally, we perform the Compaction and Expansion, +// using aopropShrink and aopropFinal, respectively, +// as our guide. +// +// Arguments: [pstatus] -- Pointer to NTSTATUS code. +// +// Returns: None +//--------------------------------------------------------------------------- + +INT _CRTAPI1 fnOffsetCompare(VOID const *ppo1, VOID const *ppo2); + +// DocumentSummaryInformation special case properties (w/packed vector elements) +#define PID_HEADINGPAIR 0x0000000c // heading pair (VT_VECTOR | VT_VARIANT): + // {VT_LPSTR, VT_I4} pairs +#define PID_DOCPARTS 0x0000000d // docparts (VT_VECTOR | VT_LPSTR) +//#define PID_HLINKS 0x00000015 // hlinks vector + +VOID +CPropertySetStream::_FixPackedPropertySet(OUT NTSTATUS *pstatus) +{ + // ------ + // Locals + // ------ + + BOOLEAN fPacked = FALSE; + BOOLEAN fDocSummaryInfo = FALSE; +#if DBGPROP + BOOLEAN fExpandDocSummaryInfo = FALSE; +#endif + PROPERTYSECTIONHEADER *psh = NULL; + PROPERTYIDOFFSET *ppoT, *ppoTMax; + PROPERTYIDOFFSET *ppo, *ppoBase, *ppoMax; + + PROPERTYIDOFFSET *apoT = NULL; + + ULONG *aopropShrink = NULL; + ULONG *aopropFinal = NULL; + ULONG cbprop; + ULONG cCompact, cExpand; + ULONG *poprop = NULL; + +#if i386 == 0 + SERIALIZEDPROPERTYVALUE *ppropbuf = NULL; + ULONG cbpropbuf = 0; +#endif + + ULONG cbtotal = 0; + + // ----- + // Begin + // ----- + + *pstatus = STATUS_SUCCESS; + + // Determine if this is the first section of the DocSumInfo + // property set. + if ((_State & (CPSS_USERDEFINEDPROPERTIES | CPSS_DOCUMENTSUMMARYINFO)) == + CPSS_DOCUMENTSUMMARYINFO) + { + fDocSummaryInfo = TRUE; + } + + // Get pointers into this section's header. + psh = _LoadPropertyOffsetPointers(&ppoBase, &ppoMax, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // We know it's packed if the section-length isn't aligned. + fPacked = !IsDwordAligned(psh->cbSection); + + // If we don't already know it's packed, check each of the properties in + // the PID/Offset array to see if one is not properly aligned, if so we'll + // assume that it's packed. Also, if this is an Ansi DocSumInfo property set, + // (first section), we'll assume that the HeadingPair and DocParts properties + // are packed (vectors). + + if (!fPacked && psh != NULL) + { + for (ppo = ppoBase; ppo < ppoMax; ppo++) + { + if ( !IsDwordAligned(ppo->dwOffset) + || + ( fDocSummaryInfo + && + _CodePage != CP_WINUNICODE + && + ( ppo->propid == PID_HEADINGPAIR + || + ppo->propid == PID_DOCPARTS + ) + ) + ) + { + fPacked = TRUE; + break; + } + } + } + + // ---------------------------------------------------- + // Fix the properties if they are packed or if there is + // unnecessary padding. + // ---------------------------------------------------- + + // If we know there's a problem, set a _State flag + // now. If we can fix the problem below, we'll clear it. + // Otherwise, the rest of the Class will know that there's + // an unresolved problem. + + if (fPacked) + { + DebugTrace(0, DEBTRACE_PROPPATCH, ( + "_FixPackedPropertySet: packed properties\n")); + _State |= CPSS_PACKEDPROPERTIES; + } + + + // --------------------------------------------------------- + // Create apoT (a sorted array of PID/Offsets), aopropShrink + // (the offsets for the Compaction phase) and aopropFinal + // (the offsets for the Expansion phase). + // --------------------------------------------------------- + + // Create a buffer for a temporary PID/Offset array. + + apoT = newk(mtPropSetStream, NULL) PROPERTYIDOFFSET[psh->cProperties + 1]; + if (apoT == NULL) + { + *pstatus = STATUS_NO_MEMORY; + goto Exit; + } + + // Copy the PID/offset pairs from the property set to the + // temporary PID/Offset array. + + RtlCopyMemory( + apoT, + psh->rgprop, + psh->cProperties * CB_PROPERTYIDOFFSET); + + // Mark the end of the temporary array. + + ppoTMax = apoT + psh->cProperties; + ppoTMax->propid = PID_ILLEGAL; + ppoTMax->dwOffset = psh->cbSection; + + // Sort the PID/Offset array by offset and check for overlapping values: + + qsort(apoT, psh->cProperties, sizeof(apoT[0]), fnOffsetCompare); + + // Create two arrays which will hold property offsets. + // aopropShrink holds the offsets for the Compaction phase where + // we shrink the property set. aopropFinal holds the offsets + // of the final property set, which will be achieved in the + // Expansion phase. + + aopropShrink = newk(mtPropSetStream, NULL) ULONG[psh->cProperties + 1]; + if (aopropShrink == NULL) + { + *pstatus = STATUS_NO_MEMORY; + goto Exit; + } + + aopropFinal = newk(mtPropSetStream, NULL) ULONG[psh->cProperties + 1]; + if (aopropFinal == NULL) + { + *pstatus = STATUS_NO_MEMORY; + goto Exit; + } + +#if i386 == 0 + // On non-x86 machines, we can't directly access unaligned + // properties. So, allocate enough (aligned) memory to hold + // the largest unaligned property. We'll copy properties here + // when we need to access them. + + for (ppoT = apoT; ppoT < ppoTMax; ppoT++) + { + if (!IsDwordAligned(ppoT->dwOffset)) + { + cbprop = DwordAlign(ppoT[1].dwOffset - ppoT->dwOffset); + if (cbpropbuf < cbprop) + { + cbpropbuf = cbprop; + } + } + } + + if (cbpropbuf != 0) + { + ppropbuf = (SERIALIZEDPROPERTYVALUE *) + newk(mtPropSetStream, NULL) BYTE[cbpropbuf]; + if (ppropbuf == NULL) + { + *pstatus = STATUS_NO_MEMORY; + goto Exit; + } + } +#endif // i386==0 + + + // ---------------------------------------------- + // Iterate through the properties, filling in the + // entries of aopropShrink and aopropFinal. + // ---------------------------------------------- + + // We'll also count the number of compacts and expands + // necessary. + + aopropShrink[0] = aopropFinal[0] = apoT[0].dwOffset; + PROPASSERT(IsDwordAligned(aopropShrink[0])); + cExpand = 0; + cCompact = 0; + + for (ppoT = apoT; ppoT < ppoTMax; ppoT++) + { + SERIALIZEDPROPERTYVALUE *pprop; + BOOLEAN fDocSumLengthComputed = FALSE; + ULONG cbpropOriginal; + + // How much space does the property take up in the current + // property set? + + cbpropOriginal = cbprop = ppoT[1].dwOffset - ppoT->dwOffset; + pprop = (SERIALIZEDPROPERTYVALUE *) + _MapOffsetToAddress(ppoT->dwOffset); + +#if i386 == 0 + // If necessary, put this property into an aligned buffer. + + if (!IsDwordAligned(ppoT->dwOffset)) + { + DebugTrace(0, Dbg, ( + "_FixPackedPropertySet: unaligned pid=%x off=%x\n", + ppoT->propid, + ppoT->dwOffset)); + PROPASSERT(DwordAlign(cbprop) <= cbpropbuf); + RtlCopyMemory((VOID *) ppropbuf, pprop, cbprop); + pprop = ppropbuf; + } +#endif + // Calculate the actual length of this property, including + // the necessary padding. This might be bigger than the + // property's current length (if the propset wasn't properly + // padded), and it might be smaller than the property's current + // length (if the propset was over-padded). + + if (ppoT->propid == PID_DICTIONARY) + { + // Get the size of the dictionary. + + cbprop = DwordAlign(_DictionaryLength( + (DICTIONARY const *) pprop, + cbprop, + pstatus)); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + else + { + ULONG cbpropT; + + // Ansi DocSumInfo property sets have two vector properties + // which are packed. If this is one of those properties, + // we won't fix it yet, but we'll compute the size required + // when the elements are un-packed. + + if (fDocSummaryInfo && _CodePage != CP_WINUNICODE) + { + if (ppoT->propid == PID_HEADINGPAIR) + { + fDocSumLengthComputed = _FixHeadingPairVector( + PATCHOP_COMPUTESIZE, + pprop, + &cbpropT); + } + else + if (ppoT->propid == PID_DOCPARTS) + { + fDocSumLengthComputed = _FixDocPartsVector( + PATCHOP_COMPUTESIZE, + pprop, + &cbpropT); + } + } + + // If we computed a length above, use it, otherwise calculate + // the length using the standard rules (we've already checked + // for the special cases). + + if (fDocSumLengthComputed) + { + cbprop = cbpropT; +#if DBGPROP + fExpandDocSummaryInfo = TRUE; +#endif + } + else + { + cbprop = PropertyLengthNoEH(pprop, DwordAlign(cbprop), 0, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + + } // if (ppoT->propid == PID_DICTIONARY) ... else + + PROPASSERT(IsDwordAligned(cbprop)); + + // Now that we know the actual cbprop, use it to update the + // *next* entry in the two arrays of correct offsets. + // + // We want aopropFinal to hold the final, correct offsets, + // so we'll use cbprop to calculate this array. + // But for aopropShrink, we only want it to differ from + // the original array (apoT) when a property is shrinking, + // so we'll use min(cbNew,cbOld) for this array. + + poprop = &aopropShrink[ ppoT - apoT ]; // 1st do aopropShrink + poprop[1] = poprop[0] + min(cbprop, cbpropOriginal); + + poprop = &aopropFinal[ ppoT - apoT ]; // 2nd do aopropFinal + poprop[1] = poprop[0] + cbprop; + + DebugTrace(0, Dbg, ( + "_FixPackedPropertySet: pid=%x off=%x->%x\n", + ppoT->propid, + ppoT->dwOffset, + poprop[0], + poprop[0] < ppoT->dwOffset? + " (compact)" : + poprop[0] > ppoT->dwOffset? " (expand)" : "")); + + + // Is this compaction or an expansion? + // If we computed the doc-sum length, we count it as + // an expansion, even if the total property size didn't change, + // because we need the expand the elements within the vector. + + if (cbprop < cbpropOriginal) + { + cCompact++; + } + else + if (cbprop > cbpropOriginal || fDocSumLengthComputed) + { + cExpand++; + } + } // for (ppoT = apoT; ppoT < ppoTMax; ppoT++) + + + // ------------------------------- + // Compact/Expand the Property Set + // ------------------------------- + + // We've now generated the complete aopropShrink and aopropFinal + // offset arrays. Now, if necessary, let's expand and/or compact + // the property set to match these offsets. + + if (cExpand || cCompact) + { + ULONG cbstm; + LONG cbdelta; + + cbstm = _oSection + psh->cbSection + _cbTail; + cbdelta = aopropFinal[psh->cProperties] - psh->cbSection; + + DebugTrace(0, Dbg, ( + "_FixPackedPropertySet: cbstm=%x cbdelta=%x cexpand=%x ccompact=%x\n", + cbstm, + cbdelta, + cExpand, + cCompact)); + + // ----------------------------- + // Grow the Stream if necessary. + // ----------------------------- + + if (cbdelta > 0) + { + DebugTrace(0, Dbg, ( + "SetSize(%x) _FixPackedPropertySet grow %x bytes\n", + cbstm + cbdelta, + cbdelta)); + + // On the set-size, say that this is a non-persistent + // change, so that the underlying Stream isn't modified. + // At this point, we don't know if this change should remain + // permanent (if the caller closes without making any changes + // the file should remain un-changed). + + _MSTM(SetSize)( + cbstm + cbdelta, + FALSE, // Not persistent + (VOID **) &_pph, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // reload all pointers into mapped image: + + psh = _LoadPropertyOffsetPointers(&ppoBase, &ppoMax, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // If there's another section after this one, move it back + // to the end of the stream now, which will create room for + // our expansion. + + if (_cbTail != 0) + { + VOID *pvSrc = _MapAbsOffsetToAddress(cbstm - _cbTail); + + PropMoveMemory( + "_FixPackedPropertySet(_cbTail:grow)", + psh, + Add2Ptr(pvSrc, cbdelta), + pvSrc, + _cbTail); + } + } // if (cbdelta > 0) + + // This previous step (growing the Stream), was the last one which can + // fail. We're about to modify the actual property set (we've been + // working only with temporary buffers so far). So we're always guaranteed + // a good property set, or the original set, we'll never end up with a + // half-updated set. + + + // ---------------- + // Compaction Phase + // ---------------- + + // Compact the property set if necessary. I.e., adjust + // the property set buffer so that it matches aopropShrink. + + if (cCompact > 0) + { + // Start at the beginning and move each property up. + + poprop = aopropShrink; + for (ppoT = apoT; ppoT < ppoTMax; ppoT++, poprop++) + { + if (*poprop != ppoT->dwOffset) + { + PROPASSERT(*poprop < ppoT->dwOffset); + PROPASSERT(poprop[1] > *poprop); + + // We're compacting; the property should not grow! + + PROPASSERT( + poprop[1] - *poprop <= + ppoT[1].dwOffset - ppoT->dwOffset); + + PropMoveMemory( + "_FixPackedPropertySet(compact)", + psh, + Add2Ptr(psh, *poprop), + Add2Ptr(psh, ppoT->dwOffset), + poprop[1] - *poprop); + } + } // for (ppoT = apoT; ppoT < ppoTMax; ppoT++, poprop++) + } // if (cCompact > 0) + + + // --------------- + // Expansion phase + // --------------- + + // Recall that, whether or not we just did a compaction, aopropShrink + // holds the property set offsets as they currently exist in the + // property set. + + if (cExpand > 0) + { + // Start at the end and move each property back. + // The 'poprop' gives us the final correct offset + // of the current property. + + LONG lOffsetIndex; + poprop = &aopropFinal[psh->cProperties - 1]; + + // Start at the second-to-last entry in the arrays of offsets + // (the last entry is an artificially added one to mark the end of the + // property set). + + for (lOffsetIndex = ppoTMax - apoT - 1, ppoT = ppoTMax - 1; + lOffsetIndex >=0; + lOffsetIndex--, poprop--, ppoT--) + { + // Get a pointer to the final location of this + // property. + + SERIALIZEDPROPERTYVALUE *pprop; + pprop = (SERIALIZEDPROPERTYVALUE *) + Add2Ptr(psh, *poprop); + + if (*poprop != aopropShrink[ lOffsetIndex ]) + { + ULONG cbCopy, cbOld; + + PROPASSERT(*poprop > aopropShrink[ lOffsetIndex ]); + PROPASSERT(poprop[1] > *poprop); + PROPASSERT(aopropShrink[ lOffsetIndex+1 ] > aopropShrink[ lOffsetIndex ]); + + // How many bytes should we copy? The minimum size of the property + // calculated using the old and new offsets. + + cbCopy = poprop[1] - poprop[0]; + cbOld = aopropShrink[ lOffsetIndex+1 ] + - aopropShrink[ lOffsetIndex+0 ]; + + if (cbCopy > cbOld) + { + cbCopy = cbOld; + } + + // Copy the property from its old location + // (psh+aopropShrink[lOffsetIndex]) to its new location + // (pprop == psh+*poprop). + + DebugTrace(0, DEBTRACE_PROPPATCH, ( + "_FixPackedPropertySet:move pid=%x off=%x->%x " + "cb=%x->%x cbCopy=%x z=%x @%x\n", + ppoT->propid, + ppoT->dwOffset, + *poprop, + cbOld, + poprop[1] - *poprop, + cbCopy, + DwordRemain(cbCopy), + _MapAddressToOffset(Add2Ptr(pprop, cbCopy)))); + + PropMoveMemory( + "_FixPackedPropertySet(expand)", + psh, + pprop, + Add2Ptr(psh, aopropShrink[ lOffsetIndex ]), + cbCopy); + RtlZeroMemory( + Add2Ptr(pprop, cbCopy), + DwordRemain(cbCopy)); + + } // if (*poprop != ppoT->dwOffset) + + // If this is an older DocSumInfo property set, + // and this property is one of the vector values, + // we must expand the vector elements now that we've + // room for it. + + if (fDocSummaryInfo && _CodePage != CP_WINUNICODE) + { + ULONG cbpropT; + + if (ppoT->propid == PID_HEADINGPAIR) + { + _FixHeadingPairVector( + PATCHOP_EXPAND, + pprop, + &cbpropT); + } + else + if (ppoT->propid == PID_DOCPARTS) + { + _FixDocPartsVector( + PATCHOP_EXPAND, + pprop, + &cbpropT); + } + } // if (fDocSummaryInfo) + } // for (ppoT = ppoTMax; --ppoT >= apoT; popropNew--) + } // if (cExpand != 0) + + + + // --------------------------------------------------------- + // Patch the section size and the moved properties' offsets. + // --------------------------------------------------------- + + DebugTrace(0, DEBTRACE_PROPPATCH, ( + "_FixPackedPropertySet: Patch section size %x->%x\n", + psh->cbSection, + psh->cbSection + cbdelta)); + + psh->cbSection += cbdelta; + + // Iterate through the original PID/Offset array to update the + // offsets. + + for (ppo = ppoBase; ppo < ppoMax; ppo++) + { + // Search the temporary PID/Offset array (which has the updated + // offsets) for ppo->propid. + + for (ppoT = apoT; ppoT < ppoTMax; ppoT++) + { + if (ppo->propid == ppoT->propid) + { + // We've found ppo->propid in the temporary PID/Offset + // array. Copy the offset value from the temporary array + // to the actual array in the property set. + + PROPASSERT(ppo->dwOffset == ppoT->dwOffset); + ppo->dwOffset = aopropFinal[ppoT - apoT]; +#if DBGPROP + if (ppo->dwOffset != ppoT->dwOffset) + { + DebugTrace(0, DEBTRACE_PROPPATCH, ( + "_FixPackedPropertySet: Patch propid %x" + " offset=%x->%x\n", + ppo->propid, + ppoT->dwOffset, + ppo->dwOffset)); + } // if (ppo->dwOffset != ppoT->dwOffset) +#endif + break; + + } // if (ppo->propid == ppoT->propid) + } // for (ppoT = apoT; ppoT < ppoTMax; ppoT++) + } // for (ppo = ppoBase; ppo < ppoMax; ppo++) + + // ------------ + // Fix the tail + // ------------ + + + // If we have a tail, fix it's offset in the FmtID/Offset + // array. Also, if we've overall shrunk this section, bring + // the tail in accordingly. + + if (_cbTail != 0) + { + if (cbdelta < 0) + { + VOID *pvSrc = _MapAbsOffsetToAddress(cbstm - _cbTail); + + PropMoveMemory( + "_FixPackedPropertySet(_cbTail:shrink)", + psh, + Add2Ptr(pvSrc, cbdelta), + pvSrc, + _cbTail); + } + + _PatchSectionOffsets(cbdelta); + + } // if (_cbTail != 0) + + + // If we get to this point we've successfully un-packed (or + // un-over-padded) the property set, so we can clear the + // state flag. + + _State &= ~CPSS_PACKEDPROPERTIES; + + } // if (cExpand || cCompact) + + + // ---- + // Exit + // ---- + +Exit: + + delete [] apoT; + delete [] aopropShrink; + delete [] aopropFinal; + +#if i386 == 0 + delete [] (BYTE *) ppropbuf; +#endif // i386 + +} // CPropertySetStream::_FixPackedPropertySet() + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_FixDocPartsVector +// +// Synopsis: Align the memory image of a DocParts vector +// The DocParts property is part of the DocSumInfo +// property set (first section). It is a vector +// of strings, and in Ansi property sets it's packed +// and must be un-packed. +// +// Arguments: [PatchOp] -- patch request +// [pprop] -- property value to be patched or sized +// [pcbprop] -- pointer to computed property length +// +// Returns: TRUE if property type and all elements meet expectations; +// FALSE on error +// +// Note: Operate on a DocumentSummaryInformation first section property, +// PID_DOCPARTS. This property is assumed to be an array of +// VT_LPSTRs. +// +// PATCHOP_COMPUTESIZE merely computes the size required to unpack +// the property, and must assume it is currently unaligned. +// +// PATCHOP_ALIGNLENGTHS patches all VT_LPSTR lengths to DWORD +// multiples, and may rely on the property already being aligned. +// +// PATCHOP_EXPAND expands the property from the Src to Dst buffer, +// moving elements to DWORD boundaries, and patching VT_LPSTR +// lengths to DWORD multiples. The Src buffer is assumed to be +// unaligned, and the Dst buffer is assumed to be properly sized. +//--------------------------------------------------------------------------- + +BOOLEAN +CPropertySetStream::_FixDocPartsVector( + IN PATCHOP PatchOp, + IN OUT SERIALIZEDPROPERTYVALUE *pprop, + OUT ULONG *pcbprop) +{ + PROPASSERT( + PatchOp == PATCHOP_COMPUTESIZE || + PatchOp == PATCHOP_ALIGNLENGTHS || + PatchOp == PATCHOP_EXPAND); + PROPASSERT(pprop != NULL); + PROPASSERT(pcbprop != NULL); + + // If the property is a variant vector, + // it's in an ANSI property set, and + // there are an even number of elements, ... + + if ( PropByteSwap(pprop->dwType) == (VT_VECTOR | VT_LPSTR) + && + _CodePage != CP_WINUNICODE) + { + ULONG cString; + VOID *pv; + + cString = PropByteSwap( *(DWORD *) pprop->rgb ); + pv = Add2Ptr(pprop->rgb, sizeof(DWORD)); + + if (_FixDocPartsElements(PatchOp, cString, pv, pv, pcbprop)) + { + *pcbprop += CB_SERIALIZEDPROPERTYVALUE + sizeof(ULONG); + return(TRUE); + } + } + return(FALSE); // Not a recognizable DocParts vector +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_FixDocPartsElements +// +// Synopsis: Recursively align the memory image of DocParts elements +// +// Arguments: [PatchOp] -- patch request +// [cString] -- count of strings remaining in the vector +// [pvDst] -- aligned overlapping destination buffer +// [pvSrc] -- unaligned overlapping source buffer +// [pcbprop] -- pointer to computed property length +// +// Returns: TRUE if all remaining elements meet expectations; +// FALSE on error +// +// Note: The pvDst & pvSrc buffers must be in property-set +// byte order (little endian). +//--------------------------------------------------------------------------- + +BOOLEAN +CPropertySetStream::_FixDocPartsElements( + IN PATCHOP PatchOp, + IN ULONG cString, + OUT VOID *pvDst, + IN VOID UNALIGNED const *pvSrc, + OUT ULONG *pcbprop) +{ + ULONG cb; + + PROPASSERT( + PatchOp == PATCHOP_COMPUTESIZE || + PatchOp == PATCHOP_ALIGNLENGTHS || + PatchOp == PATCHOP_EXPAND); + PROPASSERT(pvDst >= pvSrc); + PROPASSERT(PatchOp != PATCHOP_ALIGNLENGTHS || pvDst == pvSrc); + + if (cString == 0) + { + *pcbprop = 0; + return(TRUE); + } + cb = sizeof(DWORD) + PropByteSwap( *(DWORD UNALIGNED *) pvSrc ); + + // If the caller serialized the vector properly, all we need to do is + // to round up the string lengths to DWORD multiples, so readers that + // treat these vectors as byte-aligned get faked out. We expect + // readers will not have problems with a DWORD aligned length, and a + // '\0' character a few bytes earlier than the length indicates. + + if (PatchOp == PATCHOP_ALIGNLENGTHS) + { + cb = DwordAlign(cb); // Caller says it's already aligned + } + if (_FixDocPartsElements( + PatchOp, + cString - 1, + Add2Ptr(pvDst, DwordAlign(cb)), + (VOID UNALIGNED const *) Add2ConstPtr(pvSrc, cb), + pcbprop)) + { + *pcbprop += DwordAlign(cb); + + if (PatchOp == PATCHOP_EXPAND) + { + PropMoveMemory( + "_FixDocPartsElements", + _GetSectionHeader(), + pvDst, + pvSrc, + cb); + RtlZeroMemory(Add2Ptr(pvDst, cb), DwordRemain(cb)); + + DebugTrace(0, DEBTRACE_PROPPATCH, ( + "_FixDocPartsElements: Move(%x:%s) " + "cb=%x->%x off=%x->%x z=%x @%x\n", + cString, + Add2Ptr(pvDst, sizeof(ULONG)), + cb - sizeof(ULONG), + DwordAlign(cb) - sizeof(ULONG), + _MapAddressToOffset(pvSrc), + _MapAddressToOffset(pvDst), + DwordRemain(cb), + _MapAddressToOffset(Add2Ptr(pvDst, cb)))); + } + if (PatchOp != PATCHOP_COMPUTESIZE) + { + PROPASSERT( + PatchOp == PATCHOP_ALIGNLENGTHS || + PatchOp == PATCHOP_EXPAND); + + DebugTrace(0, DEBTRACE_PROPPATCH, ( + "_FixDocPartsElements: Patch(%x:%s) cb=%x->%x\n", + cString, + Add2Ptr(pvDst, sizeof(ULONG)), + *(ULONG *) pvDst, + DwordAlign(*(ULONG *) pvDst))); + + *(ULONG *) pvDst = PropByteSwap( DwordAlign( PropByteSwap( *(ULONG *) pvDst ))); + } + return(TRUE); + } + return(FALSE); // Not a recognizable DocParts vector +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_FixHeadingPairVector +// +// Synopsis: Align the memory image of a HeadingPair vector. +// The HeadingPair property is part of the DocSumInfo +// property set (first section). It's a vector of +// Variants, where the elements are alternating +// strings and I4s (the string is a heading name, +// and the I4 is the count of DocumentParts in that +// heading). In Ansi property sets, these elements +// are packed, and must be un-packed. +// +// Arguments: [PatchOp] -- patch request +// [pprop] -- property value to be patched or sized +// [pcbprop] -- pointer to computed property length +// +// Returns: TRUE if property and all elements meet expectations; +// FALSE on error +// +// Note: Operate on a DocumentSummaryInformation first section property, +// PID_HEADINGPAIR. This property is assumed to be an array of +// VT_VARIANTs with an even number of elements. Each pair must +// consist of a VT_LPSTR followed by a VT_I4. +// +// PATCHOP_COMPUTESIZE merely computes the size required to unpack +// the property, and must assume it is currently unaligned. +// +// PATCHOP_ALIGNLENGTHS patches all VT_LPSTR lengths to DWORD +// multiples, and may rely on the property already being aligned. +// +// PATCHOP_EXPAND expands the property from the Src to Dst buffer, +// moving elements to DWORD boundaries, and patching VT_LPSTR +// lengths to DWORD multiples. The Src buffer is assumed to be +// unaligned, and the Dst buffer is assumed to be properly sized. +//--------------------------------------------------------------------------- + +BOOLEAN +CPropertySetStream::_FixHeadingPairVector( + IN PATCHOP PatchOp, + IN OUT SERIALIZEDPROPERTYVALUE *pprop, + OUT ULONG *pcbprop) +{ + ULONG celem; + ULONG cbprop = 0; + + PROPASSERT( + PatchOp == PATCHOP_COMPUTESIZE || + PatchOp == PATCHOP_ALIGNLENGTHS || + PatchOp == PATCHOP_EXPAND); + PROPASSERT(pprop != NULL); + PROPASSERT(pcbprop != NULL); + + // If the property is a variant vector, and + // there are an even number of elements, ... + + if( PropByteSwap(pprop->dwType) == (VT_VECTOR | VT_VARIANT) + && + ( (celem = PropByteSwap(*(ULONG *) pprop->rgb) ) & 1) == 0 + && + _CodePage != CP_WINUNICODE) + { + pprop = (SERIALIZEDPROPERTYVALUE *) Add2Ptr(pprop->rgb, sizeof(ULONG)); + + if (_FixHeadingPairElements(PatchOp, celem/2, pprop, pprop, pcbprop)) + { + *pcbprop += CB_SERIALIZEDPROPERTYVALUE + sizeof(ULONG); + return(TRUE); + } + } + return(FALSE); // Not a recognizable HeadingPair vector +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_FixHeadingPairElements +// +// Synopsis: Recursively align the memory image of HeadingPair elements +// +// Arguments: [PatchOp] -- patch request +// [cPairs] -- count of heading pairs remaining +// [ppropDst] -- aligned overlapping destination buffer +// [ppropSrc] -- unaligned overlapping source buffer +// [pcbprop] -- pointer to computed property length +// +// Returns: TRUE if all remaining elements meet expectations; +// FALSE on error +//--------------------------------------------------------------------------- + +BOOLEAN +CPropertySetStream::_FixHeadingPairElements( + IN PATCHOP PatchOp, + IN ULONG cPairs, + OUT SERIALIZEDPROPERTYVALUE *ppropDst, + IN SERIALIZEDPROPERTYVALUE UNALIGNED const *ppropSrc, + OUT ULONG *pcbprop) +{ + PROPASSERT( + PatchOp == PATCHOP_COMPUTESIZE || + PatchOp == PATCHOP_ALIGNLENGTHS || + PatchOp == PATCHOP_EXPAND); + PROPASSERT(ppropDst >= ppropSrc); + PROPASSERT(PatchOp != PATCHOP_ALIGNLENGTHS || ppropDst == ppropSrc); + + if (cPairs == 0) + { + *pcbprop = 0; + return(TRUE); + } + + // If the first element of the pair is a VT_LPSTR, ... + + if( PropByteSwap(ppropSrc->dwType) == VT_LPSTR ) + { + ULONG cb; + + // Compute size of the string element. + + cb = CB_SERIALIZEDPROPERTYVALUE + + + sizeof(ULONG) + + + PropByteSwap( *(DWORD UNALIGNED *) ppropSrc->rgb ); + + // If the caller serialized the vector properly, all we need to do is + // to round up the string lengths to DWORD multiples, so readers that + // treat these vectors as byte-aligned get faked out. We expect + // readers will not have problems with a DWORD aligned length, and a + // '\0' character a few bytes earlier than the length indicates. + + if (PatchOp == PATCHOP_ALIGNLENGTHS) + { + cb = DwordAlign(cb); // Caller says it's already aligned + } + + // and if the second element of the pair is a VT_I4, ... + + if ( PropByteSwap( (DWORD) VT_I4 ) + == + ( (SERIALIZEDPROPERTYVALUE UNALIGNED const *) + Add2ConstPtr(ppropSrc, cb) + )->dwType ) + { + cb += CB_SERIALIZEDPROPERTYVALUE + sizeof(DWORD); + + if (_FixHeadingPairElements( + PatchOp, + cPairs - 1, + (SERIALIZEDPROPERTYVALUE *) + Add2Ptr(ppropDst, DwordAlign(cb)), + (SERIALIZEDPROPERTYVALUE UNALIGNED const *) + Add2ConstPtr(ppropSrc, cb), + pcbprop)) + { + *pcbprop += DwordAlign(cb); + + if (PatchOp == PATCHOP_EXPAND) + { + // Move the unaligned VT_I4 property back in memory to an + // aligned boundary, move the string back to a (possibly + // different) aligned boundary, zero the space in between + // the two and patch the string length to be a DWORD + // multiple to fake out code that expects vector elements + // to be byte aligned. + + // Adjust byte count to include just the string element. + + cb -= CB_SERIALIZEDPROPERTYVALUE + sizeof(ULONG); + + // Move the VT_I4 element. + + PropMoveMemory( + "_FixHeadingPairElements:I4", + _GetSectionHeader(), + Add2Ptr(ppropDst, DwordAlign(cb)), + Add2ConstPtr(ppropSrc, cb), + CB_SERIALIZEDPROPERTYVALUE + sizeof(ULONG)); + + // Move the VT_LPSTR element. + + PropMoveMemory( + "_FixHeadingPairElements:LPSTR", + _GetSectionHeader(), + ppropDst, + ppropSrc, + cb); + + // Zero the space in between. + + RtlZeroMemory(Add2Ptr(ppropDst, cb), DwordRemain(cb)); + + DebugTrace(0, DEBTRACE_PROPPATCH, ( + "_FixHeadingPairElements: Move(%x:%s) " + "cb=%x->%x off=%x->%x z=%x @%x\n", + cPairs, + &ppropDst->rgb[sizeof(ULONG)], + PropByteSwap( *(ULONG *) ppropDst->rgb ), + DwordAlign(PropByteSwap( *(ULONG *) ppropDst->rgb )), + _MapAddressToOffset(ppropSrc), + _MapAddressToOffset(ppropDst), + DwordRemain(cb), + _MapAddressToOffset(Add2Ptr(ppropDst, cb)))); + } + + if (PatchOp != PATCHOP_COMPUTESIZE) + { + PROPASSERT( + PatchOp == PATCHOP_ALIGNLENGTHS || + PatchOp == PATCHOP_EXPAND); +#ifdef DBGPROP + SERIALIZEDPROPERTYVALUE const *ppropT = + (SERIALIZEDPROPERTYVALUE const *) + Add2Ptr(ppropDst, DwordAlign(cb)); +#endif + DebugTrace(0, DEBTRACE_PROPPATCH, ( + "_FixHeadingPairElements: Patch(%x:%s) " + "cb=%x->%x, vt=%x, %x\n", + cPairs, + &ppropDst->rgb[sizeof(ULONG)], + PropByteSwap( *(ULONG *) ppropDst->rgb ), + DwordAlign( PropByteSwap( *(ULONG *) ppropDst->rgb )), + PropByteSwap( ppropT->dwType ), + PropByteSwap( *(ULONG *) ppropT->rgb ))); + + // Patch the string length to be a DWORD multiple. + + *(ULONG *) ppropDst->rgb + = PropByteSwap( DwordAlign( PropByteSwap( *(ULONG *) ppropDst->rgb ))); + } + return(TRUE); + } + } + } + return(FALSE); // Not a recognizable HeadingPair vector +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::QueryPropertySet +// +// Synopsis: Return the classid for the property set code +// +// Arguments: [pspss] -- pointer to buffer for output +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: None +//--------------------------------------------------------------------------- + +#ifndef KERNEL +VOID +CPropertySetStream::QueryPropertySet(OUT STATPROPSETSTG *pspss, + OUT NTSTATUS *pstatus) const +{ + *pstatus = STATUS_SUCCESS; + + PROPASSERT(_IsMapped()); + PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder); + + if ((_State & CPSS_USERDEFINEDDELETED) || _cSection < 1) + { + StatusAccessDenied(pstatus, "QueryPropertySet: deleted or no section"); + goto Exit; + } + _MSTM(QueryTimeStamps)( + pspss, + (BOOLEAN) ((_Flags & CREATEPROP_NONSIMPLE) != 0)); + pspss->clsid = _pph->clsid; + pspss->fmtid = _GetFormatidOffset( + (_State & CPSS_USERDEFINEDPROPERTIES)? 1 : 0)->fmtid; + pspss->grfFlags = _CodePage == CP_WINUNICODE? + PROPSETFLAG_DEFAULT : PROPSETFLAG_ANSI; + + // ---- + // Exit + // ---- + +Exit: + + return; +} +#endif // !KERNEL + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::SetClassId +// +// Synopsis: Set the classid for the property set code +// +// Arguments: [pclsid] -- pointer to new ClassId +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: None +//--------------------------------------------------------------------------- + +#ifndef KERNEL +VOID +CPropertySetStream::SetClassId(IN GUID const *pclsid, + OUT NTSTATUS *pstatus) +{ + *pstatus = STATUS_SUCCESS; + + PROPASSERT(_IsMapped()); + PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder); + + if (IsReadOnlyPropertySet(_Flags, _State)) + { + StatusAccessDenied(pstatus, "SetClassId: deleted or read-only"); + goto Exit; + } + + _SetModified(); + _pph->clsid = *pclsid; + + // ---- + // Exit + // ---- + +Exit: + + return; +} +#endif // KERNEL + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::EnumeratePropids +// +// Synopsis: enumerates the property ids in a prop set +// +// Arguments: [pkey] -- pointer to bookmark (0 implies beginning) +// [pcprop] -- on input: size; on output: # of props returned. +// [apropids] -- output buffer +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: TRUE if more properties are available +//--------------------------------------------------------------------------- + +#ifndef KERNEL +BOOLEAN +CPropertySetStream::EnumeratePropids( + IN OUT ULONG *pkey, + IN OUT ULONG *pcprop, + OPTIONAL OUT PROPID *apropids, + OUT NTSTATUS *pstatus) +{ + PROPERTYIDOFFSET *ppo, *ppoStart, *ppoMax; + ULONG cprop = 0; + BOOLEAN fMorePropids = FALSE; + PROPID propidPrev = *pkey; + + *pstatus = STATUS_SUCCESS; + + PROPASSERT(_IsMapped()); + PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder); + + if (_State & CPSS_USERDEFINEDDELETED) + { + StatusAccessDenied(pstatus, "EnumeratePropids: deleted"); + goto Exit; + } + + if (_LoadPropertyOffsetPointers(&ppoStart, &ppoMax, pstatus) == NULL) + { + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + else + { + if (propidPrev != 0) // if not first call, start w/last propid + { + for (ppo = ppoStart; ppo < ppoMax; ppo++) + { + if (ppo->propid == propidPrev) + { + ppoStart = ++ppo; + break; + } + } + } + for (ppo = ppoStart; ppo < ppoMax; ppo++) + { + if (ppo->propid != PID_DICTIONARY && + ppo->propid != PID_CODEPAGE && + ppo->propid != PID_LOCALE) + { + if (cprop >= *pcprop) + { + fMorePropids = TRUE; + break; + } + if (apropids != NULL) + { + apropids[cprop] = ppo->propid; + } + cprop++; + propidPrev = ppo->propid; + } + } + } + *pkey = propidPrev; + *pcprop = cprop; + + // ---- + // Exit + // ---- + +Exit: + + return(fMorePropids); +} +#endif + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_LoadPropertyOffsetPointers +// +// Synopsis: Load start and (past) end pointers to PROPERTYIDOFFSET array +// +// Arguments: [pppo] -- pointer to base of PROPERTYIDOFFSET array +// [pppoMax] -- pointer past end of PROPERTYIDOFFSET array +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: Pointer to Section Header, NULL if section not present +// or if there was an error. +//--------------------------------------------------------------------------- + +PROPERTYSECTIONHEADER * +CPropertySetStream::_LoadPropertyOffsetPointers( + OUT PROPERTYIDOFFSET **pppo, + OUT PROPERTYIDOFFSET **pppoMax, + OUT NTSTATUS *pstatus) +{ + PROPERTYSECTIONHEADER *psh; + *pstatus = STATUS_SUCCESS; + + PROPASSERT(_IsMapped()); + + if (_cSection != 0) + { + psh = _GetSectionHeader(); + ULONG cbstm = _MSTM(GetSize)(pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // Ensure that we can read all of the PID/Offset + // table. + + if (cbstm < _oSection + CB_PROPERTYSECTIONHEADER || + cbstm < _oSection + CB_PROPERTYSECTIONHEADER + + psh->cProperties * CB_PROPERTYIDOFFSET) + { + StatusCorruption(pstatus, "LoadPropertyOffsetPointers: stream size"); + goto Exit; + } + + *pppo = psh->rgprop; + *pppoMax = psh->rgprop + psh->cProperties; + } + + // ---- + // Exit + // ---- + +Exit: + if( !NT_SUCCESS(*pstatus) ) + psh = NULL; + + return(psh); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_LoadProperty +// +// Synopsis: return a pointer to the specified property value +// +// Arguments: [propid] -- property id for property +// [pcbprop] -- pointer to return property size, 0 on error +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: SERIALIZEDPROPERTYVALUE * -- NULL if not present +//--------------------------------------------------------------------------- + +SERIALIZEDPROPERTYVALUE * +CPropertySetStream::_LoadProperty( + IN PROPID propid, + OUT OPTIONAL ULONG *pcbprop, + OUT NTSTATUS *pstatus ) +{ + PROPERTYSECTIONHEADER const *psh; + PROPERTYIDOFFSET *ppo, *ppoBase, *ppoMax; + SERIALIZEDPROPERTYVALUE *pprop = NULL; + + *pstatus = STATUS_SUCCESS; + + psh = _LoadPropertyOffsetPointers(&ppoBase, &ppoMax, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + if (psh != NULL) + { + for (ppo = ppoBase; ppo < ppoMax; ppo++) + { + if (IsDwordAligned(ppo->dwOffset) + && + ppo->dwOffset >= CB_PROPERTYSECTIONHEADER + + + psh->cProperties * CB_PROPERTYIDOFFSET + && + psh->cbSection >= ppo->dwOffset + CB_SERIALIZEDPROPERTYVALUE) + { + + if (ppo->propid != propid) + { + continue; + } + pprop = (SERIALIZEDPROPERTYVALUE *) + _MapOffsetToAddress(ppo->dwOffset); + + if (pcbprop != NULL) + { + ULONG cb; + + cb = psh->cbSection - ppo->dwOffset; + if (propid == PID_DICTIONARY) + { + *pcbprop = _DictionaryLength( + (DICTIONARY const *) pprop, + cb, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + else + { + *pcbprop = PropertyLengthNoEH(pprop, cb, 0, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + } + if (pcbprop == NULL || + psh->cbSection >= ppo->dwOffset + *pcbprop) + { + // Success + goto Exit; + } + } + + pprop = NULL; + StatusCorruption(pstatus, "LoadProperty: property offset"); + goto Exit; + } + } + + // ---- + // Exit + // ---- + +Exit: + + return(pprop); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::GetValue +// +// Synopsis: return a pointer to the specified property value +// +// Arguments: [propid] -- property id of property +// [pcbprop] -- pointer to returned property length +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: pointer to property value +//--------------------------------------------------------------------------- + +SERIALIZEDPROPERTYVALUE const * +CPropertySetStream::GetValue( + IN PROPID propid, + OUT ULONG *pcbprop, + OUT NTSTATUS *pstatus) +{ + SERIALIZEDPROPERTYVALUE *pprop = NULL; + + PROPASSERT(_IsMapped()); + PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder); + + if (_State & CPSS_USERDEFINEDDELETED) + { + StatusAccessDenied(pstatus, "GetValue: deleted"); + goto Exit; + } + if (propid == PID_DICTIONARY) + { + DebugTrace(0, DEBTRACE_ERROR, ("GetValue: PID_DICTIONARY\n")); + StatusInvalidParameter(pstatus, "GetValue: PID_DICTIONARY"); + goto Exit; + } + + pprop = NULL; + if (propid == PID_SECURITY || propid == PID_MODIFY_TIME) + { + SERIALIZEDPROPERTYVALUE aprop[2]; + + PROPASSERT(sizeof(aprop) >= sizeof(ULONG) + sizeof(LONGLONG)); + + aprop[0].dwType = PropByteSwap( (DWORD) VT_EMPTY ); + if (propid == PID_SECURITY) + { + if (_MSTM(QuerySecurity)((ULONG *) aprop[0].rgb)) + { + aprop[0].dwType = PropByteSwap( (DWORD) VT_UI4 ); + *pcbprop = 2 * sizeof(ULONG); + } + } + else // (propid == PID_MODIFY_TIME) + { + LONGLONG ll; + + if (_MSTM(QueryModifyTime)(&ll)) + { + *(LONGLONG UNALIGNED *) aprop[0].rgb = PropByteSwap( ll ); + aprop[0].dwType = PropByteSwap( (DWORD) VT_FILETIME ); + *pcbprop = sizeof(ULONG) + sizeof(LONGLONG); + } + } + + if( VT_EMPTY != PropByteSwap(aprop[0].dwType) ) + { + pprop = (SERIALIZEDPROPERTYVALUE *) + newk(mtPropSetStream, NULL) BYTE[*pcbprop]; + + if (pprop == NULL) + { + StatusNoMemory(pstatus, "GetValue: no memory"); + goto Exit; + } + DebugTrace(0, Dbg, ( + "GetValue: pprop=%lx, vt=%lx, cb=%lx\n", + pprop, + PropByteSwap( aprop[0].dwType ), + *pcbprop)); + RtlCopyMemory(pprop, aprop, *pcbprop); + } + } // if (propid == PID_SECURITY || propid == PID_MODIFY_TIME) + + else + { + pprop = _LoadProperty(propid, pcbprop, pstatus); + if( !NT_SUCCESS(*pstatus) ) + { + pprop = NULL; + goto Exit; + } + } // if (propid == PID_SECURITY || propid == PID_MODIFY_TIME) ... else + +#if DBGPROP + if (pprop == NULL) + { + DebugTrace(0, Dbg, ("GetValue: propid=%lx pprop=NULL\n", propid)); + } + else + { + char valbuf[CB_VALUESTRING]; + + DebugTrace(0, Dbg, ( + "GetValue: propid=%lx pprop=%l" szX " vt=%hx val=%s cb=%l" szX "\n", + propid, + _MapAddressToOffset(pprop), + PropByteSwap( pprop->dwType ), + ValueToString(pprop, *pcbprop, valbuf), + *pcbprop)); + } +#endif + + // ---- + // Exit + // ---- + +Exit: + + return(pprop); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::SetValue +// +// Synopsis: update/add/delete property values +// +// Arguments: [cprop] -- count of properties +// [pip] -- pointer to indirect indexes +// [avar] -- PROPVARIANT array +// [apinfo] -- PROPERTY_INFORMATION array +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: None +// +// Note: All the properties in the apinfo array can be classified into +// one of the following categories: +// +// PROPOP_IGNORE: +// No change. Deleting a non-existent property or the same +// propid appears later in the apinfo array. +// +// PROPOP_DELETE: +// Deletion of an existing property. Remove the +// PROPERTYIDOFFSET structure from the property offset array and +// and pack the remaining entries. Delete the property value +// and pack remaining property values +// +// PROPOP_INSERT: +// Addition of a new property. Insert the new PROPERTYIDOFFSET +// structure at the end of the property offset array. Insert +// the new property value at the end of the section/stream. +// +// PROPOP_MOVE: +// A property whose value needs to be updated out of place +// because of a change in the property value's size. A property +// value is moved to the end of the section if it grows or +// shrinks across a DWORD boundary. The existing value is +// removed from the section and the remaining values are packed. +// Then, the new value is inserted at the end of the section. +// The idea here is to move variable length properties that are +// being changed frequently as near as possible to the end of +// the stream to minimize the cost of maintaining a packed set +// of property values. Note that the property offset structure +// is not moved around in the array. +// +// PROPOP_UPDATE: +// A property whose value can be updated in-place. The property +// value's new size is equal to the old size. There are a +// number of variant types that take up a fixed amount of space, +// e.g., VT_I4, VT_R8 etc. This would also apply to any +// variable length property that is updated without changing +// the property value's size across a DWORD boundary. +// +// Note that while the property offset array is itself packed out +// of necessity (to conform to the spec), there may be unused +// entries at the end of the array that are not compressed out of +// the stream when properties are deleted. The unused space is +// detected and reused when new properties are added later. +//--------------------------------------------------------------------------- + +#define CCHUNKSTACK (sizeof(ascnkStack)/sizeof(ascnkStack[0])) + +VOID +CPropertySetStream::SetValue( + IN ULONG cprop, + OPTIONAL IN OUT INDIRECTPROPERTY **ppip, + IN PROPVARIANT const avar[], + IN PROPERTY_INFORMATION *apinfo, + OUT NTSTATUS *pstatus) +{ + // ------ + // Locals + // ------ + + CStreamChunk ascnkStack[6]; + + ULONG cpoReserve; + ULONG cDelete, cInsert, cMove, cUpdate; + +#if DBGPROP + ULONG cIgnore; + char valbuf[CB_VALUESTRING]; + KERNELSELECT( + char valbuf2[CB_VALUESTRING], + char varbuf[CB_VARIANT_TO_STRING]); +#endif + + ULONG iprop; + ULONG cbstm; + LONG cbChange, cbInsertMove; + PROPERTYSECTIONHEADER *psh; + int cIndirect = 0; + CStreamChunk *pscnk0 = NULL; + ULONG cbNewSize; + + + // ---------- + // Initialize + // ---------- + + *pstatus = STATUS_SUCCESS; + + PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder); + + // Worst case, we will need chunks for: + // - the possible growth of the PROPERTYIDOFFSET array, + // - one for EACH property that is being modified, + // - and one chunk to mark the end of the property data. + + CStreamChunkList scl( + 1 + cprop + 1, + 1 + cprop + 1 <= CCHUNKSTACK? ascnkStack : NULL); + + PROPASSERT(_IsMapped()); + + + // Validate that this property set can be written to. + if (IsReadOnlyPropertySet(_Flags, _State)) + { + StatusAccessDenied(pstatus, "SetValue: deleted or read-only"); + goto Exit; + } + + // Mark the propset dirty. + _SetModified(); + + + psh = _GetSectionHeader(); + + cpoReserve = 0; + cDelete = cInsert = cMove = cUpdate = 0; +#if DBGPROP + cIgnore = 0; +#endif + cbInsertMove = cbChange = 0; + + pscnk0 = scl.GetFreeChunk(pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + pscnk0->oOld = 0; + pscnk0->cbChange = 0; + PROPASSERT(pscnk0 == scl.GetChunk(0)); + + cbstm = _oSection + psh->cbSection + _cbTail; + PROPASSERT( cbstm <= _MSTM(GetSize)(pstatus) && NT_SUCCESS(*pstatus) ); + PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail); + + // ------------------------ + // Classify all the updates + // ------------------------ + + // Each update gets classified as ignore, delete, insert, move, + // or update. + // Lookup the old value for each of the properties specified and + // compute the current size. + + for (iprop = 0; iprop < cprop; iprop++) + { + ULONG i; + ULONG cbPropOld; + SERIALIZEDPROPERTYVALUE const *pprop = NULL; + + PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail); + + if (IsReadOnlyPropid(apinfo[iprop].pid)) + { + if (cprop != 1 || + apinfo[0].pid != PID_DICTIONARY || + apinfo[0].cbprop == 0 || + ( avar == NULL || avar[0].vt != VT_DICTIONARY ) + ) + { + DebugTrace(0, DEBTRACE_ERROR, ( + "SetValue: read-only propid=%lx\n", + apinfo[iprop].pid)); + StatusInvalidParameter(pstatus, "SetValue: read-only PROPID"); + goto Exit; + } + } + + if (apinfo[iprop].pid != PID_ILLEGAL) + { + pprop = _LoadProperty(apinfo[iprop].pid, &cbPropOld, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail); + } + + // If this propid appears later in the array, ignore it. + + for (i = iprop + 1; i < cprop; i++) + { + if (apinfo[i].pid == apinfo[iprop].pid) + { +#if DBGPROP + cIgnore++; +#endif + apinfo[iprop].operation = PROPOP_IGNORE; + break; + } + } + + // If this propid appears only once or if it's the last instance, + // load the property and compute its size. + + if (i == cprop) + { + VOID *pvStart = NULL; + + PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail); + if (pprop != NULL) + { + ULONG cbPropNew; + + PROPASSERT(apinfo[iprop].pid != PID_DICTIONARY); + if (apinfo[iprop].cbprop == 0) + { + DebugTrace(0, Dbg, ( + "SetValue: Deleting propid=%lx oOld=%l" szX + " vt=%hx val=%s cb=%l" szX "\n", + apinfo[iprop].pid, + _MapAddressToOffset(pprop), + PropByteSwap( pprop->dwType ), + ValueToString(pprop, cbPropOld, valbuf), + cbPropOld)); + + cbPropNew = 0; + cDelete++; + apinfo[iprop].operation = PROPOP_DELETE; + } + else + { + DebugTrace(0, Dbg, ( + "SetValue: Modifying propid=%lx oOld=%l" szX + " vt=%hx-->%hx cb=%l" szX "-->%l" szX " val=%s-->%s\n", + apinfo[iprop].pid, + _MapAddressToOffset(pprop), + PropByteSwap( pprop->dwType ), + KERNELSELECT( + PropByteSwap( apinfo[iprop].pprop->dwType ), + avar[iprop].vt), + cbPropOld, + apinfo[iprop].cbprop, + ValueToString(pprop, cbPropOld, valbuf), + KERNELSELECT( + ValueToString( + apinfo[iprop].pprop, + apinfo[iprop].cbprop, + valbuf2), + VariantToString( + avar[iprop], + varbuf, + sizeof( varbuf ))))); + + cbPropNew = apinfo[iprop].cbprop; + if (cbPropOld != cbPropNew) + { + cbInsertMove += apinfo[iprop].cbprop; + cMove++; + apinfo[iprop].operation = PROPOP_MOVE; + } + else + { + cUpdate++; + apinfo[iprop].operation = PROPOP_UPDATE; + } + } + + if (apinfo[iprop].operation != PROPOP_UPDATE) + { + // Update the list of chunks that need to be adjusted + CStreamChunk *pscnk = scl.GetFreeChunk(pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + pscnk->oOld = _MapAddressToOffset(pprop); + pscnk->cbChange = - (LONG) cbPropOld; + } + + // Stream size change + cbChange += cbPropNew - cbPropOld; + } + + // Delete non-existent property: + + else if (apinfo[iprop].cbprop == 0) + { +#if DBGPROP + cIgnore++; +#endif + PROPASSERT(apinfo[iprop].pid != PID_DICTIONARY); + apinfo[iprop].operation = PROPOP_IGNORE; + } + + // Insert new property: + + else + { + DebugTrace(0, Dbg, ( + "SetValue: Inserting new propid=%lx vt=%hx " + "cbNew=%l" szX " val=%s\n", + apinfo[iprop].pid, + KERNELSELECT( + PropByteSwap( apinfo[iprop].pprop->dwType ), + avar[iprop].vt), + apinfo[iprop].cbprop, + KERNELSELECT( + ValueToString( + apinfo[iprop].pprop, + apinfo[iprop].cbprop, + valbuf), + VariantToString( + avar[iprop], + varbuf, + sizeof( varbuf ))))); + + PROPASSERT(apinfo[iprop].pid != PID_ILLEGAL); + + cbInsertMove += apinfo[iprop].cbprop; + cbChange += apinfo[iprop].cbprop; + + cInsert++; + apinfo[iprop].operation = PROPOP_INSERT; + } + PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail); + + // In order to delete any old stream or storage type properties + // we count the properties which used to be VT_STREAM etc. + // Also, we count the properties which are to be created as + // streams or storages. + + if (ppip != NULL && apinfo[iprop].operation != PROPOP_IGNORE) + { + if ((pprop != NULL && IsIndirectVarType(PropByteSwap(pprop->dwType))) + || + (avar != NULL && IsIndirectVarType(avar[iprop].vt))) + { + cIndirect++; + } + } + } // if (i == cprop) + } // for (iprop = 0; iprop < cprop; iprop++) + // We're now done classifying each of the properties to be added. + + + // ------------------------------------------------------------ + // Put existing, to-be-overwritten, indirect properties in ppip + // ------------------------------------------------------------ + + // Did the caller give us an INDIRECTPROPERTY buffer, and are + // there indirect properties being added and/or overwritten? + + if (ppip != NULL && cIndirect != 0) + { + // allocate needed space for indirect information + INDIRECTPROPERTY *pipUse; + + if (cprop != 1) + { + pipUse = *ppip = new INDIRECTPROPERTY[cIndirect + 1]; + if (*ppip == NULL) + { + // BUGBUG check no leaks + StatusNoMemory(pstatus, "SetValue: Indirect Name"); + goto Exit; + } + RtlZeroMemory( pipUse, sizeof(INDIRECTPROPERTY) * (cIndirect + 1) ); + pipUse[cIndirect].Index = MAXULONG; + } + else + { + pipUse = (INDIRECTPROPERTY *) ppip; + RtlZeroMemory( pipUse, sizeof(*pipUse) ); + } + + + int iIndirect = 0; + for (iprop = 0; iprop < cprop; iprop++) + { + ULONG cbPropOld; + SERIALIZEDPROPERTYVALUE const *pprop = NULL; + + PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail); + if (apinfo[iprop].operation == PROPOP_IGNORE || + apinfo[iprop].pid == PID_ILLEGAL) + { + continue; + } + + pprop = _LoadProperty(apinfo[iprop].pid, &cbPropOld, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail); + + if (pprop != NULL && IsIndirectVarType(PropByteSwap(pprop->dwType))) + { + CHAR *pszName; + BOOL fAlloc = FALSE; // Did we alloc pszName? + + // we are overwriting an indirect property value + + PROPASSERT(cbPropOld >= 2 * sizeof(ULONG)); + cbPropOld -= 2 * sizeof(ULONG); + pszName = (CHAR *) Add2ConstPtr(pprop->rgb, sizeof(ULONG)); + + // Do we need to convert the name between Ansi & Unicode? + + if (_CodePage != CP_WINUNICODE // Ansi propset + && + OLECHAR_IS_UNICODE) // Unicode OLE APIs + { + // Convert the indirect reference to Unicode + + RtlpConvertToUnicode( + pszName, + cbPropOld, //BUGBUG: Could be byte-granular? + _CodePage, + (WCHAR **) &pszName, + &cbPropOld, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + fAlloc = TRUE; // We need to free pszName + } + else + if (_CodePage == CP_WINUNICODE // Unicode propset + && + !OLECHAR_IS_UNICODE ) // Ansi OLE APIs + { + // Byte-Swap the Unicode indirect reference value + + WCHAR *pwszBuffer = NULL; + + // After this call, the appropriately swapped name will be + // in pszName. If an alloc was required, pszBuffer will point + // to the new buffer (we must free this). + + PBSInPlaceAlloc( (WCHAR**) &pszName, &pwszBuffer, pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // Convert the reference value to Ansi. + + RtlpConvertToMultiByte( + (WCHAR*) pszName, + cbPropOld, + CP_ACP, + (CHAR **) &pszName, + &cbPropOld, + pstatus); + delete( pwszBuffer ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + fAlloc = TRUE; // We need to free pszName + } + + pipUse[iIndirect].poszName = + new OLECHAR[cbPropOld/sizeof(OLECHAR)]; + + if (pipUse[iIndirect].poszName == NULL) + { + StatusNoMemory(pstatus, "SetValue: Indirect Name2"); + goto Exit; + } + + RtlCopyMemory( + pipUse[iIndirect].poszName, + pszName, + cbPropOld); + + + // Is byte-swapping necessary? It is if the property set + // codepage is Unicode, and if OLECHARs are also Unicode. + // If both are Ansi, then no byte-swapping is ever necessary, + // and if one is Ansi and the other is Unicode, then we + // already byte-swapped above during the conversion. + + if (_CodePage == CP_WINUNICODE + && + OLECHAR_IS_UNICODE ) + { + // Convert from propset-endian to system-endian. + PBSBuffer( pipUse[iIndirect].poszName, cbPropOld, sizeof(OLECHAR) ); + } + + // Clean up pszName + + if( fAlloc ) + { + // In the Unicode/MBCS conversions, we did an alloc which + // we must free now. + + PROPASSERT(pszName != NULL); + PROPASSERT( + pszName != + (CHAR *) Add2ConstPtr(pprop->rgb, sizeof(ULONG))); + delete [] pszName; + } + + } // if (pprop != NULL && IsIndirectVarType(PropByteSwap(pprop->dwType))) + + else + if (avar == NULL || !IsIndirectVarType(avar[iprop].vt)) + { + // Neither the property being overwritten, nor the property + // being written is indirect, so we can continue on to + // check the next property (skipping the pipUse updating + // below). + + continue; + } + + // If we get here, we know that either this property is + // an indirect type, or it's overwriting an indirect property. + // We established pipUse[].pszName above, so we just need to + // insert the index and move on. + + pipUse[iIndirect].Index = iprop; + iIndirect++; + + } // for (iprop = 0; iprop < cprop; iprop++) + + PROPASSERT(iIndirect == cIndirect); + + } // if (ppip != NULL && cIndirect != 0) + + + DebugTrace(0, Dbg, ("SetValue: Total Props %l" szX "\n", cprop)); + DebugTrace(0, Dbg, ( + "SetValue: Delete=%l" szX " Insert=%l" szX " Move=%l" szX + " Update=%l" szX " Ignore=%l" szX "\n", + cDelete, + cInsert, + cMove, + cUpdate, + cIgnore)); + + PROPASSERT(cDelete + cInsert + cMove + cUpdate + cIgnore == cprop); + PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail); + + // If we need to grow the property offset array, detect any unused + // entries at the end of the array that are available for reuse. + // and adjust the size difference to reflect the reuse. + + if (cInsert > cDelete) + { + ULONG cpoReuse, cpoExpand; + + cpoExpand = cInsert - cDelete; + cpoReuse = _CountFreePropertyOffsets(pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + if (cpoReuse > cpoExpand) + { + cpoReuse = cpoExpand; + } + cpoExpand -= cpoReuse; + + // If adding a small number of new entries, but not reusing any old + // ones, add 10% more reserved entries (but only up to 10 more) to + // avoid having to continually grow the property offset array for + // clients that insist on adding a few properties at a time. + + // We don't do this for the User-Defined property set, however, + // because older apps assume that the dictionary immediately follows + // the last entry in the PID/Offset array. + + if (cpoExpand >= 1 && cpoExpand <= 2 && cpoReuse == 0 + && + !(_State & CPSS_USERDEFINEDPROPERTIES) + ) + { + cpoReserve = 1 + min(psh->cProperties, 90)/10; + cpoExpand += cpoReserve; + } + DebugTrace(0, Dbg, ( + "SetValue: Reusing %l" szX " offsets, Expanding %l" szX + " offsets\n", + cpoReuse, + cpoExpand)); + + pscnk0->oOld = CB_PROPERTYSECTIONHEADER + + (psh->cProperties + cpoReuse) * CB_PROPERTYIDOFFSET; + pscnk0->cbChange = cpoExpand * CB_PROPERTYIDOFFSET; + cbChange += cpoExpand * CB_PROPERTYIDOFFSET; + PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail); + + } // if (cInsert > cDelete) + + // Do we instead need to *shrink* the PID/Offset array? + // If so, don't shrink any more than necessary. We'll + // leave up to min(10%,10) blank entries. + // Also, if this is the User-Defined property set, + // there can never be any unused entries (for compatibility + // with older apps), so we do a complete shrink. + + else if (cInsert < cDelete) + { + ULONG cpoRemove = 0; + ULONG cpoDelta = cDelete - cInsert; + + // How many blank entries do we already have? + ULONG cpoCurBlankEntries = _CountFreePropertyOffsets( pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + if( _State & CPSS_USERDEFINEDPROPERTIES ) + { + cpoRemove = cpoDelta; + } + else + { + // How many blank entries can we have? + ULONG cpoMaxBlankEntries; + cpoMaxBlankEntries = 1 + min(psh->cProperties - cpoDelta, 90)/10; + + // If, after deleting the properties, we'd have too many, + // remove only enough to get us down to the max allowable. + + if( cpoCurBlankEntries + cpoDelta + > + cpoMaxBlankEntries + ) + { + cpoRemove = cpoCurBlankEntries + cpoDelta - cpoMaxBlankEntries; + } + } // if( _State & CPSS_USERDEFINEDPROPERTIES ) + + // Should we remove any PID/Offset entries? + + if( cpoRemove > 0 ) + { + // Start removing at cpoRemove entries from the end of the PID/Offset array + pscnk0->oOld = CB_PROPERTYSECTIONHEADER + + + (psh->cProperties + cpoCurBlankEntries - cpoRemove) + * + CB_PROPERTYIDOFFSET; + + // Remove the bytes of the cpoRemove entries. + pscnk0->cbChange = - (LONG) (cpoRemove * CB_PROPERTYIDOFFSET ); + + // Adjust the size of the section equivalently. + cbChange += pscnk0->cbChange; + } + + } // else if (cInsert < cDelete) + + PROPASSERT( + cbstm + cbChange >= + _oSection + CB_PROPERTYSECTIONHEADER + + (psh->cProperties + cInsert - cDelete) * CB_PROPERTYIDOFFSET + + _cbTail); + + // If we need to grow the stream, do it now. + + if (cbChange > 0) + { + if (cbstm + cbChange > CBMAXPROPSETSTREAM) + { + StatusDiskFull(pstatus, "SetValue: 256k limit"); + goto Exit; + } + + DebugTrace(0, Dbg, ( + "SetSize(%x) SetValue grow\n", + cbstm + cbChange)); + + _MSTM(SetSize)(cbstm + cbChange, TRUE, (VOID **) &_pph, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // reload all pointers into mapped image: + + psh = _GetSectionHeader(); + PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail); + + // If there's another section after this one, move it back to the + // end of the stream now. + + if (_cbTail != 0) + { + VOID *pvSrc = _MapAbsOffsetToAddress(cbstm - _cbTail); + + PropMoveMemory( + "SetValue(_cbTail:grow)", + psh, + Add2Ptr(pvSrc, cbChange), + pvSrc, + _cbTail); + } + } // if (cbChange > 0) + + // From this point on, the operation should succeed. + // If necessary, the stream has already been grown. + + if (cDelete + cInsert + cMove != 0) + { + // Delete and compact property offsets in the section header. + + if (cDelete + cMove != 0) + { + _DeleteMovePropertyOffsets(apinfo, cprop, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + psh->cProperties -= cDelete; + } + PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail); + + // Use the last chunk to mark the section end, and sort the chunks + // in ascending order by start offset. + + CStreamChunk *pscnk = scl.GetFreeChunk(pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + pscnk->oOld = psh->cbSection; + pscnk->cbChange = 0; + + scl.SortByStartAddress(); + + // If we're reducing the number of properties, we may be shrinking + // the PID/Offset array. So, update that array now, since + // we may remove some bytes at the end of it when we compact + // the stream. + + if( cDelete > cInsert ) + { + _UpdatePropertyOffsets( &scl, pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + + // Compact the Stream following the directions in the + // chunk list. + + _CompactStream(&scl); + + // If the number of properties is holding constant or increasing, + // we can update the PID/Offset array now (because _CompactStream + // allocated any necessary space for us). + + if( cDelete <= cInsert ) + { + _UpdatePropertyOffsets( &scl, pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + + // Set the new section size to include the deleted and inserted + // property offsets, and the deleted property values. + + psh->cbSection += cbChange; + + // Insert new property offsets at the end of the array. + + if (cInsert + cMove != 0) + { + _InsertMovePropertyOffsets( + apinfo, + cprop, + psh->cbSection - cbInsertMove, + cpoReserve, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + psh->cProperties += cInsert; + } + + PROPASSERT(cbstm + cbChange == _oSection + psh->cbSection + _cbTail); + if (_cbTail != 0) + { + // There's another section after this one; if we're shrinking + // the stream, move it up to the new end of the stream now. + + if (cbChange < 0) + { + VOID *pvSrc = _MapAbsOffsetToAddress(cbstm - _cbTail); + + PropMoveMemory( + "SetValue(_cbTail:shrink)", + psh, + Add2Ptr(pvSrc, cbChange), + pvSrc, + _cbTail); + } + _PatchSectionOffsets(cbChange); + } + } // if (cDelete + cInsert + cMove != 0) + + // Copy the new values. + + // NOTE: It might seem unnecessary to delay the in-place updates until + // this for loop. We do not perform the in-place updates while + // classifying the changes because unmapping, remapping and changing + // the size required for handling other updates can fail. In the event + // of such a failure, the update would not be atomic. By delaying the + // in-place updates, we provide some degree of atomicity. + + if (cInsert + cUpdate + cMove != 0) + { + BOOLEAN fDocSummaryInfo = FALSE; + + if ((_State & + (CPSS_USERDEFINEDPROPERTIES | CPSS_DOCUMENTSUMMARYINFO)) == + CPSS_DOCUMENTSUMMARYINFO) + { + fDocSummaryInfo = TRUE; + } + + for (iprop = 0; iprop < cprop; iprop++) + { + // Find property in the offset array and copy in the new value. + if (apinfo[iprop].operation == PROPOP_INSERT || + apinfo[iprop].operation == PROPOP_UPDATE || + apinfo[iprop].operation == PROPOP_MOVE) + { + SERIALIZEDPROPERTYVALUE *pprop; + ULONG cbprop; + ULONG cIndirectProps; + PROPID propid = apinfo[iprop].pid; + + pprop = _LoadProperty(propid, NULL, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + PROPASSERT(pprop != NULL); + + // Special case for SetPropertyNames dictionary creation: + + if (propid == PID_DICTIONARY) + { + PROPASSERT(CB_SERIALIZEDPROPERTYVALUE == CB_DICTIONARY); + PROPASSERT(apinfo[iprop].cbprop == CB_SERIALIZEDPROPERTYVALUE); + PROPASSERT(avar[iprop].vt == VT_DICTIONARY); + ((DICTIONARY *) pprop)->cEntries = 0; + } // if (propid == PID_DICTIONARY) + else + { + // In User, serialize the PROPVARIANT in avar + // directly into the mapped stream. We ask for the + // count of indirect properties, even though we don't + // use it, in order to tell the routine that we + // can handle them. Any handling that is actually + // required must be handled by our caller. + + cbprop = apinfo[iprop].cbprop; + pprop = RtlConvertVariantToPropertyNoEH( + &avar[iprop], + _CodePage, + pprop, + &cbprop, + apinfo[iprop].pid, + FALSE, + &cIndirectProps, + pstatus + ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + PROPASSERT(pprop != NULL); + PROPASSERT(cbprop == DwordAlign(cbprop)); + PROPASSERT(cbprop == apinfo[iprop].cbprop); + + // If writing a DocumentSummaryInformation property + // for which an alignment hack is provided, hack it now. + + if (fDocSummaryInfo && _CodePage != CP_WINUNICODE) + { + // The two vectors in the DocSumInfo property set + // (if Ansi) are un-packed, but we'll adjust the lengths + // so that if a propset reader expects them to be packed, + // it will still work. E.g., a one character string will + // have a length of 4, with padding of NULL characters. + + ULONG cbpropT; + + if (propid == PID_HEADINGPAIR) + { + _FixHeadingPairVector( + PATCHOP_ALIGNLENGTHS, + pprop, + &cbpropT); + } + else + if (propid == PID_DOCPARTS) + { + _FixDocPartsVector( + PATCHOP_ALIGNLENGTHS, + pprop, + &cbpropT); + } + } + DebugTrace(0, Dbg, ( + "SetValue:Insert: pph=%x pprop=%x cb=%3l" szX + " vt=%4x val=%s o=%x oEnd=%x\n", + _pph, + pprop, + apinfo[iprop].cbprop, + PropByteSwap(pprop->dwType), + ValueToString(pprop, apinfo[iprop].cbprop, valbuf), + _MapAddressToOffset(pprop), + _MapAddressToOffset(pprop) + apinfo[iprop].cbprop)); + + } // if (propid == PID_DICTIONARY) ... else + } // if (apinfo[iprop].operation == PROPOP_INSERT || ... + } // for (iprop = 0; iprop < cprop; iprop++) + } // if (cInsert + cUpdate + cMove != 0) + + // If we need to shrink the stream or if we are cleaning up after a + // previous shrink that failed, do it last. + + cbNewSize = _MSTM(GetSize)(pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + if (cbNewSize != cbstm + cbChange) + { + DebugTrace(0, Dbg, ( + "SetSize(%x) SetValue shrink\n", + cbstm + cbChange)); + _MSTM(SetSize)(cbstm + cbChange, TRUE, (VOID **) &_pph, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + + // ---- + // Exit + // ---- + +Exit: + + scl.Delete(); + + if( !NT_SUCCESS(*pstatus) ) + { + if( ppip != NULL && 0 != cIndirect ) + { + INDIRECTPROPERTY *pipUse; + + pipUse = (1 == cprop) ? (INDIRECTPROPERTY*) ppip + : *ppip; + + for (int iFree = 0; iFree < cIndirect; iFree++) + { + delete [] pipUse[iFree].poszName; + } + if (cprop != 1) + { + delete [] pipUse; + *ppip = NULL; + } + } + } // if( !NT_SUCCESS(*pstatus) ) + + +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_CountFreePropertyOffsets, private +// +// Synopsis: counts available (free) property offsets at and of array +// +// Arguments: [pstatus] -- pointer to NTSTATUS code +// +// Returns: count of available property offsets at and of array +//+-------------------------------------------------------------------------- + +ULONG +CPropertySetStream::_CountFreePropertyOffsets(OUT NTSTATUS *pstatus) +{ + PROPERTYIDOFFSET *ppo, *ppoMax; + PROPERTYSECTIONHEADER const *psh; + ULONG oMin = MAXULONG; + ULONG oEnd; + ULONG cFree = 0; + + psh = _LoadPropertyOffsetPointers(&ppo, &ppoMax, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + if (psh != NULL) + { + for ( ; ppo < ppoMax; ppo++) + { + if (oMin > ppo->dwOffset) + { + oMin = ppo->dwOffset; + } + } + } + if (oMin == MAXULONG) + { + goto Exit; + } + PROPASSERT(psh != NULL); + oEnd = CB_PROPERTYSECTIONHEADER + psh->cProperties * CB_PROPERTYIDOFFSET; + PROPASSERT(oEnd <= oMin); + + cFree = (oMin - oEnd)/CB_PROPERTYIDOFFSET; + +Exit: + + return( cFree ); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_DeleteMovePropertyOffsets, private +// +// Synopsis: updates the offsets following the changes to the stream +// +// Arguments: [apinfo] -- array of property information +// [cprop] -- number of properties +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: None +//+-------------------------------------------------------------------------- + +VOID +CPropertySetStream::_DeleteMovePropertyOffsets( + IN PROPERTY_INFORMATION const *apinfo, + IN ULONG cprop, + OUT NTSTATUS *pstatus) +{ + ULONG i; + ULONG cDelete; + PROPERTYSECTIONHEADER const *psh; + PROPERTYIDOFFSET *ppo, *ppoBase, *ppoMax; + + psh = _LoadPropertyOffsetPointers(&ppoBase, &ppoMax, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + PROPASSERT(psh != NULL); + + // Remove the deleted properties + + DebugTrace(0, Dbg, ("Marking deleted/moved property offsets\n")); + cDelete = 0; + for (i = 0; i < cprop; i++) + { + if (apinfo[i].operation == PROPOP_DELETE || + apinfo[i].operation == PROPOP_MOVE) + { + for (ppo = ppoBase; ppo < ppoMax; ppo++) + { + if (ppo->propid == apinfo[i].pid) + { + DebugTrace(0, Dbg, ( + "%sing propid=%lx oOld=%l" szX "\n", + apinfo[i].operation == PROPOP_DELETE? "Delet" : "Mov", + ppo->propid, + ppo->dwOffset)); + if (apinfo[i].operation == PROPOP_DELETE) + { + cDelete++; + ppo->dwOffset = MAXULONG; + } + else + { + ppo->dwOffset = 0; + } + break; + } + } + } + } + + // scan once and compact the property offset array. + + if (cDelete > 0) + { + PROPERTYIDOFFSET *ppoDst = ppoBase; + + DebugTrace(0, Dbg, ("Compacting %l" szX " deleted props\n", cDelete)); + for (ppo = ppoBase; ppo < ppoMax; ppo++) + { + if (ppo->dwOffset != MAXULONG) + { + if (ppo > ppoDst) + { + *ppoDst = *ppo; + } + DebugTrace(0, Dbg, ( + "%sing propid=%lx oOld=%l" szX "\n", + ppo > ppoDst? "Compact" : "Preserv", + ppo->propid, + ppo->dwOffset)); + ppoDst++; + } + } + PROPASSERT(cDelete == (ULONG) (ppoMax - ppoDst)); + DebugTrace(0, Dbg, ("Zeroing %l" szX " entries\n", cDelete)); + RtlZeroMemory(ppoDst, (BYTE *) ppoMax - (BYTE *) ppoDst); + } + + // ---- + // Exit + // ---- + +Exit: + + return; +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_UpdatePropertyOffsets, private +// +// Synopsis: update property offsets in section header +// +// Arguments: [pscl] -- list of chunks in stream that were changed +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: None +//+-------------------------------------------------------------------------- + +VOID +CPropertySetStream::_UpdatePropertyOffsets( + IN CStreamChunkList const *pscl, + OUT NTSTATUS *pstatus) +{ + PROPERTYSECTIONHEADER const *psh; + PROPERTYIDOFFSET *ppo, *ppoMax; + + // Update the offsets for the existing properties. + DebugTrace(0, Dbg, ("Updating existing property offsets\n")); + + psh = _LoadPropertyOffsetPointers(&ppo, &ppoMax, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + PROPASSERT(psh != NULL); + + for ( ; ppo < ppoMax; ppo++) + { + if (ppo->dwOffset != 0) + { +#if DBGPROP + ULONG oOld = ppo->dwOffset; +#endif + ppo->dwOffset = _GetNewOffset(pscl, ppo->dwOffset); + + DebugTrace(0, Dbg, ( + "UpdatePropertyOffsets: propid=%lx offset=%l" szX "-->%l" szX"\n", + ppo->propid, + oOld, + ppo->dwOffset)); + } + } + +Exit: + + return; +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_InsertMovePropertyOffsets, private +// +// Synopsis: updates the offsets following the changes to the stream +// +// Arguments: [apinfo] -- array of property information +// [cprop] -- number of properties +// [oInsert] -- offset in section for new properties +// [cpoReserve] -- newly reserved property offsets to zero +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: None +//+-------------------------------------------------------------------------- + +VOID +CPropertySetStream::_InsertMovePropertyOffsets( + IN PROPERTY_INFORMATION const *apinfo, + IN ULONG cprop, + IN ULONG oInsert, + IN ULONG cpoReserve, + OUT NTSTATUS *pstatus) +{ + ULONG i; + PROPERTYSECTIONHEADER const *psh; + PROPERTYIDOFFSET *ppo, *ppoBase, *ppoMax; + + *pstatus = STATUS_SUCCESS; + + psh = _LoadPropertyOffsetPointers(&ppoBase, &ppoMax, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + PROPASSERT(psh != NULL); + + // Insert the new property offsets at the end. + DebugTrace(0, Dbg, ("Inserting/Moving/Zeroing property offsets\n")); + + for (i = 0; i < cprop; i++) + { + if (apinfo[i].operation == PROPOP_INSERT) + { + ppo = ppoMax++; + ppo->propid = apinfo[i].pid; + } + else if (apinfo[i].operation == PROPOP_MOVE) + { + for (ppo = ppoBase; ppo < ppoMax; ppo++) + { + if (ppo->propid == apinfo[i].pid) + { + PROPASSERT(ppo->dwOffset == 0); + break; + } + } + } + else + { + continue; + } + + PROPASSERT(ppo->propid == apinfo[i].pid); + ppo->dwOffset = oInsert; + oInsert += apinfo[i].cbprop; + + DebugTrace(0, Dbg, ( + "%sing propid=%lx offset=%l" szX " size=%l" szX "\n", + apinfo[i].operation == PROPOP_INSERT? "Insert" : "Mov", + ppo->propid, + ppo->dwOffset, + apinfo[i].cbprop)); + } + DebugTrace(0, Dbg, ( + "Zeroing %x property offsets o=%l" szX " size=%l" szX "\n", + cpoReserve, + _MapAddressToOffset(ppoMax), + cpoReserve * CB_PROPERTYIDOFFSET)); + RtlZeroMemory(ppoMax, cpoReserve * CB_PROPERTYIDOFFSET); + + // ---- + // Exit + // ---- + +Exit: + + return; +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_CompactStream, private +// +// Synopsis: compact all of the property stream chunks +// +// Arguments: [pscl] -- list of chunks in stream that were changed +// +// Returns: None +// +// Note: +// Each chunk structure represents a contiguous range of the stream to be +// completely removed or added. A terminating chunk is appended to +// transparently mark the end of the data stream. The unmodified data +// after each chunk (except the last one) must be preserved and compacted +// as necessary. Chunk structures contain section-relative offsets. +// +// Invariants: +// - Only the first chunk can represent an insertion; subsequent chunks +// always represent deletions. +// - The first chunk can never cause a deletion, but it might not cause +// any change at all. +// - The last chunk is a dummy used to mark the end of the stream. +// +// Algorithm: +// In the optimal case without insertions, each chunk's trailing data can +// be moved ahead (compacted) individually in ascending chunk index order. +// If the first chunk represents an insertion, then some consecutive +// number of data blocks must be moved back (in *descending* chunk index +// order) to make room for the insertion. +// +// Walk the chunk array to find the first point where the accumulated size +// change is less than or equal to zero. +// +// After (possibly) compacting a single range in descending chunk index +// order, compact all remaining chunks in ascending chunk index order. +// +// Example: the first chunk inserts 18 bytes for new property offsets +// (apo'[]), and the second two delete 10 bytes each (chnk1 & chnk2). +// There are four chunks in the array, and three blocks of data to move. +// +// oOld cbChange | AccumulatedChange oNew +// chunk[0]: 38 +18 | +18 38 (apo'[]) +// chunk[1]: 48 -10 | +8 50 (chnk1) +// chunk[2]: 6c -10 | -8 74 (chnk2) +// chunk[3]: 8c 0 | -8 84 (end) +// +// Data blocks are moved in the following sequence to avoid overlap: +// DstOff SrcOff cbMove | Chunk# +// 60 58 14 | 1 chnk1/data2: descending pass (Dst > Src) +// 50 38 10 | 0 apo'[]/data1: descending pass (Dst > Src) +// 74 7c 10 | 2 chnk2/data3: ascending pass (Dst < Src) +// +// SrcOff = oOld - min(cbChange, 0) +// DstOff = SrcOff + AccumulatedChange +// cbMove = chnk[i+1].oOld - SrcOff +// +// Before compacting: +// 0 38 48 58 6c 7c 8c +// | | | | | | | +// V V 10 V -10 V 14 V -10 V 10 V +// +----+-------+----+-------+-------+-------+----------+-------+-------+ +// | ph | afo[] | sh | apo[] | data1 | chnk1 | data2 | chnk2 | data3 | +// +----+-------+----+-------+-------+-------+----------+-------+-------+ +// +// After compacting: +// 0 38 50 60 74 84 +// | | | | | | +// V V +18 V 10 V 14 V 10 V +// +----+-------+----+-------+-----------+-------+----------+-------+ +// | ph | afo[] | sh | apo[] | apo'[] | data1 | data2 | data3 | +// +----+-------+----+-------+-----------+-------+----------+-------+ +//+-------------------------------------------------------------------------- + +VOID +CPropertySetStream::_CompactStream( + IN CStreamChunkList const *pscl) +{ + ULONG i, iMax, iAscend; + LONG cbChangeTotal, cbChangeTotalAscend; + CStreamChunk const *pscnk; + + // Subtract one to avoid operating on the terminating chunk directly. + + iMax = pscl->Count() - 1; + + // If the first chunk does not indicate an insertion, the first for loop is + // exited with i == 0. + // + // If the first chunk represents an insertion, either i == iMax or i itself + // indexes the first chunk that can be compacted normally (in ascending + // chunk index order). In either case, we compact in descending chunk + // index order starting just below i. + + DebugTrace(0, Dbg, ( + "CompactStream: %l" szX " chunks @%lx\n", + pscl->Count(), + pscl->GetChunk(0))); + + cbChangeTotal = 0; + for (i = 0; i < iMax; i++) + { + pscnk = pscl->GetChunk(i); + PROPASSERT(i == 0 || pscnk->cbChange < 0); + if (cbChangeTotal + pscnk->cbChange <= 0) + { + break; + } + cbChangeTotal += pscnk->cbChange; + } + iAscend = i; // save ascending order start + cbChangeTotalAscend = cbChangeTotal; + + DebugTrace(0, Dbg, ("CompactStream: iAscend=%l" szX "\n", iAscend)); + + // First compact range in descending chunk index order if necessary: + + while (i-- > 0) + { + pscnk = pscl->GetChunk(i); + PROPASSERT(i == 0 || pscnk->cbChange < 0); + + DebugTrace(0, Dbg, ("CompactStream: descend: i=%l" szX "\n", i)); +#if DBGPROP + pscl->AssertCbChangeTotal(pscnk, cbChangeTotal); +#endif + _CompactChunk(pscnk, cbChangeTotal, pscl->GetChunk(i + 1)->oOld); + cbChangeTotal -= pscnk->cbChange; + } + + // Compact any remaining chunks in ascending chunk index order. + + cbChangeTotal = cbChangeTotalAscend; + for (i = iAscend; i < iMax; i++) + { + pscnk = pscl->GetChunk(i); + PROPASSERT(i == 0 || pscnk->cbChange < 0); + + DebugTrace(0, Dbg, ("CompactStream: ascend: i=%l" szX "\n", i)); + cbChangeTotal += pscnk->cbChange; +#if DBGPROP + pscl->AssertCbChangeTotal(pscnk, cbChangeTotal); +#endif + _CompactChunk(pscnk, cbChangeTotal, pscl->GetChunk(i + 1)->oOld); + } +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_CompactChunk, private +// +// Synopsis: Compact the data block following one chunk +// +// Arguments: [pscnk] -- pointer to stream chunk +// [cbChangeTotal] -- Bias for this chunk +// [oOldNext] -- offset of next chunk +// +// Returns: None +//+-------------------------------------------------------------------------- + +VOID +CPropertySetStream::_CompactChunk( + IN CStreamChunk const *pscnk, + IN LONG cbChangeTotal, + IN ULONG oOldNext) +{ + LONG cbDelta = cbChangeTotal + min(pscnk->cbChange, 0); // BUGBUG: temp + + DebugTrace(0, Dbg, ( + "CompactChunk(pscnk->oOld=%l" szX ", pscnk->cbChange=%s%l" szX "\n" + " cbChangeTotal=%s%l" szX + ", cbDelta=%s%l" szX // BUGBUG: temp + ", oOldNext=%l" szX ")\n", + pscnk->oOld, + pscnk->cbChange < 0? "-" : "", + pscnk->cbChange < 0? -pscnk->cbChange : pscnk->cbChange, + cbChangeTotal < 0? "-" : "", + cbChangeTotal < 0? -cbChangeTotal : cbChangeTotal, + cbDelta < 0? "-" : "", // BUGBUG: temp + cbDelta < 0? -cbDelta : cbDelta, // BUGBUG: temp + oOldNext)); + + if (cbChangeTotal != 0) + { + ULONG oSrc; + VOID const *pvSrc; + + oSrc = pscnk->oOld - min(pscnk->cbChange, 0); + pvSrc = _MapOffsetToAddress(oSrc); + PropMoveMemory( + "CompactChunk", + _GetSectionHeader(), + (VOID *) Add2ConstPtr(pvSrc, cbChangeTotal), + pvSrc, + oOldNext - oSrc); + } +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_PatchSectionOffsets, private +// +// Synopsis: patch section offsets after moving data around +// +// Arguments: [cbChange] -- size delta +// +// Returns: none +//+-------------------------------------------------------------------------- + +VOID +CPropertySetStream::_PatchSectionOffsets( + LONG cbChange) +{ + ULONG i; + + for (i = 0; i < _cSection; i++) + { + FORMATIDOFFSET *pfo; + + pfo = _GetFormatidOffset(i); + if (pfo->dwOffset > _oSection) + { + DebugTrace(0, DEBTRACE_PROPPATCH, ( + "PatchSectionOffsets(%x): %l" szX " + %l" szX " --> %l" szX "\n", + i, + pfo->dwOffset, + cbChange, + pfo->dwOffset + cbChange)); + pfo->dwOffset += cbChange; + } + } +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_GetNewOffset, private +// +// Synopsis: gets the new address +// +// Arguments: [pscl] -- list of stream chunks that were changed +// [oOld] -- old offset +// +// Returns: new offset +//+-------------------------------------------------------------------------- + +ULONG +CPropertySetStream::_GetNewOffset( + IN CStreamChunkList const *pscl, + IN ULONG oOld) const +{ + // The Chunk list is sorted by start offsets. Locate the chunk to which + // the old offset belongs, then use the total change undergone by the chunk + // to compute the new offset. + + ULONG i; + ULONG iMax = pscl->Count(); + LONG cbChangeTotal = 0; + + for (i = 0; i < iMax; i++) + { + CStreamChunk const *pscnk = pscl->GetChunk(i); + if (pscnk->oOld > oOld) + { + break; + } + cbChangeTotal += pscnk->cbChange; + if (pscnk->oOld == oOld) + { + PROPASSERT(pscnk->cbChange >= 0); + break; + } + } + PROPASSERT(i < iMax); + DebugTrace(0, Dbg, ( + "GetNewOffset: %l" szX " + %l" szX " --> %l" szX "\n", + oOld, + cbChangeTotal, + oOld + cbChangeTotal)); + return(oOld + cbChangeTotal); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_ComputeMinimumSize, private +// +// Synopsis: computes the minimum possible size of a property set stream +// +// Arguments: [cbstm] -- actual stream size +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: computed highest offset in use +//+-------------------------------------------------------------------------- + +ULONG +CPropertySetStream::_ComputeMinimumSize( + IN ULONG cbstm, + OUT NTSTATUS *pstatus) +{ + ULONG oMax = 0; + *pstatus = STATUS_SUCCESS; + + // Don't assume *any* class variables except _pph are loaded yet! + + if (_pph != NULL && cbstm != 0) + { + ULONG cbMin; + ULONG i; + ULONG cSection; + + cSection = 1; + cbMin = 0; + + if (_HasPropHeader()) + { + cSection = _pph->reserved; + cbMin = CB_PROPERTYSETHEADER + cSection * CB_FORMATIDOFFSET; + } + oMax = cbMin; + + // Add the size of each section + + for (i = 0; i < cSection; i++) + { + ULONG oSectionEnd; + + PROPERTYSECTIONHEADER const *psh = _GetSectionHeader(i, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + cbMin += psh->cbSection; + oSectionEnd = _MapAddressToAbsOffset(psh) + psh->cbSection; + if (oMax < oSectionEnd) + { + oMax = oSectionEnd; + } + } + + // The following can't be asserted, because there may be + // a correctable reason why cbstm < oMax at in the Open path + // (see the Excel 5.0a problem in _FixSummaryInformation) + //PROPASSERT(oMax <= cbstm); + + PROPASSERT(cbMin <= oMax); + } + + // ---- + // Exit + // ---- + +Exit: + + // oMax may have been set before an error occurred. + // In this case, set it to zero. + + if( !NT_SUCCESS(*pstatus) ) + oMax = 0; + + return(oMax); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_DictionaryLength +// +// Synopsis: compute length of property set dictionary +// +// Arguments: [pdy] -- pointer to dictionary +// [cbbuf] -- maximum length of accessible memory at pdy +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: Byte-granular count of bytes in dictionary +//+-------------------------------------------------------------------------- + +ULONG +CPropertySetStream::_DictionaryLength( + IN DICTIONARY const *pdy, + IN ULONG cbbuf, + OUT NTSTATUS *pstatus ) const +{ + ENTRY UNALIGNED const *pent; + ULONG cbDict = CB_DICTIONARY; + ULONG i; + + *pstatus = STATUS_SUCCESS; + + for (i = 0, pent = &pdy->rgEntry[0]; + i < PropByteSwap( pdy->cEntries ); + i++, pent = _NextDictionaryEntry( pent )) + { + if (cbbuf < cbDict + CB_ENTRY || + cbbuf < _DictionaryEntryLength( pent )) + { + StatusCorruption(pstatus, "_DictionaryLength: section size"); + goto Exit; + } + + cbDict += _DictionaryEntryLength( pent ); + } + + // ---- + // Exit + // ---- + +Exit: + + return(cbDict); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_PropertyNameLength +// +// Synopsis: compute length (*byte* count) of a property name +// +// Arguments: [pvName] -- property name, in the codepage of +// the property set +// [pcbName] -- pointer to returned byte length of name +// +// Returns: TRUE if name length is valid; else FALSE +// +// Note: The OLE 2.0 format mandates that the null be included as part +// of the length of the name that is stored in the dictionary. +// If the propset uses the Unicode code page, names contain +// WCHARs, otherwise they contain CHARs. In either case, the +// length is a byte count that includes the L'\0' or '\0'. +// +// Also note that this routine does not concern itself with +// the byte-order of the name: for Ansi names, it's irrelevant; +// and for Unicode names, L'\0' == PropByteSwap(L'\0'). +// +//+-------------------------------------------------------------------------- + +BOOLEAN +CPropertySetStream::_PropertyNameLength( + IN VOID const *pvName, + OUT ULONG *pcbName) const +{ + ULONG cch; + + if (_CodePage == CP_WINUNICODE) + { + cch = Prop_wcslen((WCHAR const *) pvName) + 1; + *pcbName = cch * sizeof(WCHAR); + } + else + { + *pcbName = cch = strlen((char const *) pvName) + 1; + } + return(cch > 1 && cch <= CCH_MAXPROPNAMESZ ); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_ComparePropertyNames +// +// Synopsis: Compare two property names. +// +// Pre-Conditions: +// The property names are in the codepage of the +// property set. +// +// Arguments: [pvName1] -- property name 1 +// [pvName2] -- property name 2 +// [fSameByteOrder]-- TRUE: names are both big- or little-endian +// FALSE: 2nd name is wrong endian +// [cbName] -- byte count of name length +// (includes terminator) +// +// Returns: TRUE if names are equal +//+-------------------------------------------------------------------------- + + + +BOOLEAN +CPropertySetStream::_ComparePropertyNames( + IN VOID const *pvName1, + IN VOID const *pvName2, + IN BOOL fSameByteOrder, + IN ULONG cbName) const +{ + // BUGBUG: When the property code is moved to OLE32, + // remove awcByteSwap, and compare unicode strings one + // character at a time, using CharLowerW. + + WCHAR awcByteSwap[ CCH_MAXPROPNAMESZ ]; + +#ifdef WINNT + + if (_CodePage == CP_WINUNICODE) + { + // On big-endian systems, when the second name + // is byte-swapped, we'll byte-swap it into a new + // buffer to use for the comparisson. + +#ifdef BIGENDIAN + if( !fSameByteOrder ) + { + ULONG ulIndex = 0; + PROPASSERT( (WCHAR) L'\0' == ByteSwap( (WCHAR) L'\0' )); + + do + { + awcByteSwap[ ulIndex ] = ByteSwap( ((WCHAR*)pvName2)[ ulIndex ] ); + + } while( awcByteSwap[ulIndex++] != L'\0' ); + } +#endif // BIGENDIAN + + UNICODE_STRING s1, s2; + + s1.Buffer = (WCHAR *) pvName1; + +#ifdef BIGENDIAN + s2.Buffer = fSameByteOrder ? (WCHAR *) pvName2 + : awcByteSwap; +#else + s2.Buffer = (WCHAR *) pvName2; +#endif + + s1.Length = + s1.MaximumLength = + s2.Length = + s2.MaximumLength = (USHORT) (cbName - sizeof(WCHAR)); + + return(RtlEqualUnicodeString(&s1, &s2, TRUE)); + + } // if (_CodePage == CP_WINUNICODE) + + else + { + + STRING s1, s2; + + s1.Buffer = (CHAR *) pvName1; + s2.Buffer = (CHAR *) pvName2; + s1.Length = + s1.MaximumLength = + s2.Length = + s2.MaximumLength = (USHORT) (cbName - sizeof(CHAR)); + return(RtlEqualString(&s1, &s2, TRUE)); + } // if (_CodePage == CP_WINUNICODE) ... else + + +#else // !WINNT + + if (_CodePage == CP_WINUNICODE) + { + // On big-endian systems, when the second name + // is byte-swapped, we'll byte-swap it into a new + // buffer to use for the comparisson. + +#ifdef BIGENDIAN + if( !fSameByteOrder ) + { + ULONG ulIndex = 0; + PROPASSERT( L'\0' == ByteSwap( (WCHAR) L'\0' )); + + do + { + awcByteSwap[ ulIndex ] = ByteSwap( ((WCHAR*)pvName2)[ ulIndex ] ); + + } while( awcByteSwap[ulIndex++] != L'\0' ); + } +#endif // BIGENDIAN + + // Nashville has no Rtl routines: + return(Prop_wcsnicmp( + (WCHAR const *) pvName1, +#ifdef BIGENDIAN + fSameByteOrder ? (WCHAR const *) pvName2 + : awcByteSwap, +#else + (WCHAR const *) pvName2, +#endif + cbName / sizeof(WCHAR) ) == 0); + + } // if (_CodePage == CP_WINUNICODE) + + else + { + + // Nashville has no Rtl routines: + return(_strnicmp( + (char const *) pvName1, + (char const *) pvName2, + cbName) == 0); + } // if (_CodePage == CP_WINUNICODE) ... else + +#endif // !WINNT + +} + + + +//+--------------------------------------------------------------------------- +// Function: CPropertySetStream::DuplicatePropertyName +// +// Synopsis: Duplicate an OLECHAR property name string +// +// Arguments: [poszName] -- input string +// [cbName] -- count of bytes in string (includes null) +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: pointer to new string +//--------------------------------------------------------------------------- + +OLECHAR * +CPropertySetStream::DuplicatePropertyName( + IN OLECHAR const *poszName, + IN ULONG cbName, + OUT NTSTATUS *pstatus) const +{ + OLECHAR *poc = NULL; + *pstatus = STATUS_SUCCESS; + + PROPASSERT(cbName != 0); + PROPASSERT(IsOLECHARString(poszName, cbName)); + + if (cbName != 0) + { + PROPASSERT((ocslen(poszName) + 1) * sizeof(OLECHAR) == cbName); + + poc = (OLECHAR *) _pma->Allocate(cbName); + + if (NULL == poc) + { + StatusNoMemory(pstatus, "DuplicatePropertyName: no memory"); + goto Exit; + } + RtlCopyMemory(poc, poszName, cbName); + } + + // ---- + // Exit + // ---- + +Exit: + + return(poc); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::QueryPropid +// +// Synopsis: translate a property name to a property id using the +// dictionary on the property stream +// +// Arguments: [poszName] -- name of property +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: propid for property if found; PID_ILLEGAL if not found +//--------------------------------------------------------------------------- + +PROPID +CPropertySetStream::QueryPropid( + IN OLECHAR const *poszName, + OUT NTSTATUS *pstatus ) +{ + // ------ + // Locals + // ------ + + ULONG cbname; + DICTIONARY const *pdy; + ENTRY UNALIGNED const *pent; + ULONG cdye; + ULONG cbDict; // BYTE granular size! + VOID const *pvName = NULL; + PROPID propid = PID_ILLEGAL; + + // ---------- + // Initialize + // ---------- + + *pstatus = STATUS_SUCCESS; + + PROPASSERT(_HasPropHeader()); + PROPASSERT(_IsMapped()); + PROPASSERT( IsOLECHARString( poszName, MAXULONG )); + PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder); + + + // Make sure this isn't a UD propset which has been deleted. + if (_State & CPSS_USERDEFINEDDELETED) + { + StatusAccessDenied(pstatus, "QueryPropid: deleted"); + goto Exit; + } + + // Put the name into pvName, converting it if + // necessary to the code-page of the property set. + + pvName = poszName; + if (_CodePage == CP_WINUNICODE // Property set is Unicode + && + !OLECHAR_IS_UNICODE ) // Name is in Ansi + { + // Convert the caller-provided name from the system + // Ansi codepage to Unicode. + + ULONG cb = 0; + pvName = NULL; + _OLECHARToWideChar( poszName, (ULONG)-1, CP_ACP, + (WCHAR**)&pvName, &cb, pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + + else + if (_CodePage != CP_WINUNICODE // Property set is Ansi + && + OLECHAR_IS_UNICODE ) // Name is in Unicode + { + // Convert the caller-provided name from Unicode + // to the propset's Ansi codepage. + + ULONG cb = 0; + pvName = NULL; + _OLECHARToMultiByte( poszName, (ULONG)-1, _CodePage, + (CHAR**)&pvName, &cb, pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + + // How long is this property name (in bytes)? + + if (!_PropertyNameLength(pvName, &cbname)) + { + // The length is invalid. + StatusInvalidParameter(pstatus, "QueryPropid: name length"); + goto Exit; + } + + // Get a pointer to the raw dictionary. + + pdy = (DICTIONARY const *) _LoadProperty(PID_DICTIONARY, &cbDict, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // Is there a dictionary? + + if (pdy != NULL) + { + // Yes - there is a dictionary. + + PROPERTYSECTIONHEADER const *psh = _GetSectionHeader(); + + // Search the dictionary for an entry name matching + // pvName. + + for (cdye = PropByteSwap(pdy->cEntries), pent = &pdy->rgEntry[0]; + cdye > 0; + cdye--, pent = _NextDictionaryEntry( pent )) + { + // Is the length of this dictionary entry valid? + if ( _MapAddressToOffset(pent) + _DictionaryEntryLength( pent ) + > psh->cbSection + ) + { + StatusCorruption(pstatus, "QueryPropid: section size"); + goto Exit; + } + + // If the byte-length matches what we're looking for, + // and the names compare successfully, then we're done. + + if ( CCh2CB(PropByteSwap( pent->cch )) == cbname + && + _ComparePropertyNames(pvName, pent->sz, + FALSE, // pvName, pent->sz could be dif Endians + cbname) + ) + { + propid = PropByteSwap( pent->propid ); + break; + } + } // for (cdye = PropByteSwap(pdy->cEntries), pent = &pdy->rgEntry[0]; ... + + PROPASSERT(cdye > 0 || pent == Add2ConstPtr(pdy, cbDict)); + + } // if (pdy != NULL) + + // ---- + // Exit + // ---- + +Exit: + + // If we did an alloc on the name to munge it, + // delete that buffer now. We must cast pvName + // as a non-const in order for the compiler to accept + // the free call. + + if( pvName != poszName ) + _pma->Free( (VOID*) pvName ); + + return(propid); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::QueryPropertyNameBuf +// +// Synopsis: convert from a property id to a property name using the +// dictionary in the property set, and putting the result +// in a caller-provided buffer. +// +// Arguments: [propid] -- property id to look up +// [aocName] -- output buffer +// [pcbName] -- IN: length of aocName; +// OUT: actual length of name +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: TRUE if name is found in dictionary +//--------------------------------------------------------------------------- + +BOOLEAN +CPropertySetStream::QueryPropertyNameBuf( + IN PROPID propid, + OUT OLECHAR *aocName, + IN OUT ULONG *pcbName, + OUT NTSTATUS *pstatus) +{ + BOOL fFound = FALSE; + DICTIONARY const *pdy; + ULONG cbDict; // BYTE granular size! + + *pstatus = STATUS_SUCCESS; + + PROPASSERT(_IsMapped()); + PROPASSERT(propid != PID_DICTIONARY); + PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder); + PROPASSERT(NULL != aocName); + + // Ensure that this isn't an already-deleted UD propset. + if (_State & CPSS_USERDEFINEDDELETED) + { + StatusAccessDenied(pstatus, "QueryPropertyNameBuf: deleted"); + goto Exit; + } + + // Get a pointer to the raw dictionary. + + pdy = (DICTIONARY const *) _LoadProperty(PID_DICTIONARY, &cbDict, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // Is there a dictionary? + if (pdy != NULL) + { + // Yes - the dictionary was found. + + ULONG cdye; + ENTRY UNALIGNED const *pent; + VOID const *pvDictEnd; + + // Get pointers to the first and last+1 entries. + + pent = pdy->rgEntry; + pvDictEnd = Add2ConstPtr(pdy, cbDict); + + // Scan through the dictionary, searching for 'propid'. + + for (cdye = PropByteSwap(pdy->cEntries), pent = &pdy->rgEntry[0]; + cdye > 0; + cdye--, pent = _NextDictionaryEntry( pent )) + { + // Make sure this entry doesn't go off the end of the + // dictionary. + + if (Add2ConstPtr(pent, _DictionaryEntryLength( pent )) > pvDictEnd) + { + StatusCorruption(pstatus, "QueryPropertyNameBuf: dictionary entry size"); + goto Exit; + } + + // Is this the PID we're looking for? + if (PropByteSwap(pent->propid) == propid) + { + // Yes. Copy or convert the name into the caller's + // buffer. + + // Is a Unicode to Ansi conversion required? + if (_CodePage == CP_WINUNICODE // Property set is Unicode + && + !OLECHAR_IS_UNICODE ) // Caller's buffer is Ansi + { + WCHAR *pwszName = (WCHAR*) pent->sz; + + // If we're byte-swapping, alloc a new buffer, swap + // pwszName into it (getting the string into system-endian + // byte-order), and point pwszName to the result. + + PBSInPlaceAlloc( &pwszName, NULL, pstatus ); + if( !NT_SUCCESS( *pstatus )) goto Exit; + + // Convert the Unicode string in the property set + // to the system default codepage. + + _WideCharToOLECHAR( pwszName, (ULONG)-1, CP_ACP, + &aocName, pcbName, pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // If we allocated a buffer for byte-swapping, + // we don't need it any longer. + + if( pwszName != (WCHAR*) pent->sz ) + delete pwszName; + } + + // Or is an Ansi to Unicode conversion required? + else + if (_CodePage != CP_WINUNICODE // Property set is Ansi + && + OLECHAR_IS_UNICODE ) // Caller's buffer is Unicode + { + // Convert the Ansi property set name from the + // propset's codepage to Unicode. + + _MultiByteToOLECHAR( (CHAR*) pent->sz, (ULONG)-1, _CodePage, + &aocName, pcbName, pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + + // Otherwise, no conversion of the name is required + else + { + // Copy the name into the caller's buffer. + RtlCopyMemory(aocName, pent->sz, + min(CCh2CB(PropByteSwap(pent->cch)), *pcbName)); + + // BUGBUG: Shouldn't we terminate the string if we truncated it? + + // Swap the name to the correct endian + // (This will do nothing if OLECHARs are CHARs). + PBSBuffer( aocName, + min( CCh2CB(PropByteSwap( pent->cch )), *pcbName), + sizeof(OLECHAR) ); + + // Tell the caller the actual size of the name. + *pcbName = CCh2CB( PropByteSwap( pent->cch )); + } + + PROPASSERT( NULL == aocName || IsOLECHARString( aocName, MAXULONG )); + fFound = TRUE; + break; + + } // if (pent->propid == propid) + } // for (cdye = pdy->cEntries, pent = &pdy->rgEntry[0]; ... + + PROPASSERT(fFound || pent == pvDictEnd); + + } // if (pdy != NULL) + + // ---- + // Exit + // ---- + +Exit: + + return( fFound ); +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::QueryPropertyNames +// +// Synopsis: query dictionary names for the passed property ids. +// +// Arguments: [cprop] -- count of name to propid mappings to change +// [apid] -- array of property ids +// [aposz] -- array of pointers to the new names +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: TRUE if the property exists. +//+-------------------------------------------------------------------------- + +BOOLEAN +CPropertySetStream::QueryPropertyNames( + IN ULONG cprop, + IN PROPID const *apid, + OUT OLECHAR *aposz[], + OUT NTSTATUS *pstatus) +{ + DICTIONARY const *pdy; + ULONG cbDict; // BYTE granular size! + ULONG iprop; + BOOLEAN fFound = FALSE; + + *pstatus = STATUS_SUCCESS; + + PROPASSERT(_HasPropHeader()); + PROPASSERT(_IsMapped()); + PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder); + + // If this is an attempt to access a deleted UD + // propset, exit now. + if (_State & CPSS_USERDEFINEDDELETED) + { + StatusAccessDenied(pstatus, "QueryPropertyNames: deleted"); + goto Exit; + } + + // Validate the input array of strings. + for (iprop = 0; iprop < cprop; iprop++) + { + PROPASSERT(aposz[iprop] == NULL); + } + + // Get a pointer to the beginning of the dictionary + pdy = (DICTIONARY const *) _LoadProperty(PID_DICTIONARY, &cbDict, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // Did we get a dictionary? + if (pdy != NULL) + { + // Yes, the dictionary exists. + + ULONG i; + ENTRY UNALIGNED const *pent; + + // Iterate through each of the entries in the dictionary. + + for (i = 0, pent = &pdy->rgEntry[0]; + i < PropByteSwap( pdy->cEntries ); + i++, pent = _NextDictionaryEntry( pent )) + { + // Scan the input array of PIDs to see if one matches + // this dictionary entry. + + for (iprop = 0; iprop < cprop; iprop++) + { + if( PropByteSwap(pent->propid) == apid[iprop] ) + { + // We've found an entry in the dictionary + // that's in the input PID array. Put the property's + // name in the caller-provided array (aposz). + + PROPASSERT(aposz[iprop] == NULL); + + // Do we need to convert to Unicode? + + if (_CodePage != CP_WINUNICODE // Ansi property set + && + OLECHAR_IS_UNICODE) // Unicode property names + { + ULONG cbName = 0; + _MultiByteToOLECHAR( (CHAR*)pent->sz, (ULONG)-1, _CodePage, + &aposz[iprop], &cbName, pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + + // Or, do we need to convert to Ansi? + else + if (_CodePage == CP_WINUNICODE // Unicode property set + && + !OLECHAR_IS_UNICODE) // Ansi property names + { + ULONG cbName = 0; + WCHAR *pwszName = (WCHAR*) pent->sz; + + // If necessary, swap the Unicode name in the dictionary, + // pointing pwszName to the new, byte-swapped, buffer. + + PBSInPlaceAlloc( &pwszName, NULL, pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // And convert to Ansi. + _WideCharToOLECHAR( pwszName, (ULONG)-1, CP_ACP, + &aposz[iprop], &cbName, pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // If we alloced a new buffer for byte-swapping, + // we can free it now. + + if( pwszName != (WCHAR*) pent->sz ) + delete pwszName; + + } // else if (_CodePage == CP_WINUNICODE ... + + // Otherwise, both the propset & in-memory property names + // are both Unicode or both Ansi, so we can just do + // an alloc & copy. + + else + { + aposz[iprop] = DuplicatePropertyName( + (OLECHAR *) pent->sz, + CCh2CB( PropByteSwap( pent->cch )), + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // If necessary, swap the in-memory copy. + PBSBuffer( (OLECHAR*) aposz[iprop], + CCh2CB( PropByteSwap( pent->cch )), + sizeof(OLECHAR) ); + + } // if (_CodePage != CP_WINUNICODE ... else if ... else + + PROPASSERT( IsOLECHARString( aposz[iprop], MAXULONG )); + + fFound = TRUE; + + } // if (pent->propid == apid[iprop]) + } // for (iprop = 0; iprop < cprop; iprop++) + } // for (i = 0, pent = &pdy->rgEntry[0]; + + PROPASSERT(pent == Add2ConstPtr(pdy, cbDict)); + + } // if (pdy != NULL) + + // ---- + // Exit + // ---- + +Exit: + + // If the property name simply didn't exist, return + // a special success code. + + if( !fFound && NT_SUCCESS(*pstatus) ) + *pstatus = STATUS_BUFFER_ALL_ZEROS; + + return( fFound ); + +} // CPropertySetStream::QueryPropertyNames + + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::SetPropertyNames +// +// Synopsis: changes dictionary entry names associated with property ids. +// +// Arguments: [cprop] -- count of name to propid mappings to change +// [apid] -- array of property ids +// [aposz] -- array of pointers to the new names +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: None +// +// Note: Attempting to set a property name for a property that does not +// exist in the property set is not an error. +// +// Attempting to set a property name or property id that would +// result in a duplicate name or property id causes the existing +// entry(ies) to be replaced. +//+-------------------------------------------------------------------------- + +VOID +CPropertySetStream::SetPropertyNames( + IN ULONG cprop, + IN const PROPID *apid, + IN OPTIONAL OLECHAR const * const aposz[], + OUT NTSTATUS *pstatus ) +{ + + // ------ + // Locals + // ------ + + DICTIONARY *pdy = NULL; + ULONG cbDictOld = 0; // Byte granular Old dictionary size + ULONG cbDictOldD = 0; // Dword granular Old dictionary size + ULONG iprop = 0; + ULONG i = 0; + ULONG cDel, cAdd; + LONG cbDel, cbAdd; // Byte granular sizes + LONG cbChangeD; // Dword granular size + ENTRY UNALIGNED *pent; + BOOLEAN fDupPropid = FALSE; + BOOLEAN fDupName = FALSE; + BOOLEAN fDeleteByName = FALSE; + BOOLEAN fDeleteAll = FALSE; + VOID **appvNames = NULL; + + ULONG cbstm; + ULONG oDictionary; + ULONG cbTail; + ULONG cbNewSize; + + // ---------- + // Initialize + // ---------- + + *pstatus = STATUS_SUCCESS; + + DebugTrace(0, Dbg, ( + "SetPropertyNames(cprop=%x, apid=%x, apwsz=%x)\n", + cprop, + apid, + aposz)); + + PROPASSERT(_HasPropHeader()); + PROPASSERT(_IsMapped()); + PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder); + + // -------- + // Validate + // -------- + + // Verify that this propset is modifiable. + if (IsReadOnlyPropertySet(_Flags, _State)) + { + StatusAccessDenied(pstatus, "SetPropertyNames: deleted or read-only"); + goto Exit; + } + + // Verify that none of the names are illegally long. + + if (aposz != NULL) + { + for (iprop = 0; iprop < cprop; iprop++) + { + PROPASSERT( IsOLECHARString( aposz[iprop], MAXULONG )); + + if (ocslen( aposz[iprop] ) > CCH_MAXPROPNAME) + { + StatusInvalidParameter(pstatus, "SetPropertyNames: Name is too long" ); + goto Exit; + } + } + } // if (apwsz != NULL) + + // ---------------------------------------------------------------- + // If necessary, convert each of the caller-provided names: + // to Unicode (if the property set is Unicode) or Ansi (otherwise). + // ---------------------------------------------------------------- + + // In the end, appvNames will have the names in the same codepage + // as the property set. + + appvNames = (VOID **) aposz; + if (appvNames != NULL) + { + // Do we need to convert the caller's names to Ansi? + + if( _CodePage != CP_WINUNICODE // Property set is Ansi + && + OLECHAR_IS_UNICODE ) // Caller's names are Unicode + { + // Allocate an array of cprop string pointers. + + appvNames = (VOID **) newk(mtPropSetStream, NULL) char *[cprop]; + if (appvNames == NULL) + { + StatusNoMemory(pstatus, "SetpropertyNames: Ansi Name Pointers"); + goto Exit; + } + RtlZeroMemory(appvNames, cprop * sizeof(appvNames[0])); + + // Convert the caller-provided property names from Unicode to + // the property set's codepage. + + for (iprop = 0; iprop < cprop; iprop++) + { + ULONG cb = 0; + appvNames[iprop] = NULL; + _OLECHARToMultiByte( (OLECHAR*) aposz[iprop], (ULONG)-1, _CodePage, + (CHAR**) &appvNames[iprop], &cb, pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + } // if( _CodePage != CP_WINUNICODE ... + + // Or, do we need to convert the caller's names to Unicode? + + if( _CodePage == CP_WINUNICODE // Property set is Unicode + && + !OLECHAR_IS_UNICODE ) // Caller's names are Ansi + { + // Allocate an array of cprop string pointers. + + appvNames = (VOID **) newk(mtPropSetStream, NULL) WCHAR *[cprop]; + if (appvNames == NULL) + { + StatusNoMemory(pstatus, "SetpropertyNames: Unicode Name Pointers"); + goto Exit; + } + RtlZeroMemory(appvNames, cprop * sizeof(appvNames[0])); + + // Convert the caller-provided property names from the system + // default Ansi codepage to Unicode. + + for (iprop = 0; iprop < cprop; iprop++) + { + ULONG cb = 0; + appvNames[iprop] = NULL; + _OLECHARToWideChar( (OLECHAR*) aposz[iprop], (ULONG)-1, CP_ACP, + (WCHAR**) &appvNames[iprop], &cb, pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + } // if( _CodePage == CP_WINUNICODE ) + } // if (appvNames != NULL) + + + // ----------------------------------------------------- + // Compute total size of entries to be modified or added + // ----------------------------------------------------- + + cbAdd = 0; + cAdd = 0; + for (iprop = 0; iprop < cprop; iprop++) + { + // Did the caller give us no array of names? If so, + // it means that the name for this PID is to be deleted. + + if (appvNames == NULL) + { + // If the PID is for the dictionary, then it must be the + // only entry in apid, and it indicates that we're going to + // delete all the names in the dictionary. + + if (apid[iprop] == PID_DICTIONARY) + { + if (cprop != 1) + { + StatusInvalidParameter(pstatus, "SetPropertyNames: DeleteAll parms"); + goto Exit; + } + fDeleteAll = TRUE; + } + } + + // Otherwise, we're setting a new name for this PID. + + else + { + ULONG cbname; + + // Validate the caller-provided length. + + if (!_PropertyNameLength(appvNames[iprop], &cbname)) + { + StatusInvalidParameter(pstatus, "SetPropertyNames: name length"); + goto Exit; + } + + // See if this propid or name appears later in the array. + + for (i = iprop + 1; i < cprop; i++) + { + ULONG cbname2; + + if (apid[i] == apid[iprop]) + { + fDupPropid = TRUE; + break; + } + + _PropertyNameLength(appvNames[i], &cbname2); + + if (cbname == cbname2 && + _ComparePropertyNames( + appvNames[iprop], + appvNames[i], + TRUE, // Both names are in the same byte-order + cbname)) + { + fDupName = TRUE; + break; + } + } + + // If this propid appears only once or if it's the last instance, + // count it. If the property set is Unicode, include DWORD padding. + + if (i == cprop) + { + DebugTrace(0, Dbg, ( + _CodePage == CP_WINUNICODE? + "Adding New Entry: propid=%lx L'%ws'\n" : + "Adding New Entry: propid=%lx '%s'\n", + apid[iprop], + appvNames[iprop])); + + cAdd++; + + cbAdd += CB_ENTRY + cbname; + if( _CodePage == CP_WINUNICODE ) + { + cbAdd = DwordAlign( cbAdd ); + } + } + } + } + PROPASSERT( _CodePage == CP_WINUNICODE ? IsDwordAligned( cbAdd ) : TRUE ); + + + // --------------------------------------------- + // Get the dictionary, creating it if necessary. + // --------------------------------------------- + + _SetModified(); + + for (i = 0; ; i++) + { + PROPERTY_INFORMATION pinfo; + PROPVARIANT var; + + pdy = (DICTIONARY *) _LoadProperty(PID_DICTIONARY, &cbDictOld, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + if (pdy != NULL) + { + break; + } + PROPASSERT(i == 0); + if (cprop == 0 || appvNames == NULL) + { + // no dictionary and we are deleting or doing nothing -- return + goto Exit; + } + // create dictionary if it doesn't exist + DebugTrace(0, Dbg, ("Creating empty dictionary\n")); + + PROPASSERT(CB_SERIALIZEDPROPERTYVALUE == CB_DICTIONARY); + pinfo.cbprop = CB_SERIALIZEDPROPERTYVALUE; + pinfo.pid = PID_DICTIONARY; + + var.vt = VT_DICTIONARY; + SetValue(1, NULL, &var, &pinfo, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + Validate(pstatus); // Make sure dictionary was properly created + if( !NT_SUCCESS(*pstatus) ) goto Exit; + DebugTrace(0, Dbg, ("Created empty dictionary\n")); + + } // for (i = 0; ; i++) + + // ---------------------------------------------------------------- + // Compute total size of existing entries to be modified or deleted + // ---------------------------------------------------------------- + + // Walk the dictionary looking for entries which are referenced + // in the caller's 'apid' array or 'appvNames' array. + + cbDel = 0; + cDel = 0; + for (i = 0, pent = &pdy->rgEntry[0]; + i < PropByteSwap( pdy->cEntries ); + i++, pent = _NextDictionaryEntry( pent )) + { + DebugTrace(0, Dbg, ( + _CodePage == CP_WINUNICODE? + "Dictionary Entry @%lx: propid=%lx L'%ws'\n" : + "Dictionary Entry @%lx: propid=%lx '%s'\n", + pent, + PropByteSwap( pent->propid ), + pent->sz )); + + // For this dictionary entry, walk the caller's + // 'apid' and 'appvNames' arrays, looking for a match. + + for (iprop = 0; iprop < cprop; iprop++) + { + // If we get to the bottom of this 'for' loop, + // then we know that we've found an entry to delete. + // If fDeleteAll, or the PID in apid matches this + // dictionary entry, then we can fall to the bottom. + // Otherwise, the following 'if' block checks the + // name in 'appvNames' against this dictionary entry. + + if (!fDeleteAll + && + apid[iprop] != PropByteSwap( pent->propid )) + { + // The caller's PID didn't match this dictionary entry, + // does the name? + + ULONG cbname; + + // If we have no names from the caller, then we obviously + // don't have a match, and we can continue on to check this + // dictionary entry against the next of the caller's PIDs. + + if (appvNames == NULL) + { + continue; + } + + // Or, if this name from the caller doesn't match this + // dictionary entry, we again can continue on to check + // the next of the caller's properties. + + _PropertyNameLength(appvNames[iprop], &cbname); + if (cbname != CCh2CB( PropByteSwap( pent->cch )) + || + !_ComparePropertyNames( + appvNames[iprop], + pent->sz, + FALSE, // appvNames & pent->sz may be dif endians. + cbname) + ) + { + continue; + } + fDeleteByName = TRUE; + + } // if (!fDeleteAll ... + + // If we reach this point, we're going to delete this entry + // in the dictionary. So update cDel & cbDel. + + DebugTrace(0, Dbg, ( + "Deleting Entry (%s) @%lx: propid=%lx\n", + fDeleteAll? "DeleteAll" : + apid[iprop] == PropByteSwap(pent->propid) + ? "replace by propid" + : "replace by name", + pent, + PropByteSwap( pent->propid ))); + + cDel++; + cbDel += _DictionaryEntryLength( pent ); + + // We don't need to continue through the caller's arrays, + // we can move on to the next dictionary entry. + + break; + + } // for (iprop = 0; iprop < cprop; iprop++) + } // for (i = 0, pent = &pdy->rgEntry[0]; ... + + PROPASSERT(pent == Add2Ptr(pdy, cbDictOld)); + PROPASSERT( _CodePage == CP_WINUNICODE ? IsDwordAligned( cbDel ) : TRUE ); + + + cbDictOldD = DwordAlign(cbDictOld); + cbChangeD = DwordAlign(cbDictOld + cbAdd - cbDel) - cbDictOldD; + + cbstm = _oSection + _GetSectionHeader()->cbSection + _cbTail; + oDictionary = _MapAddressToOffset(pdy); + cbTail; + + cbTail = cbstm - (_oSection + oDictionary + cbDictOldD); + + // -------------------------------------------------------- + // Before we change anything, grow the stream if necessary. + // -------------------------------------------------------- + + if (cbChangeD > 0) + { + DebugTrace(0, Dbg, ( + "SetSize(%x) dictionary grow\n", cbstm + cbChangeD)); + if (cbstm + cbChangeD > CBMAXPROPSETSTREAM) + { + StatusDiskFull(pstatus, "SetPropertyNames: 256k limit"); + goto Exit; + } + + _MSTM(SetSize)(cbstm + cbChangeD, TRUE, (VOID **) &_pph, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // reload all pointers into mapped image: + + pdy = (DICTIONARY *) _MapOffsetToAddress(oDictionary); + + // move everything after the dictionary back by cbChangeD bytes. + + PropMoveMemory( + "SetPropertyNames:TailBack", + _GetSectionHeader(), + Add2Ptr(pdy, cbDictOldD + cbChangeD), + Add2Ptr(pdy, cbDictOldD), + cbTail); + } + + // ------------------------------------------------------------------- + // Walk through the existing dictionary and compact unmodified entries + // toward the front. New and modified entries will be appended later. + // ------------------------------------------------------------------- + + VOID *pvSrc; + VOID *pvDst; + ULONG cbCopy; + + pvDst = pvSrc = pent = &pdy->rgEntry[0]; + cbCopy = 0; + + if (!fDeleteAll) + { + ULONG cb; + + for (i = 0; i < PropByteSwap(pdy->cEntries); i++) + { + for (iprop = 0; iprop < cprop; iprop++) + { + if( apid[iprop] == PropByteSwap(pent->propid) ) + { + break; + } + if (fDeleteByName) // if deleting any properties by name + { + ULONG cbname; + + _PropertyNameLength(appvNames[iprop], &cbname); + if (cbname == CCh2CB( PropByteSwap( pent->cch )) + && + _ComparePropertyNames( + appvNames[iprop], + pent->sz, + FALSE, // appvNames & pent->sz may be dif endians + cbname) + ) + { + break; // found an entry to be removed. + } + } + } // for (iprop = 0; iprop < cprop; iprop++) + + cb = _DictionaryEntryLength( pent ); + pent = _NextDictionaryEntry( pent ); + + if (iprop == cprop) // keep the dictionary entry + { + cbCopy += cb; + } + else // remove the dictionary entry + { + if (cbCopy != 0) + { + if (pvSrc != pvDst) + { + PropMoveMemory( + "SetPropertyNames:Compact", + _GetSectionHeader(), + pvDst, + pvSrc, + cbCopy); + } + pvDst = Add2Ptr(pvDst, cbCopy); + cbCopy = 0; + } + pvSrc = pent; + } + } // for (i = 0; i < PropByteSwap(pdy->cEntries); i++) + + // Compact last chunk and point past compacted entries. + + if (cbCopy != 0 && pvSrc != pvDst) + { + PropMoveMemory( + "SetPropertyNames:CompactLast", + _GetSectionHeader(), + pvDst, + pvSrc, + cbCopy); + } + pent = (ENTRY UNALIGNED *) Add2Ptr(pvDst, cbCopy); + + } // if (!fDeleteAll) + + pdy->cEntries = PropByteSwap( PropByteSwap(pdy->cEntries) - cDel ); + + // ------------------------------------ + // Append new and modified entries now. + // ------------------------------------ + + if (appvNames != NULL) + { + // Add each name to the property set. + + for (iprop = 0; iprop < cprop; iprop++) + { + // See if this propid appears later in the array. + + i = cprop; + if (fDupPropid) + { + for (i = iprop + 1; i < cprop; i++) + { + if (apid[i] == apid[iprop]) + { + break; + } + } + } + + // See if this name appears later in the array. + + if (i == cprop && fDupName) + { + ULONG cbname; + + _PropertyNameLength(appvNames[iprop], &cbname); + + for (i = iprop + 1; i < cprop; i++) + { + ULONG cbname2; + + _PropertyNameLength(appvNames[i], &cbname2); + + if (cbname == cbname2 && + _ComparePropertyNames( + appvNames[iprop], + appvNames[i], + TRUE, // Both names are the same endian + cbname)) + { + break; + } + } + } + + // If this propid appears only once or if it's the last instance, + // append the mapping entry. + + if (i == cprop) + { + ULONG cbname; + + // Set the PID & character-count fields for this entry. + _PropertyNameLength(appvNames[iprop], &cbname); + pent->propid = PropByteSwap( apid[iprop] ); + pent->cch = PropByteSwap( CB2CCh( cbname )); + + // Copy the name into the dictionary. + RtlCopyMemory(pent->sz, appvNames[iprop], cbname); + + // If this is a Unicode property set, we need to correct + // the byte-order. + + if( CP_WINUNICODE == _CodePage ) + { + PBSBuffer( pent->sz, cbname, sizeof(WCHAR) ); + } + + // Zero-out the pad bytes. + + RtlZeroMemory( + Add2Ptr(pent->sz, cbname), + DwordRemain((ULONG) pent->sz + cbname)); + + + pent = _NextDictionaryEntry( pent ); + } + } // for (iprop = 0; iprop < cprop; iprop++) + + // We've added all the names, now let's update the entry count. + pdy->cEntries = PropByteSwap( PropByteSwap(pdy->cEntries) + cAdd ); + + } // if (appvNames != NULL) + + // Zero the possible partial DWORD at the end of the dictionary. + + { + ULONG cb = (ULONG) ((BYTE *) pent - (BYTE *) pdy); + PROPASSERT(DwordAlign(cb) == cbDictOldD + cbChangeD); + RtlZeroMemory(pent, DwordRemain(cb)); + } + + + // ----------------------------------------------------- + // Adjust the remaining property offsets in the section. + // ----------------------------------------------------- + + PROPERTYIDOFFSET *ppo, *ppoMax; + PROPERTYSECTIONHEADER *psh; + + psh = _LoadPropertyOffsetPointers(&ppo, &ppoMax, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + PROPASSERT(psh != NULL); + + // Don't rely on the dictionary being the first property. + // Skip PID_DICTIONARY and adjust every other higher entry. + + for ( ; ppo < ppoMax; ppo++) + { + if (ppo->dwOffset > oDictionary) + { + ppo->dwOffset += cbChangeD; + PROPASSERT(ppo->propid != PID_DICTIONARY); + } + } + + // Update the size of the section + psh->cbSection += cbChangeD; + + if (cbChangeD < 0) + { + // move everything after the dictionary forward by cbChangeD bytes. + + PropMoveMemory( + "SetPropertyNames:TailUp", + _GetSectionHeader(), + Add2Ptr(pdy, cbDictOldD + cbChangeD), + Add2Ptr(pdy, cbDictOldD), + cbTail); + } + if (_cbTail != 0) + { + _PatchSectionOffsets(cbChangeD); + } + + // If we need to shrink the stream or if we are cleaning up after a + // previous shrink that failed, do it last. + + cbNewSize = _MSTM(GetSize)(pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + if ( cbNewSize != cbstm + cbChangeD) + { + DebugTrace(0, Dbg, ( + "SetSize(%x) dictionary shrink\n", + cbstm + cbChangeD)); + _MSTM(SetSize)(cbstm + cbChangeD, TRUE, (VOID **) &_pph, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + + // ---- + // Exit + // ---- + +Exit: + + // If we had to convert the array of names into a different + // codepage, delete those temporary buffers now. + + if (appvNames != NULL && appvNames != (VOID **) aposz) + { + for (iprop = 0; iprop < cprop; iprop++) + { + _pma->Free( appvNames[iprop] ); + } + delete [] (char **) appvNames; + } + + DebugTrace(0, Dbg, ("SetPropertyNames() ==> s=%x\n", STATUS_SUCCESS)); + return; +} + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_ValidateStructure +// +// Synopsis: validate property set structure +// +// Arguments: [pstatus] -- pointer to NTSTATUS code +// +// Returns: None +//+-------------------------------------------------------------------------- + +#if DBGPROP +VOID +CPropertySetStream::_ValidateStructure(OUT NTSTATUS *pstatus) +{ + PROPID propid; + ULONG cb; + + OLECHAR aocName[ CCH_MAXPROPNAMESZ ]; + ULONG cbName; + + *pstatus = STATUS_SUCCESS; + + // Walk through properties to make sure all properties are consistent + // and are contained within the section size. A NULL return value + // means _LoadProperty walked the entire section, so we can quit then. + + for (propid = PID_CODEPAGE; propid != PID_ILLEGAL; propid++) + { + SERIALIZEDPROPERTYVALUE const *pprop; + + pprop = GetValue(propid, &cb, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + if (NULL == pprop) + { + break; + } + } + + // Walk through dictionary entries to make sure all entries are consistent + // and are contained within the dictionary size. A FALSE return value + // means QueryPropertyNameBuf walked the entire dictionary, so quit then. + + for (propid = PID_CODEPAGE + 1; propid != PID_ILLEGAL; propid++) + { + BOOL fExists; + cb = 0; + + cbName = sizeof(aocName); + fExists = QueryPropertyNameBuf(propid, aocName, &cbName, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + if( !fExists ) + { + break; + } + } + + if (_cSection > 1) + { + FORMATIDOFFSET const *pfo; + + if (_cSection != 2) + { + DebugTrace(0, DEBTRACE_ERROR, ( + "_ValidateStructure: csection(%x) != 2", + _cSection)); + StatusCorruption(pstatus, "_ValidateStructure: csection != 2"); + goto Exit; + } + pfo = _GetFormatidOffset(0); + if (pfo->fmtid != guidDocumentSummary) + { + DebugTrace(0, DEBTRACE_ERROR, ( + "_ValidateStructure: DocumentSummary[0] fmtid")); + StatusCorruption(pstatus, "_ValidateStructure: DocumentSummary[0] fmtid"); + goto Exit; + } + if (!IsDwordAligned(pfo->dwOffset)) + { + DebugTrace(0, DEBTRACE_ERROR, ( + "_ValidateStructure: dwOffset[0] = %x", + pfo->dwOffset)); + StatusCorruption(pstatus, "_ValidateStructure: dwOffset[0]"); + goto Exit; + } + + pfo = _GetFormatidOffset(1); + if (pfo->fmtid != guidDocumentSummarySection2) + { + DebugTrace(0, DEBTRACE_ERROR, ( + "_ValidateStructure: DocumentSummary[1] fmtid")); + StatusCorruption(pstatus, "_ValidateStructure: DocumentSummary[1] fmtid"); + goto Exit; + } + if (!IsDwordAligned(pfo->dwOffset)) + { + DebugTrace(0, DEBTRACE_ERROR, ( + "_ValidateStructure: dwOffset[1] = %x", + pfo->dwOffset)); + StatusCorruption(pstatus, "_ValidateStructure: dwOffset[1]"); + goto Exit; + } + } // if (_cSection > 1) + + // ---- + // Exit + // ---- + +Exit: + + return; +} +#endif + + +//+-------------------------------------------------------------------------- +// Member: fnPropidCompare +// +// Synopsis: qsort helper to compare propids in a PROPERTYIDOFFSET array. +// +// Arguments: [ppo1] -- pointer to PROPERTYIDOFFSET 1 +// [ppo2] -- pointer to PROPERTYIDOFFSET 2 +// +// Returns: difference +//+-------------------------------------------------------------------------- + +#if DBGPROP +INT _CRTAPI1 +fnPropidCompare(VOID const *ppo1, VOID const *ppo2) +{ + return(((PROPERTYIDOFFSET const *) ppo1)->propid - + ((PROPERTYIDOFFSET const *) ppo2)->propid); +} +#endif + + +//+-------------------------------------------------------------------------- +// Member: fnOffsetCompare +// +// Synopsis: qsort helper to compare offsets in a PROPERTYIDOFFSET array. +// +// Arguments: [ppo1] -- pointer to PROPERTYIDOFFSET 1 +// [ppo2] -- pointer to PROPERTYIDOFFSET 2 +// +// Returns: difference +//+-------------------------------------------------------------------------- + +INT _CRTAPI1 +fnOffsetCompare(VOID const *ppo1, VOID const *ppo2) +{ + return(((PROPERTYIDOFFSET const *) ppo1)->dwOffset - + ((PROPERTYIDOFFSET const *) ppo2)->dwOffset); +} + + +//+-------------------------------------------------------------------------- +// Member: GetStringLength +// +// Synopsis: return length of possibly unicode string. +// +// Arguments: [CodePage] -- TRUE if string is Unicode +// [pwsz] -- pointer to string +// [cb] -- MAXULONG or string length with L'\0' or '\0' +// +// Returns: length of string in bytes including trailing L'\0' or '\0' +//+-------------------------------------------------------------------------- + +ULONG +GetStringLength( + IN USHORT CodePage, + IN WCHAR const *pwsz, + IN ULONG cb) +{ + ULONG i; + + if (CodePage == CP_WINUNICODE) + { + for (i = 0; i < cb/sizeof(WCHAR); i++) + { + if (pwsz[i] == L'\0') + { + break; + } + } + PROPASSERT(cb == MAXULONG || cb == (i + 1) * sizeof(WCHAR)); + return((i + 1) * sizeof(WCHAR)); + } + else + { + char *psz = (char *) pwsz; + + for (i = 0; i < cb; i++) + { + if (psz[i] == '\0') + { + break; + } + } + PROPASSERT(cb == MAXULONG || cb == (i + 1) * sizeof(char)); + return((i + 1) * sizeof(char)); + } +} + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_ValidateProperties +// +// Synopsis: validate properties +// +// Arguments: [pstatus] -- pointer to NTSTATUS code +// +// Returns: None +//+-------------------------------------------------------------------------- + +#if DBGPROP +VOID +CPropertySetStream::_ValidateProperties(OUT NTSTATUS *pstatus) const +{ + PROPERTYIDOFFSET *apo = NULL; + PROPERTYSECTIONHEADER const *psh = _GetSectionHeader(); + static ULONG cValidate = 0; + ULONG cbwasted = 0; + ULONG cbtotal = 0; + + *pstatus = STATUS_SUCCESS; + + cValidate++; + DebugTrace(0, DEBTRACE_PROPVALIDATE, ( + "_ValidateProperties(%x ppsstm=%x state=%x pph=%x)\n", + cValidate, + this, + _State, + _pph)); + + if (psh->cProperties != 0) + { + PROPERTYIDOFFSET *ppo, *ppoMax; + + apo = newk(mtPropSetStream, NULL) PROPERTYIDOFFSET[psh->cProperties + 1]; + if (apo == NULL) + { + *pstatus = STATUS_NO_MEMORY; + goto Exit; + } + + RtlCopyMemory( + apo, + psh->rgprop, + psh->cProperties * CB_PROPERTYIDOFFSET); + + ppoMax = apo + psh->cProperties; + ppoMax->propid = PID_ILLEGAL; + ppoMax->dwOffset = psh->cbSection; + + // Sort by property id and check for duplicate propids: + + qsort(apo, psh->cProperties, sizeof(apo[0]), fnPropidCompare); + + for (ppo = apo; ppo < ppoMax; ppo++) + { + if (ppo->propid == PID_ILLEGAL || + ppo->propid == ppo[1].propid) + { + DebugTrace(0, DEBTRACE_ERROR, ( + "_ValidateProperties(bad propid=%x @%x)\n", + ppo->propid, + ppo->dwOffset)); + StatusCorruption(pstatus, "_ValidateProperties: bad or dup propid"); + goto Exit; + } + } + + // Sort by offset and check for overlapping values: + + qsort(apo, psh->cProperties, sizeof(apo[0]), fnOffsetCompare); + + cbtotal = _oSection; + for (ppo = apo; ppo < ppoMax; ppo++) + { + ULONG cbdiff, cbprop, cbpropraw; + SERIALIZEDPROPERTYVALUE const *pprop; + + cbprop = MAXULONG; + cbpropraw = cbprop; + cbdiff = ppo[1].dwOffset - ppo->dwOffset; + if (IsDwordAligned(ppo->dwOffset) && + IsDwordAligned(ppo[1].dwOffset)) + { + pprop = (SERIALIZEDPROPERTYVALUE const *) + _MapOffsetToAddress(ppo->dwOffset); + + if (ppo->propid == PID_DICTIONARY) + { + cbprop = _DictionaryLength( + (DICTIONARY const *) pprop, + cbdiff, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + cbpropraw = cbprop; + cbprop = DwordAlign(cbprop); + } + else + { + cbprop = PropertyLengthNoEH(pprop, cbdiff, 0, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + cbpropraw = cbprop; + } + DebugTrace(0, DEBTRACE_PROPVALIDATE, ( + "_ValidateProperties(%x) i=%x cb=%x/%x/%x @%x/%x pid=%x\n", + cValidate, + ppo - apo, + cbprop, + cbdiff, + ppo->dwOffset, + pprop, + ppo->propid)); + cbtotal += cbdiff; + + // Technically, the OLE spec allows extra unused space + // between properties, but this implementation never + // writes out streams with space between properties. + + if (cbdiff == cbprop) + { + continue; + } + } + DebugTrace(0, DEBTRACE_ERROR, ( + "_ValidateProperties(bad value length: propid=%x @%x/%x cb=%x/%x/%x ppsstm=%x)\n", + ppo->propid, + ppo->dwOffset, + pprop, + cbpropraw, + cbprop, + cbdiff, + this)); + StatusCorruption(pstatus, "_ValidateProperties: bad property length"); + goto Exit; + + } // for (ppo = apo; ppo < ppoMax; ppo++) + + } // if (psh->cProperties != 0) + + // ---- + // Exit + // ---- + +Exit: + + delete [] apo; + + DebugTrace(0, cbwasted != 0? 0 : Dbg, ( + "_ValidateProperties(wasted %x bytes, total=%x)\n", + cbwasted, + cbtotal)); + +} +#endif + + +#if DBGPROP +typedef struct tagENTRYVALIDATE // ev +{ + ENTRY UNALIGNED const *pent; + CPropertySetStream const *ppsstm; +} ENTRYVALIDATE; +#endif + + +//+-------------------------------------------------------------------------- +// Member: fnEntryPropidCompare +// +// Synopsis: qsort helper to compare propids in a ENTRYVALIDATE array. +// +// Arguments: [pev1] -- pointer to ENTRYVALIDATE 1 +// [pev2] -- pointer to ENTRYVALIDATE 2 +// +// Returns: difference +//+-------------------------------------------------------------------------- + +#if DBGPROP +INT _CRTAPI1 +fnEntryPropidCompare(VOID const *pev1, VOID const *pev2) +{ + return(((ENTRYVALIDATE const *) pev1)->pent->propid - + ((ENTRYVALIDATE const *) pev2)->pent->propid); +} +#endif + + +//+-------------------------------------------------------------------------- +// Member: fnEntryNameCompare +// +// Synopsis: qsort helper to compare names in a ENTRYVALIDATE array. +// +// Arguments: [pev1] -- pointer to ENTRYVALIDATE 1 +// [pev2] -- pointer to ENTRYVALIDATE 2 +// +// Returns: difference +//+-------------------------------------------------------------------------- + +#if DBGPROP +INT _CRTAPI1 +fnEntryNameCompare(VOID const *pev1, VOID const *pev2) +{ + ENTRY UNALIGNED const *pent1; + ENTRY UNALIGNED const *pent2; + INT rc; + + pent1 = ((ENTRYVALIDATE const *) pev1)->pent; + pent2 = ((ENTRYVALIDATE const *) pev2)->pent; + + rc = PropByteSwap(pent1->cch) - PropByteSwap(pent2->cch); + if (rc == 0) + { + rc = !((ENTRYVALIDATE const *) pev1)->ppsstm->_ComparePropertyNames( + pent1->sz, + pent2->sz, + TRUE, // Both names have the same byte-order + ( (ENTRYVALIDATE const *) + pev1 + )->ppsstm->CCh2CB(PropByteSwap( pent1->cch ))); + } + return(rc); +} +#endif + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::_ValidateDictionary +// +// Synopsis: validate property set dictionary +// +// Arguments: [pstatus] -- pointer to NTSTATUS code +// +// Returns: None +//+-------------------------------------------------------------------------- + +#if DBGPROP +VOID +CPropertySetStream::_ValidateDictionary(OUT NTSTATUS *pstatus) +{ + DICTIONARY const *pdy; + ULONG cbDict; // BYTE granular size! + + ENTRYVALIDATE *aev = NULL; + ENTRYVALIDATE *pev, *pevMax; + PROPERTYSECTIONHEADER const *psh; + ENTRY UNALIGNED const *pent; + ENTRY entMax; + VOID const *pvDictEnd; + + *pstatus = STATUS_SUCCESS; + + pdy = (DICTIONARY const *) _LoadProperty(PID_DICTIONARY, &cbDict, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + if (pdy != NULL && PropByteSwap(pdy->cEntries) != 0) + { + aev = newk (mtPropSetStream, NULL) + ENTRYVALIDATE[ PropByteSwap(pdy->cEntries) + 1 ]; + if (aev == NULL) + { + *pstatus = STATUS_NO_MEMORY; + goto Exit; + } + + psh = _GetSectionHeader(); + pent = pdy->rgEntry; + pvDictEnd = Add2ConstPtr(pdy, cbDict); + pevMax = aev + PropByteSwap( pdy->cEntries ); + + for (pev = aev; pev < pevMax; pev++) + { + ULONG cb = _DictionaryEntryLength( pent ); + + if (Add2ConstPtr(pent, cb) > pvDictEnd) + { + DebugTrace(0, DEBTRACE_ERROR, ( + "_ValidateDictionary(bad entry size for propid=%x)\n", + PropByteSwap( pev->pent->propid ))); + StatusCorruption(pstatus, "ValidateDictionary: entry size"); + goto Exit; + } + pev->pent = pent; + pev->ppsstm = this; +#if DBGPROP +#ifdef LITTLEENDIAN + if (_CodePage == CP_WINUNICODE) + { + PROPASSERT(IsUnicodeString((WCHAR const *) pent->sz, + CCh2CB(PropByteSwap( pent->cch )))); + } + else + { + PROPASSERT(IsAnsiString((char const *) pent->sz, + CCh2CB( PropByteSwap( pent->cch )))); + } +#endif +#endif + pent = _NextDictionaryEntry( pent ); + } + if ((VOID const *) pent != pvDictEnd) + { + StatusCorruption(pstatus, "ValidateDictionary: end offset"); + goto Exit; + } + entMax.cch = 0; + entMax.propid = PID_ILLEGAL; + pevMax->pent = &entMax; + pevMax->ppsstm = this; + + // Sort by property id and check for duplicate propids: + + qsort(aev, PropByteSwap(pdy->cEntries), sizeof(aev[0]), fnEntryPropidCompare); + + for (pev = aev; pev < pevMax; pev++) + { + if (PID_ILLEGAL == PropByteSwap(pev->pent->propid) + || + pev[1].pent->propid == pev->pent->propid) + { + DebugTrace(0, DEBTRACE_ERROR, ( + "_ValidateDictionary(bad propid=%x)\n", + PropByteSwap( pev->pent->propid ))); + StatusCorruption(pstatus, "_ValidateDictionary: bad or dup propid"); + goto Exit; + } + } + + // Sort by property name and check for duplicate names: + + qsort(aev, PropByteSwap(pdy->cEntries), sizeof(aev[0]), fnEntryNameCompare); + + for (pev = aev; pev < pevMax; pev++) + { + if (pev->pent->cch == 0 + || + ( pev->pent->cch == pev[1].pent->cch + && + _ComparePropertyNames( + pev->pent->sz, + pev[1].pent->sz, + TRUE, // Names are the same byte-order + CCh2CB(PropByteSwap(pev->pent->cch))) + ) + ) + { + DebugTrace(0, DEBTRACE_ERROR, ( + "_ValidateDictionary(bad name for propid=%x)\n", + PropByteSwap( pev->pent->propid ))); + StatusCorruption(pstatus, "_ValidateDictionary: bad or dup name"); + goto Exit; + } + } // for (pev = aev; pev < pevMax; pev++) + } // if (pdy != NULL && pdy->cEntries != 0) + + // ---- + // Exit + // ---- + +Exit: + + delete [] aev; + +} +#endif // DBGPROP + + +//+-------------------------------------------------------------------------- +// Member: CPropertySetStream::Validate +// +// Synopsis: validate entire property stream +// +// Arguments: [pstatus] -- pointer to NTSTATUS code +// +// Returns: None +//+-------------------------------------------------------------------------- + +#if DBGPROP + +extern "C" BOOLEAN fValidatePropSets = KERNELSELECT(DBG, TRUE); + +VOID +CPropertySetStream::Validate(OUT NTSTATUS *pstatus) +{ + if (fValidatePropSets && (_State & CPSS_USERDEFINEDDELETED) == 0) + { + ULONG cbstm = _MSTM(GetSize)(pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // Walk through section headers to make sure all sections are contained + // within the stream size. + + if (_ComputeMinimumSize(cbstm, pstatus) != 0) + { + // If an error had occurred in the above call, + // it would have returned zero. + + _ValidateStructure( pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + _ValidateProperties( pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + _ValidateDictionary( pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + _ComputeMinimumSize(cbstm, pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + } // if (fValidatePropSets && (_State & CPSS_USERDEFINEDDELETED) == 0) + + // ---- + // Exit + // ---- + +Exit: + + return; +} +#endif + + +//+-------------------------------------------------------------------------- +// Function: CopyPropertyValue +// +// Synopsis: copy a property value into a supplied buffer +// +// Arguments: [pprop] -- property value (possibly NULL) +// [cb] -- property length +// [ppropDst] -- output buffer for property value +// [pcb] -- length of buffer (in); actual length (out) +// +// Returns: None +//--------------------------------------------------------------------------- + +#ifdef WINNT +VOID +CopyPropertyValue( + IN OPTIONAL SERIALIZEDPROPERTYVALUE const *pprop, + IN ULONG cb, + OUT SERIALIZEDPROPERTYVALUE *ppropDst, + OUT ULONG *pcb) +{ +#if DBG==1 + NTSTATUS Status; +#endif + + if (pprop == NULL) + { + static SERIALIZEDPROPERTYVALUE prop = { VT_EMPTY, }; + + pprop = ∝ + cb = CB_SERIALIZEDPROPERTYVALUE; + } + PROPASSERT(cb == PropertyLengthNoEH(pprop, cb, 0, &Status) + && + NT_SUCCESS(Status) ); + + RtlCopyMemory(ppropDst, pprop, min(cb, *pcb)); + *pcb = cb; +} +#endif // WINNT diff --git a/private/dcomidl/propvar.cxx b/private/dcomidl/propvar.cxx new file mode 100644 index 000000000..d0c7ec9de --- /dev/null +++ b/private/dcomidl/propvar.cxx @@ -0,0 +1,3281 @@ +//+-------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1993 +// +// File: propvar.cxx +// +// Contents: PROPVARIANT manipulation code +// +// History: 15-Aug-95 vich created +// 22-Feb-96 MikeHill Moved DwordRemain to "propmac.hxx". +// 09-May-96 MikeHill Use the 'boolVal' member of PropVariant +// rather than the member named 'bool'. +// 22-May-96 MikeHill Use the caller-provided codepage for +// string conversions, not the system default. +// 06-Jun-96 MikeHill Modify CLIPDATA.cbData to include sizeof +// ulClipFmt. +// 12-Jun-96 MikeHill - Use new BSTR alloc/free routines. +// - Added VT_I1 support (under ifdefs) +// - Bug for VT_CF|VT_VECTOR in RtlConvPropToVar +// 25-Jul-96 MikeHill - Removed Win32 SEH. +// - BSTRs: WCHAR=>OLECHAR +// - Added big-endian support. +// 08-Nov-96 MikeHill Restore NT4 export signatures. +// +//--------------------------------------------------------------------------- + +#include <pch.cxx> + +#include <stdio.h> + +#ifndef _MAC +#include <ddeml.h> // for CP_WINUNICODE +#endif + +#include "propvar.h" + +// These optionally-compiled directives tell the compiler & debugger +// where the real file, rather than the copy, is located. +#ifdef _ORIG_FILE_LOCATION_ +#if __LINE__ != 40 +#error File heading has change size +#else +#line 44 "\\nt\\private\\dcomidl\\propvar.cxx" +#endif +#endif + +#ifndef newk +#define newk(Tag, pCounter) new +#endif + + + +// The below variant types are supported in property set streams. In addition, +// the variants found in an array of variants (VT_VECTOR | VT_VARIANT) can only +// contain the types listed below as legal for arrays. Nested vectors of +// VT_VARIANT are specifically *allowed*. +// +// dd xx symbolic name field size +// -- --- ------------- ----- ---- +// -1 - ffff VT_ILLEGAL <none> can't legally be stored +// +// 0 - x00 VT_EMPTY <none> 0 +// 1 - x01 VT_NULL <none> 0 +// +// 16 - x10 VT_I1 CHAR cVal sizeof(char) +// 17 - x11 VT_UI1 UCHAR bVal sizeof(char) +// +// 2 - x02 VT_I2 short iVal sizeof(short) +// 18 - x12 VT_UI2 USHORT uiVal sizeof(short) +// 11 - x0b VT_BOOL VARIANT_BOOL boolVal sizeof(short) +// +// 3 - x03 VT_I4 long lVal sizeof(long) +// 19 - x13 VT_UI4 ULONG ulVal sizeof(long) +// 4 - x04 VT_R4 float fltVal sizeof(long) +// 10 - x0a VT_ERROR SCODE scode sizeof(long) +// +// 20 - x14 VT_I8 LARGE_INTEGER hVal sizeof(ll) +// 21 - x15 VT_UI8 ULARGE_INTEGER uhVal sizeof(ll) +// 5 - x05 VT_R8 double dblVal sizeof(ll) +// 6 - x06 VT_CY CY cyVal sizeof(ll) +// 7 - x07 VT_DATE DATE date sizeof(ll) +// 64 - x40 VT_FILETIME FILETIME filetime sizeof(ll) +// +// 72 - x48 VT_CLSID CLSID *puuid sizeof(GUID) +// +// 65 - x41 VT_BLOB BLOB blob counted array of bytes +// 70 - x46 VT_BLOB_OBJECT BLOB blob counted array of bytes +// 71 - x47 VT_CF CLIPDATA *pclipdata " + ulClipFmt +// 66 - x42 VT_STREAM LPSTR pszVal counted array of bytes +// 68 - x44 VT_STREAMED_OBJECT LPSTR pszVal counted array of bytes +// 67 - x43 VT_STORAGE LPSTR pszVal counted array of bytes +// 69 - x45 VT_STORED_OBJECT LPSTR pszVal counted array of bytes +// 8 - x08 VT_BSTR BSTR bstrVal counted array of bytes +// 30 - x1e VT_LPSTR LPSTR pszVal counted array of bytes +// +// 31 - x1f VT_LPWSTR LPWSTR pwszVal counted array of WCHARs +// +// x1010 VT_VECTOR | VT_I1 CAC cac cElems * sizeof(char) +// x1011 VT_VECTOR | VT_UI1 CAUB caub cElems * sizeof(char) +// +// x1002 VT_VECTOR | VT_I2 CAI cai cElems * sizeof(short) +// x1012 VT_VECTOR | VT_UI2 CAUI caui cElems * sizeof(short) +// x100b VT_VECTOR | VT_BOOL CABOOL cabool cElems * sizeof(short) +// +// x1003 VT_VECTOR | VT_I4 CAL cal cElems * sizeof(long) +// x1013 VT_VECTOR | VT_UI4 CAUL caul cElems * sizeof(long) +// x1004 VT_VECTOR | VT_R4 CAFLT caflt cElems * sizeof(long) +// x100a VT_VECTOR | VT_ERROR CAERROR cascode cElems * sizeof(long) +// +// x1014 VT_VECTOR | VT_I8 CAH cah cElems * sizeof(ll) +// x1015 VT_VECTOR | VT_UI8 CAUH cauh cElems * sizeof(ll) +// x1005 VT_VECTOR | VT_R8 CADBL cadbl cElems * sizeof(ll) +// x1006 VT_VECTOR | VT_CY CACY cacy cElems * sizeof(ll) +// x1007 VT_VECTOR | VT_DATE CADATE cadate cElems * sizeof(ll) +// x1040 VT_VECTOR | VT_FILETIME CAFILETIME cafiletime cElems * sizeof(ll) +// +// x1048 VT_VECTOR | VT_CLSID CACLSID cauuid cElems * sizeof(GUID) +// +// x1047 VT_VECTOR | VT_CF CACLIPDATA caclipdata cElems cntarray of bytes +// x1008 VT_VECTOR | VT_BSTR CABSTR cabstr cElems cntarray of bytes +// x101e VT_VECTOR | VT_LPSTR CALPSTR calpstr cElems cntarray of bytes +// +// x101f VT_VECTOR | VT_LPWSTR CALPWSTR calpwstr cElems cntarray of WCHAR +// +// x100c VT_VECTOR | VT_VARIANT CAPROPVARIANT capropvar cElems variants +// (recurse on each) + + +//+--------------------------------------------------------------------------- +// Function: RtlpConvertToUnicode, private +// +// Synopsis: Convert a MultiByte string to a Unicode string +// +// Arguments: [pch] -- pointer to MultiByte string +// [cb] -- byte length of MultiByte string +// [CodePage] -- property set codepage +// [ppwc] -- pointer to returned pointer to Unicode string +// [pcb] -- returned byte length of Unicode string +// +// Returns: Nothing +//--------------------------------------------------------------------------- + +VOID +RtlpConvertToUnicode( + IN CHAR const *pch, + IN ULONG cb, + IN USHORT CodePage, + OUT WCHAR **ppwc, + OUT ULONG *pcb, + OUT NTSTATUS *pstatus) +{ + WCHAR *pwszName; + + *pstatus = STATUS_SUCCESS; + + PROPASSERT(pch != NULL); + PROPASSERT(ppwc != NULL); + PROPASSERT(pcb != NULL); + + *ppwc = NULL; + *pcb = 0; + + ULONG cwcName; + + PROPASSERT(UnicodeCallouts.pfnMultiByteToWideChar != NULL); + + pwszName = NULL; + cwcName = 0; + while (TRUE) + { + cwcName = (*UnicodeCallouts.pfnMultiByteToWideChar)( + CodePage, + 0, // dwFlags + pch, + cb, + pwszName, + cwcName); + if (cwcName == 0) + { + delete [] pwszName; + // If there was an error, assume that it was a code-page + // incompatibility problem. + StatusError(pstatus, "RtlpConvertToUnicode: MultiByteToWideChar error", + STATUS_UNMAPPABLE_CHARACTER); + goto Exit; + } + if (pwszName != NULL) + { + DebugTrace(0, DEBTRACE_PROPERTY, ( + "RtlpConvertToUnicode: pch='%s'[%x] pwc='%ws'[%x->%x]\n", + pch, + cb, + pwszName, + *pcb, + cwcName * sizeof(WCHAR))); + break; + } + *pcb = cwcName * sizeof(WCHAR); + *ppwc = pwszName = (WCHAR *) newk(mtPropSetStream, NULL) CHAR[*pcb]; + if (pwszName == NULL) + { + StatusNoMemory(pstatus, "RtlpConvertToUnicode: no memory"); + goto Exit; + } + } + + // ---- + // Exit + // ---- + +Exit: + + return; +} + + +//+--------------------------------------------------------------------------- +// Function: RtlpConvertToMultiByte, private +// +// Synopsis: Convert a Unicode string to a MultiByte string +// +// Arguments: [pwc] -- pointer to Unicode string +// [cb] -- byte length of Unicode string +// [CodePage] -- property set codepage +// [ppch] -- pointer to returned pointer to MultiByte string +// [pcb] -- returned byte length of MultiByte string +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: Nothing +//--------------------------------------------------------------------------- + +VOID +RtlpConvertToMultiByte( + IN WCHAR const *pwc, + IN ULONG cb, + IN USHORT CodePage, + OUT CHAR **ppch, + OUT ULONG *pcb, + OUT NTSTATUS *pstatus) +{ + ULONG cbName; + CHAR *pszName; + + *pstatus = STATUS_SUCCESS; + + PROPASSERT(pwc != NULL); + PROPASSERT(ppch != NULL); + PROPASSERT(pcb != NULL); + + *ppch = NULL; + *pcb = 0; + + PROPASSERT(UnicodeCallouts.pfnWideCharToMultiByte != NULL); + + pszName = NULL; + cbName = 0; + while (TRUE) + { + cbName = (*UnicodeCallouts.pfnWideCharToMultiByte)( + CodePage, + 0, // dwFlags + pwc, + cb/sizeof(WCHAR), + pszName, + cbName, + NULL, // lpDefaultChar + NULL); // lpUsedDefaultChar + if (cbName == 0) + { + delete [] pszName; + // If there was an error, assume that it was a code-page + // incompatibility problem. + StatusError(pstatus, "RtlpConvertToMultiByte: WideCharToMultiByte error", + STATUS_UNMAPPABLE_CHARACTER); + goto Exit; + } + if (pszName != NULL) + { + DebugTrace(0, DEBTRACE_PROPERTY, ( + "RtlpConvertToMultiByte: pwc='%ws'[%x] pch='%s'[%x->%x]\n", + pwc, + cb, + pszName, + *pcb, + cbName)); + break; + } + *pcb = cbName; + *ppch = pszName = newk(mtPropSetStream, NULL) CHAR[cbName]; + if (pszName == NULL) + { + StatusNoMemory(pstatus, "RtlpConvertToMultiByte: no memory"); + goto Exit; + } + } + + // ---- + // Exit + // ---- + +Exit: + + return; +} + + +//+--------------------------------------------------------------------------- +// Function: RtlConvertVariantToProperty, private +// +// Synopsis: Convert a PROPVARIANT to a SERIALIZEDPROPERTYVALUE +// +// Arguments: [pvar] -- pointer to PROPVARIANT +// [CodePage] -- property set codepage +// [pprop] -- pointer to SERIALIZEDPROPERTYVALUE +// [pcb] -- pointer to remaining stream length, +// updated to actual property size on return +// [pid] -- propid (used if indirect) +// [fVariantVector] -- TRUE if recursing on VT_VECTOR | VT_VARIANT +// [pcIndirect] -- pointer to indirect property count +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: NULL if buffer too small, else input [pprop] argument +//--------------------------------------------------------------------------- + + +// Define a macro which sets a variable named 'cbByteSwap', but +// only on big-endian builds. This value is not needed on little- +// endian builds (because byte-swapping is not necessary). + +#ifdef BIGENDIAN +#define CBBYTESWAP(cb) cbByteSwap = cb +#elif LITTLEENDIAN +#define CBBYTESWAP(cb) +#else +#error Either BIGENDIAN or LITTLEENDIAN must be set. +#endif + + +// First, define a wrapper for this function which returns errors +// using NT Exception Handling, rather than returning an NTSTATUS. + +#if defined(WINNT) && !defined(IPROPERTY_DLL) + +SERIALIZEDPROPERTYVALUE * +RtlConvertVariantToProperty( + IN PROPVARIANT const *pvar, + IN USHORT CodePage, + OPTIONAL OUT SERIALIZEDPROPERTYVALUE *pprop, + IN OUT ULONG *pcb, + IN PROPID pid, + IN BOOLEAN fVariantVector, + OPTIONAL OUT ULONG *pcIndirect) +{ + SERIALIZEDPROPERTYVALUE *ppropRet; + NTSTATUS status; + + ppropRet = RtlConvertVariantToPropertyNoEH( + pvar, CodePage, pprop, + pcb, pid, fVariantVector, + pcIndirect, &status ); + + if (!NT_SUCCESS( status )) + RtlRaiseStatus( status ); + + return (ppropRet ); + +} + +#endif // #if defined(WINNT) && !defined(IPROPERTY_DLL) + +// Now define the body of the function, returning errors with an +// NTSTATUS value instead of raising. + +SERIALIZEDPROPERTYVALUE * +RtlConvertVariantToPropertyNoEH( + IN PROPVARIANT const *pvar, + IN USHORT CodePage, + OPTIONAL OUT SERIALIZEDPROPERTYVALUE *pprop, + IN OUT ULONG *pcb, + IN PROPID pid, + IN BOOLEAN fVariantVector, + OPTIONAL OUT ULONG *pcIndirect, + OUT NTSTATUS *pstatus) +{ + *pstatus = STATUS_SUCCESS; + + // ------ + // Locals + // ------ + CHAR *pchConvert = NULL; + + ULONG count; + BYTE *pbdst; + ULONG cbch = 0; + ULONG cbchdiv = 0; + ULONG cb = 0; + + // Size of byte-swapping units (e.g. 2 to swap a WORD). + INT cbByteSwap = 0; + + ULONG const *pcount = NULL; + VOID const *pv = NULL; + LONG *pclipfmt = NULL; + BOOLEAN fCheckNullSource = (BOOLEAN) ((pvar->vt & VT_VECTOR) != 0); + BOOLEAN fIllegalType = FALSE; + VOID **ppv; + OLECHAR aocName[4 + 10 + 1]; // enough for "prop%lu" + L'\0' + + // ------------------------------------------------------- + // Analyze the PropVariant, and store information about it + // in fIllegalType, cb, pv, pcount, count, pclipfmt, + // fCheckNullSource, cbch, chchdiv, and ppv. + // ------------------------------------------------------- + + switch (pvar->vt) + { + case VT_EMPTY: + case VT_NULL: + fIllegalType = fVariantVector; + break; + +#ifdef PROPVAR_VT_I1 + case VT_I1: + AssertByteField(cVal); // VT_I1 +#endif + case VT_UI1: + AssertByteField(bVal); // VT_UI1 + cb = sizeof(pvar->bVal); + pv = &pvar->bVal; + break; + + case VT_I2: + case VT_UI2: + case VT_BOOL: + AssertShortField(iVal); // VT_I2 + AssertShortField(uiVal); // VT_UI2 + AssertShortField(boolVal); // VT_BOOL + cb = sizeof(pvar->iVal); + pv = &pvar->iVal; + + // If swapping, swap as a WORD + CBBYTESWAP(cb); + break; + + case VT_I4: + case VT_UI4: + case VT_R4: + case VT_ERROR: + AssertLongField(lVal); // VT_I4 + AssertLongField(ulVal); // VT_UI4 + AssertLongField(fltVal); // VT_R4 + AssertLongField(scode); // VT_ERROR + cb = sizeof(pvar->lVal); + pv = &pvar->lVal; + + // If swapping, swap as a DWORD + CBBYTESWAP(cb); + break; + + case VT_I8: + case VT_UI8: + case VT_FILETIME: + AssertLongLongField(hVal); // VT_I8 + AssertLongLongField(uhVal); // VT_UI8 + AssertLongLongField(filetime); // VT_FILETIME + cb = sizeof(pvar->hVal); + pv = &pvar->hVal; + + // If swapping, swap each DWORD independently. + CBBYTESWAP(sizeof(DWORD)); + break; + + case VT_R8: + case VT_CY: + case VT_DATE: + AssertLongLongField(dblVal); // VT_R8 + AssertLongLongField(cyVal); // VT_CY + AssertLongLongField(date); // VT_DATE + cb = sizeof(pvar->dblVal); + pv = &pvar->dblVal; + + // If swapping, swap as a LONGLONG (64 bits). + CBBYTESWAP(cb); + break; + + case VT_CLSID: + AssertStringField(puuid); // VT_CLSID + cb = sizeof(GUID); + pv = pvar->puuid; + fCheckNullSource = TRUE; + + // If swapping, special handling is required. + CBBYTESWAP( CBBYTESWAP_UID ); + break; + + case VT_CF: + + // Validate the PropVariant + if (pvar->pclipdata == NULL + || + pvar->pclipdata->cbSize < sizeof(pvar->pclipdata->ulClipFmt) ) + { + StatusInvalidParameter(pstatus, "RtlConvertVariantToProperty: pclipdata NULL"); + goto Exit; + } + + // How many bytes should we copy? + cb = CBPCLIPDATA( *(pvar->pclipdata) ); + + // Identify the value for this property's count field. + // (which includes sizeof(ulClipFmt)) + count = pvar->pclipdata->cbSize; + pcount = &count; + + // Identify the clipdata's format & data + pclipfmt = &pvar->pclipdata->ulClipFmt; + pv = pvar->pclipdata->pClipData; + + fCheckNullSource = TRUE; + + // Note that no byte-swapping of 'pv' is necessary. + break; + + case VT_BLOB: + case VT_BLOB_OBJECT: + fIllegalType = fVariantVector; + pcount = &pvar->blob.cbSize; + cb = *pcount; + pv = pvar->blob.pBlobData; + fCheckNullSource = TRUE; + + // Note that no byte-swapping of 'pv' is necessary. + break; + + case VT_STREAM: + case VT_STREAMED_OBJECT: + case VT_STORAGE: + case VT_STORED_OBJECT: + if (fVariantVector) + { + fIllegalType = TRUE; + break; + } + if (pcIndirect != NULL) + { + (*pcIndirect)++; + PROPGENPROPERTYNAME(aocName, pid); + pv = aocName; + } + else + { + PROPASSERT( + pvar->pwszVal == NULL || + IsUnicodeString(pvar->pwszVal, MAXULONG)); + pv = pvar->pwszVal; + } + count = 1; // default to forcing an error on NULL pointer + goto noansicheck; + + case VT_LPSTR: + PROPASSERT( + pvar->pszVal == NULL || + IsAnsiString(pvar->pszVal, MAXULONG)); + // FALLTHROUGH + + case VT_BSTR: + count = 0; // allow NULL pointer + pv = pvar->pszVal; +noansicheck: + AssertStringField(pwszVal); // VT_STREAM, VT_STREAMED_OBJECT + AssertStringField(pwszVal); // VT_STORAGE, VT_STORED_OBJECT + AssertStringField(bstrVal); // VT_BSTR + AssertStringField(pszVal); // VT_LPSTR + + // We have the string for an LPSTR, BSTR, or indirect + // property pointed to by 'pv'. Now we'll perform any + // Ansi/Unicode conversions and byte-swapping that's + // necessary (putting the result in 'pv'). + + if (pv == NULL) + { + fCheckNullSource = TRUE; + } + + else + if (pvar->vt == VT_LPSTR) + { + count = strlen((char *) pv) + 1; + + // If the propset is Unicode, convert the LPSTR to Unicode. + + if (CodePage == CP_WINUNICODE) + { + // Convert to Unicode. + + PROPASSERT(IsAnsiString((CHAR const *) pv, count)); + RtlpConvertToUnicode( + (CHAR const *) pv, + count, + CP_ACP, // Variants are in the system codepage + (WCHAR **) &pchConvert, + &count, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // 'pv' always has the ready-to-serialize string. + pv = pchConvert; + + // This unicode string may require byte-swapping. + CBBYTESWAP( sizeof(WCHAR) ); + } + } // else if (pvar->vt == VT_LPSTR) + + else + { + // If this is a BSTR, increment the count to include + // the string terminator. + if (pvar->vt == VT_BSTR) + { + count = BSTRLEN(pv); + + // Verify that the input BSTR is terminated. + if (pvar->bstrVal[count/sizeof(OLECHAR)] != OLESTR('\0')) + { + PROPASSERT(pvar->bstrVal[count/sizeof(OLECHAR)] == OLESTR('\0')); + StatusInvalidParameter(pstatus, + "RtlConvertVariantToProperty: bad BSTR null char"); + goto Exit; + } + + // Increment the count to include the terminator. + count += sizeof(OLECHAR); + } + else + { + count = (Prop_ocslen((OLECHAR *) pv) + 1) * sizeof(OLECHAR); + PROPASSERT(IsOLECHARString((OLECHAR const *) pv, count)); + } + + // This string is either an indirect property name, + // or a BSTR, both of which could be Ansi or Unicode. + + if (CodePage != CP_WINUNICODE // Ansi property set + && + OLECHAR_IS_UNICODE // The PropVariant is in Unicode + ) + { + // A Unicode to Ansi conversion is required. + + PROPASSERT( IsUnicodeString( (WCHAR*)pv, count )); + + RtlpConvertToMultiByte( + (WCHAR const *) pv, + count, + CodePage, + &pchConvert, + &count, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + pv = pchConvert; + } + + else + if (CodePage == CP_WINUNICODE // Unicode property set, + && + !OLECHAR_IS_UNICODE // The PropVariant is in Ansi + ) + { + // An Ansi to Unicode conversion is required. + + PROPASSERT(IsAnsiString((CHAR const *) pv, count)); + PROPASSERT(sizeof(OLECHAR) == sizeof(CHAR)); + + RtlpConvertToUnicode( + (CHAR const *) pv, + count, + CP_ACP, // In-mem BSTR is in system CP + (WCHAR **) &pchConvert, + &count, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // 'pv' always holds the ready-to-serialize value. + pv = pchConvert; + + // This unicode string may require swapping. + CBBYTESWAP( sizeof(WCHAR) ); + } + + else + if (CodePage == CP_WINUNICODE) + { + // No conversion is required (i.e., both 'pv' and the + // property set are Unicode). But we must remember + // to perform a byte-swap (if byte-swapping is necessary). + + CBBYTESWAP( sizeof(WCHAR) ); + } + } // if (pv == NULL) ... else if ... else + + // Validate 'pv'. + +#ifdef LITTLEENDIAN + // BUGBUG: Create an IsByteSwappedUnicodeString routine + PROPASSERT( NULL == pv + || + CodePage == CP_WINUNICODE && IsUnicodeString((WCHAR*)pv, count) + || + CodePage != CP_WINUNICODE && IsAnsiString((CHAR*)pv, count) ); +#endif + + cb = count; + pcount = &count; + break; + + case VT_LPWSTR: + AssertStringField(pwszVal); // VT_LPWSTR + PROPASSERT( + pvar->pwszVal == NULL || + IsUnicodeString(pvar->pwszVal, MAXULONG)); + + pv = pvar->pwszVal; + if (pv == NULL) + { + count = 0; + fCheckNullSource = TRUE; + } + else + { + // Calculate the [length] field. + count = Prop_wcslen(pvar->pwszVal) + 1; + + // If byte-swapping will be necessary to get to the serialized + // format, we'll do so in units of WCHARs. + + CBBYTESWAP( sizeof(WCHAR) ); + } + + cb = count * sizeof(WCHAR); + pcount = &count; + break; + + // Vector properties: + +#ifdef PROPVAR_VT_I1 + case VT_VECTOR | VT_I1: + AssertByteVector(cac); // VT_I1 +#endif + case VT_VECTOR | VT_UI1: + AssertByteVector(caub); // VT_UI1 + pcount = &pvar->caub.cElems; + cb = *pcount * sizeof(pvar->caub.pElems[0]); + pv = pvar->caub.pElems; + break; + + case VT_VECTOR | VT_I2: + case VT_VECTOR | VT_UI2: + case VT_VECTOR | VT_BOOL: + AssertShortVector(cai); // VT_I2 + AssertShortVector(caui); // VT_UI2 + AssertShortVector(cabool); // VT_BOOL + pcount = &pvar->cai.cElems; + cb = *pcount * sizeof(pvar->cai.pElems[0]); + pv = pvar->cai.pElems; + + // If swapping, swap as WORDs + CBBYTESWAP(sizeof(pvar->cai.pElems[0])); + break; + + case VT_VECTOR | VT_I4: + case VT_VECTOR | VT_UI4: + case VT_VECTOR | VT_R4: + case VT_VECTOR | VT_ERROR: + AssertLongVector(cal); // VT_I4 + AssertLongVector(caul); // VT_UI4 + AssertLongVector(caflt); // VT_R4 + AssertLongVector(cascode); // VT_ERROR + pcount = &pvar->cal.cElems; + cb = *pcount * sizeof(pvar->cal.pElems[0]); + pv = pvar->cal.pElems; + + // If swapping, swap as DWORDs + CBBYTESWAP(sizeof(pvar->cal.pElems[0])); + break; + + case VT_VECTOR | VT_I8: + case VT_VECTOR | VT_UI8: + case VT_VECTOR | VT_FILETIME: + AssertLongLongVector(cah); // VT_I8 + AssertLongLongVector(cauh); // VT_UI8 + AssertLongLongVector(cafiletime);// VT_FILETIME + pcount = &pvar->cah.cElems; + cb = *pcount * sizeof(pvar->cah.pElems[0]); + pv = pvar->cah.pElems; + + // If swapping, swap as DWORDs + CBBYTESWAP(sizeof(DWORD)); + break; + + case VT_VECTOR | VT_R8: + case VT_VECTOR | VT_CY: + case VT_VECTOR | VT_DATE: + AssertLongLongVector(cadbl); // VT_R8 + AssertLongLongVector(cacy); // VT_CY + AssertLongLongVector(cadate); // VT_DATE + pcount = &pvar->cah.cElems; + cb = *pcount * sizeof(pvar->cadbl.pElems[0]); + pv = pvar->cadbl.pElems; + + // If swapping, swap as LONGLONGs (8 bytes) + CBBYTESWAP(sizeof(pvar->cadbl.pElems[0])); + break; + + + case VT_VECTOR | VT_CLSID: + AssertVarVector(cauuid, sizeof(GUID)); + pcount = &pvar->cauuid.cElems; + cb = *pcount * sizeof(pvar->cauuid.pElems[0]); + pv = pvar->cauuid.pElems; + + // If swapping, special handling is required. + CBBYTESWAP( CBBYTESWAP_UID ); + break; + + case VT_VECTOR | VT_CF: + cbch = sizeof(CLIPDATA); + cbchdiv = sizeof(BYTE); + goto stringvector; + + case VT_VECTOR | VT_BSTR: + case VT_VECTOR | VT_LPSTR: + cbchdiv = cbch = sizeof(BYTE); + goto stringvector; + + case VT_VECTOR | VT_LPWSTR: + cbchdiv = cbch = sizeof(WCHAR); + goto stringvector; + + case VT_VECTOR | VT_VARIANT: + cbch = MAXULONG; +stringvector: + AssertVarVector(caclipdata, sizeof(CLIPDATA)); // VT_CF + AssertStringVector(cabstr); // VT_BSTR + AssertStringVector(calpstr); // VT_LPSTR + AssertStringVector(calpwstr); // VT_LPWSTR + AssertVarVector(capropvar, sizeof(PROPVARIANT));// VT_VARIANT + + pcount = &pvar->calpstr.cElems; + ppv = (VOID **) pvar->calpstr.pElems; + break; + + default: + DebugTrace(0, DEBTRACE_ERROR, ( + "RtlConvertVariantToProperty: unsupported vt=%x\n", + pvar->vt)); + StatusInvalidParameter(pstatus, "RtlConvertVariantToProperty: bad type"); + goto Exit; + + } // switch (pvar->vt) + + // At this point we've analyzed the PropVariant, and stored + // information about it in various local variables. Now we + // can use this information to serialize the propvar. + + // Early exit if this is an illegal type. + + if (fIllegalType) + { + StatusInvalidParameter(pstatus, "RtlConvertVariantToProperty: Illegal VarType"); + goto Exit; + } + + // Set pbdst to point into the serialization buffer, or to + // NULL if there is no such buffer. + + if (pprop == NULL) + { + pbdst = NULL; + } + else + { + pbdst = pprop->rgb; + } + + // Is this a Vector of Strings/Variants/CFs? + if (cbch != 0) + { + // Yes. + + ULONG cElems; + + PROPASSERT(pcount != NULL); + PROPASSERT(*pcount == 0 || ppv != NULL); + PROPASSERT(0 == cbByteSwap); + + // Start calculating the serialized size. Include the sizes + // of the VT & element count. + + cb = sizeof(ULONG) + sizeof(ULONG); + + // Is this a Variant Vector? + if (cbch != MAXULONG) + { + // No. Include each element's length field. + cb += *pcount * sizeof(ULONG); + } + + // Is there room in the caller's buffer for everything + // counted so far? + if (*pcb < cb) + { + // No - we won't serialize the data, but we will continue + // to calculate cb. + pprop = NULL; + } + + // Write the count of vector elements. + if (pprop != NULL) + { + *(ULONG *) pbdst = PropByteSwap((ULONG) *pcount); + pbdst += sizeof(ULONG); + } + + // Walk through the vector and write the elements. + + for (cElems = *pcount; cElems > 0; cElems--) + { + ULONG cbcopy = 0; + + // Switch on the size of the element. + switch (cbch) + { + // + // VT_VARIANT + // + case MAXULONG: + cbcopy = MAXULONG; + + // Perform a recursive serialization + RtlConvertVariantToPropertyNoEH( + (PROPVARIANT *) ppv, + CodePage, + NULL, + &cbcopy, + PID_ILLEGAL, + TRUE, + NULL, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + break; + + // + // VT_CF + // + case sizeof(CLIPDATA): + + // We copy cbSize-sizeof(ulClipFmt) bytes. + + if( ((CLIPDATA *) ppv)->cbSize < sizeof(ULONG) ) + { + StatusInvalidParameter(pstatus, "RtlConvertVariantToProperty: short cbSize on VT_CF"); + goto Exit; + } + else + { + cbcopy = CBPCLIPDATA( *(CLIPDATA*) ppv ); + } + + // But increment cb to to include sizeof(ulClipFmt) + cb += sizeof(ULONG); + break; + + // + // VT_LPWSTR + // + case sizeof(WCHAR): + if (*ppv != NULL) + { + PROPASSERT(IsUnicodeString((WCHAR const *) *ppv, MAXULONG)); + cbcopy = (Prop_wcslen((WCHAR *) *ppv) + 1) * sizeof(WCHAR); + pv = *ppv; + + // If byte-swapping is necessary, swap in units of WCHARs + CBBYTESWAP( sizeof(WCHAR) ); + + } + break; + + // + // VT_LPSTR/VT_BSTR + // + default: + PROPASSERT(cbch == sizeof(BYTE)); + PROPASSERT(pchConvert == NULL); + if (*ppv != NULL) + { + pv = *ppv; + + // Is this a BSTR? + if (pvar->vt == (VT_VECTOR | VT_BSTR)) + { + // Initialize the # bytes to copy. + cbcopy = BSTRLEN(pv); + + // Verify that the BSTR is terminated. + if (((OLECHAR const *) pv)[cbcopy/sizeof(OLECHAR)] != OLESTR('\0')) + { + PROPASSERT(((OLECHAR const *) pv)[cbcopy/sizeof(OLECHAR)] == OLESTR('\0')); + StatusInvalidParameter(pstatus, + "RtlConvertVariantToProperty: bad BSTR array null char"); + goto Exit; + } + + // Also copy the string terminator. + cbcopy += sizeof(OLECHAR); + + // If the propset and the BSTR are in mismatched + // codepages (one's Unicode, the other's Ansi), + // correct the BSTR now. In any case, the correct + // string is in 'pv'. + + if (CodePage != CP_WINUNICODE // Ansi property set + && + OLECHAR_IS_UNICODE) // Unicode BSTR + { + PROPASSERT(IsUnicodeString((WCHAR*)pv, cbcopy)); + + RtlpConvertToMultiByte( + (WCHAR const *) pv, + cbcopy, + CodePage, + &pchConvert, + &cbcopy, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + pv = pchConvert; + } + + else + if (CodePage == CP_WINUNICODE // Unicode property set + && + !OLECHAR_IS_UNICODE) // Ansi BSTRs + { + PROPASSERT(IsAnsiString((CHAR const *) pv, cbcopy)); + + RtlpConvertToUnicode( + (CHAR const *) pv, + cbcopy, + CP_ACP, // In-mem BSTR is in system CP + (WCHAR **) &pchConvert, + &cbcopy, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // The Unicode string must have the proper byte order + CBBYTESWAP( sizeof(WCHAR) ); + + pv = pchConvert; + + } + + else + if (CodePage == CP_WINUNICODE ) + { + // Both the BSTR and the property set are Unicode. + // No conversion is required, but byte-swapping + // is (if byte-swapping is enabled). + + CBBYTESWAP( sizeof(WCHAR) ); + } + + } // if (pvar->vt == (VT_VECTOR | VT_BSTR)) + + // Otherwise it's an LPSTR + else + { + PROPASSERT(IsAnsiString((char const *) pv, MAXULONG)); + PROPASSERT(pvar->vt == (VT_VECTOR | VT_LPSTR)); + cbcopy = strlen((char *) pv) + 1; // + trailing null + + if (CodePage == CP_WINUNICODE) + { + PROPASSERT(IsAnsiString( + (CHAR const *) pv, + cbcopy)); + RtlpConvertToUnicode( + (CHAR const *) pv, + cbcopy, + CP_ACP, + (WCHAR **) &pchConvert, + &cbcopy, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // If byte-swapping, we'll do so with the WCHARs + CBBYTESWAP( sizeof(WCHAR) ); + + pv = pchConvert; + } + } // if (pvar->vt == (VT_VECTOR | VT_BSTR)) ... else + } // if (*ppv != NULL) + + // In the end, pv should be in the codepage of + // the property set. + +#ifdef LITTLEENDIAN + PROPASSERT( NULL == pv + || + CodePage == CP_WINUNICODE && IsUnicodeString((WCHAR*)pv, cbcopy) + || + CodePage != CP_WINUNICODE && IsAnsiString((CHAR*)pv, cbcopy)); +#endif + + break; + + } // switch (cbch) + + // Add the size of this vector element to the property total + cb += DwordAlign(cbcopy); + + // Will there be enough room for this vector element? + + if (*pcb < cb) + { + // No - we'll continue (thus calculating the total size + // necessary), but we won't write to the caller's buffer. + pprop = NULL; + } + + // Is this a vector of Variants? + + if (cbch == MAXULONG) + { + // Yes. Convert this variant. + if (pprop != NULL) + { + RtlConvertVariantToPropertyNoEH( + (PROPVARIANT *) ppv, + CodePage, + (SERIALIZEDPROPERTYVALUE *) pbdst, + &cbcopy, + PID_ILLEGAL, + TRUE, + NULL, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + pbdst += cbcopy; + } + ppv = (VOID **) Add2Ptr(ppv, sizeof(PROPVARIANT)); + } // if (cbch == MAXULONG) + + else + { + // This is a vector of something other than Variants. + + PROPASSERT( + cbch == sizeof(BYTE) || + cbch == sizeof(WCHAR) || + cbch == sizeof(CLIPDATA)); + + PROPASSERT(cbchdiv == sizeof(BYTE) || cbchdiv == sizeof(WCHAR)); + + // Are we writing the serialized property? + if (pprop != NULL) + { + ULONG cbVectElement; + + // Calculate the length of the vector element. + cbVectElement = (ULONG) cbcopy/cbchdiv; + + // Is this a ClipData? + if (cbch == sizeof(CLIPDATA)) + { + // Adjust the length to include sizeof(ulClipFmt) + cbVectElement += sizeof(ULONG); + + // Write the vector element length. + *(ULONG *) pbdst = PropByteSwap( cbVectElement ); + + // Advance pbdst & write the clipboard format. + pbdst += sizeof(ULONG); + *(ULONG *) pbdst = PropByteSwap( ((CLIPDATA *) ppv)->ulClipFmt ); + } + else // This isn't a ClipFormat vector element. + { + // Write the vector element length. + *(ULONG *) pbdst = PropByteSwap( cbVectElement ); + } + + // Advance pbdst & write the property data. + pbdst += sizeof(ULONG); + RtlCopyMemory( + pbdst, + cbch == sizeof(CLIPDATA)? + ((CLIPDATA *) ppv)->pClipData : + pv, + cbcopy); + + // Zero out the pad bytes. + RtlZeroMemory(pbdst + cbcopy, DwordRemain(cbcopy)); + + // If byte-swapping is necessary, do so now. + PBSBuffer( pbdst, DwordAlign(cbcopy), cbByteSwap ); + + // Advance pbdst to the next property. + pbdst += DwordAlign(cbcopy); + + } // if (pprop != NULL) + + // Advance ppv to point into the PropVariant at the + // next element in the array. + + if (cbch == sizeof(CLIPDATA)) + { + ppv = (VOID **) Add2Ptr(ppv, sizeof(CLIPDATA)); + } + else + { + ppv++; + delete [] pchConvert; + pchConvert = NULL; + } + } // if (cbch == MAXULONG) ... else + } // for (cElems = *pcount; cElems > 0; cElems--) + } // if (cbch != 0) // STRING/VARIANT/CF VECTOR property + + else + { + // This isn't a vector, or if it is, the elements + // aren't Strings, Variants, or CFs. + + ULONG cbCopy = cb; + + // Adjust cb (the total serialized buffer size) for + // pre-data. + + if (pvar->vt != VT_EMPTY) + { // Allow for the VT + cb += sizeof(ULONG); + } + if (pcount != NULL) + { // Allow for the count field + cb += sizeof(ULONG); + } + if (pclipfmt != NULL) + { // Allow for the ulClipFmt field. + cb += sizeof(ULONG); + } + + // Is there room in the caller's buffer? + if (*pcb < cb) + { // No - calculate cb but don't write anything. + pprop = NULL; + } + + // 'pv' should point to the source data. If it does, then + // we'll copy it into the property set. If it doesn't but + // it should, then we'll report an error. + + if (pv != NULL || fCheckNullSource) + { + ULONG cbZero = DwordRemain(cbCopy); + + // Do we have a destination (propset) buffer? + + if (pprop != NULL) + { + // Does this property have a count field? + if (pcount != NULL) + { + // Write the count & advance pbdst + *(ULONG *) pbdst = PropByteSwap( *pcount ); + pbdst += sizeof(ULONG); + } + + // Is this a VT_CF? + if (pclipfmt != NULL) + { + // Write the ClipFormat & advance pbdst + *(ULONG *) pbdst = PropByteSwap( (DWORD) *pclipfmt ); + pbdst += sizeof(ULONG); + } + } + + // Are we missing the source data? + if (pv == NULL) + { + // The Source pointer is NULL. If cbCopy != 0, the passed + // VARIANT is not properly formed. + + if (cbCopy != 0) + { + StatusInvalidParameter(pstatus, "RtlConvertVariantToProperty: bad NULL"); + goto Exit; + } + } + else if (pprop != NULL) + { + // We have a non-NULL source & destination. + // First, copy the bytes from the former to the latter. + + RtlCopyMemory(pbdst, pv, cbCopy); + + // Then, if necessary, swap the bytes in the property + // set (leaving the PropVariant bytes untouched). + + PBSBuffer( (VOID*) pbdst, cbCopy, cbByteSwap ); + } + + // Did we write the serialization? + if (pprop != NULL) + { + // Zero the padding bytes. + RtlZeroMemory(pbdst + cbCopy, cbZero); + + // Canonicalize VARIANT_BOOLs. We do this here because + // we don't want to muck with the caller's buffer directly. + + if ((pvar->vt & ~VT_VECTOR) == VT_BOOL) + { + VARIANT_BOOL *pvb = (VARIANT_BOOL *) pbdst; + VARIANT_BOOL *pvbEnd = &pvb[cbCopy/sizeof(*pvb)]; + + while (pvb < pvbEnd) + { + if (*pvb + && + PropByteSwap(*pvb) != VARIANT_TRUE) + { + DebugTrace(0, DEBTRACE_ERROR, ( + "Patching VARIANT_TRUE value: %hx --> %hx\n", + *pvb, + VARIANT_TRUE)); + + *pvb = PropByteSwap( (VARIANT_BOOL) VARIANT_TRUE ); + } + pvb++; + } + } + } // if (pprop != NULL) + } + } // if (cbch != 0) ... else // non - STRING/VARIANT/CF VECTOR property + + // Set the VT in the serialized buffer now that all size + // checks completed. + + if (pprop != NULL && pvar->vt != VT_EMPTY) + { + // When byte-swapping the VT, treat it as a DWORD + // (it's a WORD in the PropVariant, but a DWORD when + // serialized). + + pprop->dwType = PropByteSwap( (DWORD) pvar->vt ); + } + + // Update the caller's copy of the total size. + *pcb = DwordAlign(cb); + +Exit: + + delete [] pchConvert; + return(pprop); + +} + + +//+--------------------------------------------------------------------------- +// Function: RtlConvertPropertyToVariant, private +// +// Synopsis: Convert a SERIALIZEDPROPERTYVALUE to a PROPVARIANT +// +// Arguments: [pprop] -- pointer to SERIALIZEDPROPERTYVALUE +// [PointerDelta] -- adjustment to pointers to get user addresses +// [fConvertNullStrings] -- map NULL strings to empty strings +// [CodePage] -- property set codepage +// [pvar] -- pointer to PROPVARIANT +// [pma] -- caller's memory allocation routine +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: TRUE if property is an indirect property type +//--------------------------------------------------------------------------- + +#ifdef KERNEL +#define ADJUSTPOINTER(ptr, delta, type) (ptr) = (type) Add2Ptr((ptr), (delta)) +#else +#define ADJUSTPOINTER(ptr, delta, type) +#endif + +// First, define a wrapper for this function which returns errors +// using NT Exception Handling, rather than returning an NTSTATUS. + +#if defined(WINNT) && !defined(IPROPERTY_DLL) + +BOOLEAN +RtlConvertPropertyToVariant( + IN SERIALIZEDPROPERTYVALUE const *pprop, + IN USHORT CodePage, + OUT PROPVARIANT *pvar, + IN PMemoryAllocator *pma) +{ + BOOLEAN boolRet; + NTSTATUS status; + + boolRet = RtlConvertPropertyToVariantNoEH( + pprop, CodePage, pvar, + pma, &status ); + + if (!NT_SUCCESS( status )) + RtlRaiseStatus( status ); + + return (boolRet); + +} + +#endif // #if defined(WINNT) && !defined(IPROPERTY_DLL) + + +// Now define the body of the function, returning errors with an +// NTSTATUS value instead of raising. + +BOOLEAN +RtlConvertPropertyToVariantNoEH( + IN SERIALIZEDPROPERTYVALUE const *pprop, + IN USHORT CodePage, + OUT PROPVARIANT *pvar, + IN PMemoryAllocator *pma, + OUT NTSTATUS *pstatus) +{ + *pstatus = STATUS_SUCCESS; + + // ------ + // Locals + // ------ + + BOOLEAN fIndirect = FALSE; + + // Buffers which must be freed before exiting. + CHAR *pchConvert = NULL, *pchByteSwap = NULL; + + VOID **ppv = NULL; + VOID *pv = NULL; + ULONG cbskip = sizeof(ULONG); + ULONG cb = 0; + + // Size of byte-swapping units (must be signed). + INT cbByteSwap = 0; + + BOOLEAN fPostAllocInit = FALSE; + BOOLEAN fNullLegal = (BOOLEAN) ( (PropByteSwap(pprop->dwType) & VT_VECTOR) != 0 ); + const BOOLEAN fConvertToEmpty = FALSE; + + // --------------------------------------------------------- + // Based on the VT, calculate ppv, pv, cbskip, + // cb, fPostAllocInit, fNullLegal, & fConvertToEmpty + // --------------------------------------------------------- + + // Set the VT in the PropVariant. Note that in 'pprop' it's a + // DWORD, but it's a WORD in 'pvar'. + + pvar->vt = (VARTYPE) PropByteSwap(pprop->dwType); + + switch (pvar->vt) + { + case VT_EMPTY: + case VT_NULL: + break; + +#ifdef PROPVAR_VT_I1 + case VT_I1: + AssertByteField(cVal); // VT_I1 +#endif + case VT_UI1: + AssertByteField(bVal); // VT_UI1 + cb = sizeof(pvar->bVal); + pv = &pvar->bVal; + break; + + case VT_I2: + case VT_UI2: + case VT_BOOL: + AssertShortField(iVal); // VT_I2 + AssertShortField(uiVal); // VT_UI2 + AssertShortField(boolVal); // VT_BOOL + cb = sizeof(pvar->iVal); + pv = &pvar->iVal; + + // If swapping, swap as a WORD + CBBYTESWAP(cb); + break; + + case VT_I4: + case VT_UI4: + case VT_R4: + case VT_ERROR: + AssertLongField(lVal); // VT_I4 + AssertLongField(ulVal); // VT_UI4 + AssertLongField(fltVal); // VT_R4 + AssertLongField(scode); // VT_ERROR + cb = sizeof(pvar->lVal); + pv = &pvar->lVal; + + // If swapping, swap as a DWORD + CBBYTESWAP(cb); + break; + + case VT_I8: + case VT_UI8: + case VT_FILETIME: + AssertLongLongField(hVal); // VT_I8 + AssertLongLongField(uhVal); // VT_UI8 + AssertLongLongField(filetime); // VT_FILETIME + cb = sizeof(pvar->hVal); + pv = &pvar->hVal; + + // If swapping, swap as a pair of DWORDs + CBBYTESWAP(sizeof(DWORD)); + break; + + case VT_R8: + case VT_CY: + case VT_DATE: + AssertLongLongField(dblVal); // VT_R8 + AssertLongLongField(cyVal); // VT_CY + AssertLongLongField(date); // VT_DATE + cb = sizeof(pvar->dblVal); + pv = &pvar->dblVal; + + // If swapping, swap as a LONGLONG + CBBYTESWAP(cb); + break; + + case VT_CLSID: + AssertStringField(puuid); // VT_CLSID + cb = sizeof(GUID); + ppv = (VOID **) &pvar->puuid; + cbskip = 0; + + // If swapping, special handling is required + CBBYTESWAP( CBBYTESWAP_UID ); + break; + + case VT_CF: + + // Allocate a CLIPDATA buffer + pvar->pclipdata = (CLIPDATA *) pma->Allocate(sizeof(CLIPDATA)); + if (pvar->pclipdata == NULL) + { + StatusKBufferOverflow(pstatus, "RtlConvertPropertyToVariant: no memory for CF"); + goto Exit; + } + + // Set the size (includes sizeof(ulClipFmt)) + pvar->pclipdata->cbSize = PropByteSwap( ((CLIPDATA *) pprop->rgb)->cbSize ); + if( pvar->pclipdata->cbSize < sizeof(pvar->pclipdata->ulClipFmt) ) + { + StatusError(pstatus, "RtlConvertPropertyToVariant: Invalid VT_CF cbSize", + STATUS_INTERNAL_DB_CORRUPTION); + goto Exit; + } + + // Set the # bytes-to-copy. We can't use the CBPCLIPDATA macro + // here because it assumes that the CLIPDATA parameter is correctly + // byte-swapped. + cb = PropByteSwap( *(DWORD*) pprop->rgb ) - sizeof(pvar->pclipdata->ulClipFmt); + + // Set the ClipFormat itself. + pvar->pclipdata->ulClipFmt = PropByteSwap( ((CLIPDATA *) pprop->rgb)->ulClipFmt ); + + // Prepare for the alloc & copy. Put the buffer pointer + // in pClipData, & skip the ulClipFmt in the copy. + ppv = (VOID **) &pvar->pclipdata->pClipData; + cbskip += sizeof(ULONG); + + // It's legal for cb to be 0. + fNullLegal = TRUE; + + // Adjust to the user-mode pointer (Kernel only) + ADJUSTPOINTER(pvar->pclipdata, PointerDelta, CLIPDATA *); + + break; + + case VT_BLOB: + case VT_BLOB_OBJECT: + cb = pvar->blob.cbSize = PropByteSwap( *(ULONG *) pprop->rgb ); + ppv = (VOID **) &pvar->blob.pBlobData; + fNullLegal = TRUE; + break; + + case VT_STREAM: + case VT_STREAMED_OBJECT: + case VT_STORAGE: + case VT_STORED_OBJECT: + fIndirect = TRUE; + goto lpstr; + + case VT_BSTR: + case VT_LPSTR: +lpstr: + AssertStringField(pszVal); // VT_STREAM, VT_STREAMED_OBJECT + AssertStringField(pszVal); // VT_STORAGE, VT_STORED_OBJECT + AssertStringField(bstrVal); // VT_BSTR + AssertStringField(pszVal); // VT_LPSTR + + // [length field] bytes should be allocated + cb = PropByteSwap( *(ULONG *) pprop->rgb ); + + // When a buffer is allocated, it's pointer will go + // in *ppv. + ppv = (VOID **) &pvar->pszVal; + + // Is this a non-empty string? + if (cb != 0) + { + // Is the serialized value one that should be + // an Ansi string in the PropVariant? + + if (pvar->vt == VT_LPSTR // It's an LPSTR (always Ansi), or + || + !OLECHAR_IS_UNICODE ) // PropVariant strings are Ansi + { + // If the propset is Unicode, we must do a + // conversion to Ansi. + + if (CodePage == CP_WINUNICODE) + { + WCHAR *pwsz = (WCHAR *) Add2ConstPtr(pprop->rgb, sizeof(ULONG)); + + // If necessary, swap the WCHARs. 'pwsz' will point to + // the correct (system-endian) string either way. If an + // alloc is necessary, 'pchByteSwap' will point to the new + // buffer. + + PBSInPlaceAlloc( &pwsz, (WCHAR**) &pchByteSwap, pstatus ); + if( !NT_SUCCESS( *pstatus )) goto Exit; + PROPASSERT(IsUnicodeString( pwsz, cb)); + + // Convert the properly-byte-ordered string in 'pwsz' + // into MBCS, putting the result in pchConvert. + + RtlpConvertToMultiByte( + pwsz, + cb, + CP_ACP, // Use the system default codepage + &pchConvert, + &cb, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + } + } // if (pvar->vt == VT_LPSTR) ... + + // Otherwise, even though this string may be + // Ansi in the Property Set, it must be Unicode + // in the PropVariant. + + else + { + // If necessary, convert to Unicode + + if (CodePage != CP_WINUNICODE) + { + PROPASSERT( + IsAnsiString( + (CHAR const *) + Add2ConstPtr(pprop->rgb, sizeof(ULONG)), + cb)); + + RtlpConvertToUnicode( + (CHAR const *) + Add2ConstPtr(pprop->rgb, sizeof(ULONG)), + cb, + CodePage, + (WCHAR **) &pchConvert, + &cb, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + } // if (CodePage != CP_WINUNICODE) + else + { + // The value is Unicode both the property set + // and the PropVariant. If byte-swapping is + // necessary, we'll do so in units of WCHARs. + + CBBYTESWAP( sizeof(WCHAR) ); + } + + } // if (pvar->vt == VT_LPSTR) ... else + + // If this is a BSTR property, verify that it is terminated + // appropriately. + + if (VT_BSTR == pvar->vt) + { + BSTR bstr = ( NULL == pchConvert ) + ? (BSTR) Add2ConstPtr(pprop->rgb, sizeof(ULONG)) + : (BSTR) pchConvert; + + // On little-endian machines, validate the string. +#ifdef LITTLEENDIAN + PROPASSERT( IsOLECHARString( bstr, MAXULONG )); +#endif + + // Validate the bstr. Note that even though this bstr may + // be byte-swapped, this 'if' block still works because + // ByteSwap('\0') == ('\0'). + + PROPASSERT( PropByteSwap( (OLECHAR) OLESTR('\0') ) + == + (OLECHAR) OLESTR('\0') ); + + if( (cb & (sizeof(OLECHAR) - 1)) != 0 + && + OLECHAR_IS_UNICODE + || + bstr[cb/sizeof(OLECHAR) - 1] != OLESTR('\0') ) + { + StatusError(pstatus, "RtlConvertPropertyToVariant: Invalid BSTR Property", + STATUS_INTERNAL_DB_CORRUPTION); + goto Exit; + } + } // if (VT_BSTR == pvar->vt) + } // if (cb != 0) + + fNullLegal = TRUE; + break; + + case VT_LPWSTR: + fNullLegal = TRUE; + AssertStringField(pwszVal); // VT_LPWSTR + cb = PropByteSwap( *(ULONG *) pprop->rgb ) * sizeof(WCHAR); + ppv = (VOID **) &pvar->pwszVal; + + // If byte-swapping will be necessary, do so for the WCHARs + CBBYTESWAP( sizeof(WCHAR) ); + + break; + +#ifdef PROPVAR_VT_I1 + case VT_VECTOR | VT_I1: + AssertByteVector(cac); // VT_I1 +#endif + case VT_VECTOR | VT_UI1: + AssertByteVector(caub); // VT_UI1 + pvar->caub.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); + cb = pvar->caub.cElems * sizeof(pvar->caub.pElems[0]); + ppv = (VOID **) &pvar->caub.pElems; + break; + + case VT_VECTOR | VT_I2: + case VT_VECTOR | VT_UI2: + case VT_VECTOR | VT_BOOL: + AssertShortVector(cai); // VT_I2 + AssertShortVector(caui); // VT_UI2 + AssertShortVector(cabool); // VT_BOOL + pvar->cai.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); + cb = pvar->cai.cElems * sizeof(pvar->cai.pElems[0]); + ppv = (VOID **) &pvar->cai.pElems; + + // If swapping, swap as a WORD + CBBYTESWAP(sizeof(pvar->cai.pElems[0])); + break; + + case VT_VECTOR | VT_I4: + case VT_VECTOR | VT_UI4: + case VT_VECTOR | VT_R4: + case VT_VECTOR | VT_ERROR: + AssertLongVector(cal); // VT_I4 + AssertLongVector(caul); // VT_UI4 + AssertLongVector(caflt); // VT_R4 + AssertLongVector(cascode); // VT_ERROR + pvar->cal.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); + cb = pvar->cal.cElems * sizeof(pvar->cal.pElems[0]); + ppv = (VOID **) &pvar->cal.pElems; + + // If byte swapping, swap as DWORDs + CBBYTESWAP(sizeof(pvar->cal.pElems[0])); + break; + + case VT_VECTOR | VT_I8: + case VT_VECTOR | VT_UI8: + case VT_VECTOR | VT_FILETIME: + AssertLongLongVector(cah); // VT_I8 + AssertLongLongVector(cauh); // VT_UI8 + AssertLongLongVector(cafiletime); // VT_FILETIME + pvar->cah.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); + cb = pvar->cah.cElems * sizeof(pvar->cah.pElems[0]); + ppv = (VOID **) &pvar->cah.pElems; + + // If byte swapping, swap as DWORDs + CBBYTESWAP(sizeof(DWORD)); + break; + + case VT_VECTOR | VT_R8: + case VT_VECTOR | VT_CY: + case VT_VECTOR | VT_DATE: + AssertLongLongVector(cadbl); // VT_R8 + AssertLongLongVector(cacy); // VT_CY + AssertLongLongVector(cadate); // VT_DATE + pvar->cadbl.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); + cb = pvar->cadbl.cElems * sizeof(pvar->cadbl.pElems[0]); + ppv = (VOID **) &pvar->cadbl.pElems; + + // If byte swapping, swap as LONGLONGs + CBBYTESWAP(sizeof(pvar->cadbl.pElems[0])); + break; + + + case VT_VECTOR | VT_CLSID: + AssertVarVector(cauuid, sizeof(GUID)); + pvar->cauuid.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); + cb = pvar->cauuid.cElems * sizeof(pvar->cauuid.pElems[0]); + ppv = (VOID **) &pvar->cauuid.pElems; + + // If byte swapping, special handling is required. + CBBYTESWAP( CBBYTESWAP_UID ); + break; + + case VT_VECTOR | VT_CF: + + // Set the count of clipdatas + pvar->caclipdata.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); + + // How much should we allocate for caclipdata.pElems, & where + // should that buffer pointer go? + cb = pvar->caclipdata.cElems * sizeof(pvar->caclipdata.pElems[0]); + ppv = (VOID **) &pvar->caclipdata.pElems; + + // We need to do work after pElems is allocated. + fPostAllocInit = TRUE; + break; + + case VT_VECTOR | VT_BSTR: + case VT_VECTOR | VT_LPSTR: + AssertStringVector(cabstr); // VT_BSTR + AssertStringVector(calpstr); // VT_LPSTR + + // Put the element count in the PropVar + pvar->calpstr.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); + + // An array of cElems pointers should be alloced + cb = pvar->calpstr.cElems * sizeof(CHAR*); + + // Show where the array of pointers should go. + ppv = (VOID **) &pvar->calpstr.pElems; + + // Additional allocs will be necessary after the vector + // is alloced. + fPostAllocInit = TRUE; + + break; + + case VT_VECTOR | VT_LPWSTR: + AssertStringVector(calpwstr); // VT_LPWSTR + pvar->calpwstr.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); + cb = pvar->calpwstr.cElems * sizeof(ULONG); + ppv = (VOID **) &pvar->calpwstr.pElems; + fPostAllocInit = TRUE; + break; + + case VT_VECTOR | VT_VARIANT: + AssertVariantVector(capropvar); // VT_VARIANT + pvar->capropvar.cElems = PropByteSwap( *(ULONG *) pprop->rgb ); + cb = pvar->capropvar.cElems * sizeof(PROPVARIANT); + ppv = (VOID **) &pvar->capropvar.pElems; + fPostAllocInit = TRUE; + break; + + default: + DebugTrace(0, DEBTRACE_ERROR, ( + "RtlConvertPropertyToVariant: unsupported vt=%x\n", + pvar->vt)); + StatusInvalidParameter(pstatus, "RtlConvertPropertyToVariant: bad type"); + goto Exit; + + } // switch (pvar->vt) + + // ------------------------------------------------------ + // We've now analyzed the serialized property and learned + // about it, now we can put it into the PropVariant. + // ------------------------------------------------------ + + // Is this a simple, unaligned scalar? + + if (pv != NULL) + { + // Yes. All we need to do is copy some bytes. + PROPASSERT(pchConvert == NULL); + RtlCopyMemory(pv, pprop->rgb, cb); + + // We also might need to byte-swap them (but only in the PropVar). + PBSBuffer( pv, cb, cbByteSwap ); + } + + // Otherwise, we need to allocate memory, to which the + // PropVariant will point. + + else if (ppv != NULL) + { + *ppv = NULL; + + if (!fConvertToEmpty && cb == 0) // Kernel only + { + if (!fNullLegal) + { + StatusInvalidParameter(pstatus, "RtlConvertPropertyToVariant: bad NULL"); + goto Exit; + } + } + + else + { + + PROPASSERT(cb != 0 || fConvertToEmpty); + + // Allocate the necessary buffer (which we figured out in the + // switch above). For vector properties, + // this will just be the pElems buffer at this point. + // For singleton BSTR properties, we'll skip this allocate + // altogether; they're allocated with SysStringAlloc. + + if( VT_BSTR != pvar->vt ) + { + *ppv = pma->Allocate(max(1, cb)); + if (*ppv == NULL) + { + StatusKBufferOverflow(pstatus, "RtlConvertPropertyToVariant: no memory"); + goto Exit; + } + } + + // Can we load the PropVariant with a simple copy? + if (!fPostAllocInit) + { + // Yes - all we need is a copy (and an implicit + // alloc for BSTRs). + + if (VT_BSTR == pvar->vt) + { + // We do the copy with the OleAutomation routine + // (which does an allocation too). + // If byte-swapping is necessary, the switch block + // already took care of it, leaving the buffer in + // 'pchConvert'. + + PROPASSERT( NULL == *ppv ); + *ppv = (*UnicodeCallouts.pfnSysAllocString)( + ( pchConvert != NULL ) + ? (OLECHAR *) pchConvert + : (OLECHAR *) (pprop->rgb + cbskip) ); + if (*ppv == NULL) + { + StatusKBufferOverflow(pstatus, "RtlConvertPropertyToVariant: no memory"); + goto Exit; + } + } + else + { + // Copy the property into the PropVariant. + RtlCopyMemory( + *ppv, + pchConvert != NULL? + (BYTE const *) pchConvert : pprop->rgb + cbskip, + cb); + + } + + // If necessary, byte-swap the property (only in the PropVar). + PBSBuffer( *ppv, cb, cbByteSwap ); + + } // if (!fPostAllocInit) + + else + { + // We must do more than just a copy. + // (Thus this is a vector of strings, variants, or CFs). + + ULONG cElems = pvar->calpstr.cElems; + + // Initialize the source pointer to point just beyond + // the element count. + + BYTE const *pbsrc = pprop->rgb + sizeof(ULONG); + + // Zero all pointers in the pElems array for easy caller cleanup + ppv = (VOID **) *ppv; + RtlZeroMemory(ppv, cb); + + // Handle Variants, ClipFormats, & Strings separately. + + if (pvar->vt == (VT_VECTOR | VT_VARIANT)) + { + PROPVARIANT *pvarT = (PROPVARIANT *) ppv; + + PROPASSERT(!fIndirect); + while (cElems-- > 0) + { + ULONG cbelement; + + fIndirect = RtlConvertPropertyToVariantNoEH( + (SERIALIZEDPROPERTYVALUE const *) pbsrc, + CodePage, + pvarT, + pma, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + PROPASSERT(!fIndirect); + + cbelement = PropertyLengthNoEH( + (SERIALIZEDPROPERTYVALUE const *) pbsrc, + MAXULONG, + CPSS_VARIANTVECTOR, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + pbsrc += cbelement; + pvarT++; + } + } // if (pvar->vt == (VT_VECTOR | VT_VARIANT)) + + else if (pvar->vt == (VT_VECTOR | VT_CF)) + { + // Set pcd to &pElems[0] + CLIPDATA *pcd = (CLIPDATA *) ppv; + + // Loop through pElems + while (cElems-- > 0) + { + // What is the size of the clipdata (including sizeof(ulClipFmt))? + pcd->cbSize = PropByteSwap( ((CLIPDATA *) pbsrc)->cbSize ); + if( pcd->cbSize < sizeof(pcd->ulClipFmt) ) + { + StatusError(pstatus, "RtlConvertPropertyToVariant: Invalid VT_CF cbSize", + STATUS_INTERNAL_DB_CORRUPTION); + goto Exit; + } + + // How many bytes should we copy to pClipData? + cb = CBPCLIPDATA( *pcd ); + + // Set the ClipFormat & advance pbsrc to the clipdata. + pcd->ulClipFmt = PropByteSwap( ((CLIPDATA *) pbsrc)->ulClipFmt ); + pbsrc += 2 * sizeof(ULONG); + + // Copy the ClipData into the PropVariant + + pcd->pClipData = NULL; + if (cb > 0) + { + // Get a buffer for the clip data. + pcd->pClipData = (BYTE *) pma->Allocate(cb); + if (pcd->pClipData == NULL) + { + StatusKBufferOverflow(pstatus, "RtlConvertPropertyToVariant: no memory for CF[]"); + goto Exit; + } + + // Copy the clipdata into pElems[i].pClipData + RtlCopyMemory(pcd->pClipData, pbsrc, cb); + ADJUSTPOINTER(pcd->pClipData, PointerDelta, BYTE *); + + } // if (cb > 0) + + // Move pcd to &pElems[i+1], and advance the buffer pointer. + pcd++; + pbsrc += DwordAlign(cb); + + } // while (cElems-- > 0) + } // else if (pvar->vt == (VT_VECTOR | VT_CF)) + + else // This is a vector of some kind of string. + { + // Assume that characters are CHARs + ULONG cbch = sizeof(char); + + if (pvar->vt == (VT_VECTOR | VT_LPWSTR)) + { + // Characters are WCHARs + cbch = sizeof(WCHAR); + + // If byte-swapping is enabled, LPWSTRs must have + // their WCHARs swapped. + CBBYTESWAP( sizeof(WCHAR) ); + } + + while (cElems-- > 0) + { + ULONG cbcopy; + + cbcopy = cb = PropByteSwap( *((ULONG *) pbsrc) ) * cbch; + pbsrc += sizeof(ULONG); + pv = (VOID *) pbsrc; + PROPASSERT(*ppv == NULL); + PROPASSERT(pchConvert == NULL); + + if (fConvertToEmpty || cb != 0) + { + // Do we have actual data to work with? + if (cb != 0) + { + // Special BSTR pre-processing ... + if (pvar->vt == (VT_VECTOR | VT_BSTR)) + { + // If the propset & in-memory BSTRs are of + // different Unicode-ness, convert now. + + if (CodePage != CP_WINUNICODE // Ansi PropSet + && + OLECHAR_IS_UNICODE ) // Unicode BSTRs + { + PROPASSERT(IsAnsiString((CHAR*) pv, cb)); + RtlpConvertToUnicode( + (CHAR const *) pv, + cb, + CodePage, + (WCHAR **) &pchConvert, + &cbcopy, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + pv = pchConvert; + } + + else + if (CodePage == CP_WINUNICODE // Unicode PropSet + && + !OLECHAR_IS_UNICODE ) // Ansi BSTRs + { + // If byte-swapping is necessary, the string from + // the propset must be swapped before it can be + // converted to MBCS. If such a conversion + // is necessary, a new buffer is alloced and + // put in pchByteSwap. Either way, 'pv' points + // to the correct string. + + PBSInPlaceAlloc( (WCHAR**) &pv, + (WCHAR**) &pchByteSwap, + pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + PROPASSERT(IsUnicodeString((WCHAR*)pv, cb)); + + // Convert the Unicode string from the property + // set to Ansi. + + RtlpConvertToMultiByte( + (WCHAR const *) pv, + cb, + CP_ACP, // Use the system default codepage + &pchConvert, + &cbcopy, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + // 'pv' always has the correct string. + pv = pchConvert; + } + else + if (CodePage == CP_WINUNICODE) + { + // Both the BSTR is unicode in the property set, + // and must remain unicode in the PropVariant. + // But byte-swapping may still be necessary. + + CBBYTESWAP( sizeof(WCHAR) ); + } + + +#ifdef LITTLEENDIAN + PROPASSERT( IsOLECHARString((BSTR)pv, cbcopy )); +#endif + + // Verify that the BSTR is valid. + if( (cbcopy & (sizeof(OLECHAR)-1)) != 0 + && + OLECHAR_IS_UNICODE + || + ((OLECHAR const *) pv)[cbcopy/sizeof(OLECHAR) - 1] != OLESTR('\0') ) + { + StatusError(pstatus, "RtlConvertPropertyToVariant: Invalid BSTR element", + STATUS_INTERNAL_DB_CORRUPTION); + goto Exit; + } + + } // if (pvar->vt == (VT_VECTOR | VT_BSTR)) + + // Special LPSTR pre-processing + else if (pvar->vt == (VT_VECTOR | VT_LPSTR)) + { + // LPSTRs are always Ansi. If the string + // is Unicode in the propset, convert now. + + if (CodePage == CP_WINUNICODE) + { + // If byte-swapping is necessary, the string from + // the propset must be swapped before it can be + // converted to MBCS. If such a conversion + // is necessary, a new buffer is alloced and + // put in pchByteSwap. Either way, 'pv' points + // to the correct string. + + PBSInPlaceAlloc( (WCHAR**) &pv, (WCHAR**) &pchByteSwap, + pstatus ); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + PROPASSERT(IsUnicodeString((WCHAR*)pv, cb)); + + // Convert to Ansi. + RtlpConvertToMultiByte( + (WCHAR const *) pv, + cb, + CP_ACP, // Use the system default codepage + &pchConvert, + &cbcopy, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + pv = pchConvert; + } + + PROPASSERT( IsAnsiString( (CHAR const *)pv, cbcopy )); + } // else if (pvar->vt == (VT_VECTOR | VT_LPSTR)) + } // if (cb != 0) + + + // Allocate memory in the PropVariant and copy + // the string. + + if( (VT_BSTR | VT_VECTOR) == pvar->vt ) + { + // For BSTRs, the allocate/copy is performed + // by SysStringAlloc. + + *ppv = (*UnicodeCallouts.pfnSysAllocString)( (BSTR) pv ); + if (*ppv == NULL) + { + StatusKBufferOverflow(pstatus, "RtlConvertPropertyToVariant: no memory for BSTR element"); + goto Exit; + } + + // The BSTR length should be the property length + // minus the NULL. + PROPASSERT( BSTRLEN(*ppv) == cbcopy - sizeof(OLECHAR) ); + + } // if( VT_BSTR == pvar->vt ) + + else + { + // Allocate a buffer in the PropVariant + *ppv = pma->Allocate(max(1, cbcopy)); + if (*ppv == NULL) + { + StatusKBufferOverflow(pstatus, "RtlConvertPropertyToVariant: no memory for string element"); + goto Exit; + } + + // Copy from the propset buffer to the PropVariant + RtlCopyMemory(*ppv, pv, cbcopy); + + } // if( VT_BSTR == pvar->vt ) ... else + + // If necessary, byte-swap in the PropVariant to get + // the proper byte-ordering. + PBSBuffer( *ppv, cbcopy, cbByteSwap ); + + // Adjust the PropVar element ptr to user-space (kernel only) + ADJUSTPOINTER(*ppv, PointerDelta, VOID *); + + // Move, within the propset buffer, to the + // next element in the vector. + pbsrc += DwordAlign(cb); + + // Delete the temporary buffers + + delete[] pchByteSwap; + pchByteSwap = NULL; + + delete [] pchConvert; + pchConvert = NULL; + + } // if (fConvertToEmpty || cb != 0) + + // Move, within the PropVariant, to the next + // element in the vector. + ppv++; + + } // while (cElems-- > 0) + } // else if (pvar->vt == (VT_VECTOR | VT_CF)) ... else + } // if (!fPostAllocInit) ... else + + ADJUSTPOINTER(*ppvK, PointerDelta, VOID *); + + } // if (!fConvertToEmpty && cb == 0) ... else + } // else if (ppv != NULL) + +Exit: + + delete[] pchByteSwap; + delete [] pchConvert; + + return(fIndirect); +} + + +//+--------------------------------------------------------------------------- +// Function: CleanupVariants, private +// +// Synopsis: Free all memory used by an array of PROPVARIANT +// +// Arguments: [pvar] -- pointer to PROPVARIANT +// [cprop] -- property count +// [pma] -- caller's memory free routine +// +// Returns: None +//--------------------------------------------------------------------------- + +#ifndef KERNEL +VOID +CleanupVariants( + IN PROPVARIANT *pvar, + IN ULONG cprop, + IN PMemoryAllocator *pma) +{ + while (cprop-- > 0) + { + VOID *pv = NULL; + VOID **ppv = NULL; +#ifdef KERNEL + ULONG cbbstr = 0; +#endif + ULONG cElems; + + switch (pvar->vt) + { + case VT_CF: + pv = pvar->pclipdata; + if (pv != NULL && pvar->pclipdata->pClipData) + { + pma->Free(pvar->pclipdata->pClipData); + } + break; + + case VT_BLOB: + case VT_BLOB_OBJECT: + pv = pvar->blob.pBlobData; + break; + + case VT_BSTR: +#ifdef KERNEL + cbbstr = sizeof(ULONG); + //FALLTHROUGH +#endif + + case VT_CLSID: + case VT_STREAM: + case VT_STREAMED_OBJECT: + case VT_STORAGE: + case VT_STORED_OBJECT: + case VT_LPSTR: + case VT_LPWSTR: + AssertStringField(puuid); // VT_CLSID + AssertStringField(pszVal); // VT_STREAM, VT_STREAMED_OBJECT + AssertStringField(pszVal); // VT_STORAGE, VT_STORED_OBJECT + AssertStringField(bstrVal); // VT_BSTR + AssertStringField(pszVal); // VT_LPSTR + AssertStringField(pwszVal); // VT_LPWSTR + pv = pvar->pszVal; + break; + + // Vector properties: + +#ifdef PROPVAR_VT_I1 + case VT_VECTOR | VT_I1: + AssertByteVector(cac); // VT_I1 +#endif + case VT_VECTOR | VT_UI1: + case VT_VECTOR | VT_I2: + case VT_VECTOR | VT_UI2: + case VT_VECTOR | VT_BOOL: + case VT_VECTOR | VT_I4: + case VT_VECTOR | VT_UI4: + case VT_VECTOR | VT_R4: + case VT_VECTOR | VT_ERROR: + 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: + case VT_VECTOR | VT_CLSID: + AssertByteVector(caub); // VT_UI1 + AssertShortVector(cai); // VT_I2 + AssertShortVector(caui); // VT_UI2 + AssertShortVector(cabool); // VT_BOOL + AssertLongVector(cal); // VT_I4 + AssertLongVector(caul); // VT_UI4 + AssertLongVector(caflt); // VT_R4 + AssertLongVector(cascode); // VT_ERROR + AssertLongLongVector(cah); // VT_I8 + AssertLongLongVector(cauh); // VT_UI8 + AssertLongLongVector(cadbl); // VT_R8 + AssertLongLongVector(cacy); // VT_CY + AssertLongLongVector(cadate); // VT_DATE + AssertLongLongVector(cafiletime); // VT_FILETIME + AssertVarVector(cauuid, sizeof(GUID)); // VT_CLSID + pv = pvar->cai.pElems; + break; + + case VT_VECTOR | VT_CF: + { + CLIPDATA *pcd; + + cElems = pvar->caclipdata.cElems; + pv = pcd = pvar->caclipdata.pElems; + while (cElems-- > 0) + { + if (pcd->pClipData != NULL) + { + pma->Free(pcd->pClipData); + } + pcd++; + } + } + break; + + case VT_VECTOR | VT_BSTR: +#ifdef KERNEL + cbbstr = sizeof(ULONG); + //FALLTHROUGH +#endif + + case VT_VECTOR | VT_LPSTR: + case VT_VECTOR | VT_LPWSTR: + AssertStringVector(cabstr); // VT_BSTR + AssertStringVector(calpstr); // VT_LPSTR + AssertStringVector(calpwstr); // VT_LPWSTR + cElems = pvar->calpstr.cElems; + ppv = (VOID **) pvar->calpstr.pElems; + break; + + case VT_VECTOR | VT_VARIANT: + CleanupVariants( + pvar->capropvar.pElems, + pvar->capropvar.cElems, + pma); + pv = pvar->capropvar.pElems; + break; + + } // switch (pvar->vt) + + if (ppv != NULL) // STRING VECTOR property + { + // Save the vector of pointers + pv = (VOID *) ppv; + + // Free the vector elements + while (cElems-- > 0) + { + if (*ppv != NULL) + { +#ifdef KERNEL + pma->Free((BYTE *) *ppv - cbbstr); +#else + if( (VT_BSTR | VT_VECTOR) == pvar->vt ) + { + (*UnicodeCallouts.pfnSysFreeString)( (BSTR) *ppv ); + } + else + { + pma->Free((BYTE *) *ppv); + } +#endif + } + ppv++; + } + + // Free the vector of pointers. + pma->Free(pv); + pv = NULL; + + } // if (ppv != NULL) + + if (pv != NULL) + { +#ifdef KERNEL + pma->Free((BYTE *) pv - cbbstr); +#else + if( VT_BSTR == pvar->vt ) + { + (*UnicodeCallouts.pfnSysFreeString)( (BSTR) pv ); + } + else + { + pma->Free((BYTE *) pv); + } +#endif + } + + pvar->vt = VT_EMPTY; + + // Move on to the next PropVar in the vector. + pvar++; + + } // while (cprop-- > 0) +} +#endif // !KERNEL + + +//+-------------------------------------------------------------------------- +// Function: PropertyLength +// +// Synopsis: compute the length of a property including the variant type +// +// Arguments: [pprop] -- property value +// [cbbuf] -- max length of accessible memory at pprop +// [flags] -- CPropertySetStream flags +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: length of property +//--------------------------------------------------------------------------- + + +// First, define a wrapper for this function which returns errors +// using NT Exception Handling, rather than returning an NTSTATUS. + +#if defined(WINNT) && !defined(IPROPERTY_DLL) + +ULONG +PropertyLength( + SERIALIZEDPROPERTYVALUE const *pprop, + ULONG cbbuf, + BYTE flags) +{ + NTSTATUS status; + ULONG ulRet; + + ulRet = PropertyLengthNoEH( pprop, cbbuf, flags, &status ); + + if (!NT_SUCCESS( status )) + RtlRaiseStatus( status ); + + return( ulRet ); +} + +#endif // #if defined(WINNT) && !defined(IPROPERTY_DLL) + + +// Now define the body of the function, returning errors with an +// NTSTATUS value instead of raising. + +ULONG +PropertyLengthNoEH( + SERIALIZEDPROPERTYVALUE const *pprop, + ULONG cbbuf, + BYTE flags, + OUT NTSTATUS *pstatus) +{ + ULONG const *pl = (ULONG const *) pprop->rgb; + ULONG cElems = 1; + ULONG cbremain = cbbuf; + ULONG cb = 0, cbch; + BOOLEAN fIllegalType = FALSE; + + *pstatus = STATUS_SUCCESS; + + if (cbremain < CB_SERIALIZEDPROPERTYVALUE) + { + StatusOverflow(pstatus, "PropertyLength: dwType"); + goto Exit; + } + cbremain -= CB_SERIALIZEDPROPERTYVALUE; + if( PropByteSwap(pprop->dwType) & VT_VECTOR ) + { + if (cbremain < sizeof(ULONG)) + { + StatusOverflow(pstatus, "PropertyLength: cElems"); + goto Exit; + } + cbremain -= sizeof(ULONG); + cElems = PropByteSwap( *pl++ ); + } + if( PropByteSwap(pprop->dwType) == (VT_VECTOR | VT_VARIANT) ) + { + while (cElems-- > 0) + { + cb = PropertyLengthNoEH( + (SERIALIZEDPROPERTYVALUE const *) pl, + cbremain, + flags | CPSS_VARIANTVECTOR, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + pl = (ULONG const *) Add2ConstPtr(pl, cb); + cbremain -= cb; + } + } + else + { + cbch = sizeof(WCHAR); + + switch( PropByteSwap(pprop->dwType) & VT_TYPEMASK) + { + case VT_EMPTY: + case VT_NULL: + fIllegalType = (flags & CPSS_VARIANTVECTOR) != 0; + break; + +#ifdef PROPVAR_VT_I1 + case VT_I1: +#endif + case VT_UI1: + pl = (ULONG const *) Add2ConstPtr(pl, DwordAlign(cElems * sizeof(BYTE))); + break; + + case VT_I2: + case VT_UI2: + case VT_BOOL: + pl = (ULONG const *) Add2ConstPtr(pl, DwordAlign(cElems * sizeof(USHORT))); + break; + + case VT_I4: + case VT_UI4: + case VT_R4: + case VT_ERROR: + pl = (ULONG const *) Add2ConstPtr(pl, cElems * sizeof(ULONG)); + break; + + case VT_I8: + case VT_UI8: + case VT_R8: + case VT_CY: + case VT_DATE: + case VT_FILETIME: + pl = (ULONG const *) Add2ConstPtr(pl, cElems * sizeof(LONGLONG)); + break; + + case VT_CLSID: + pl = (ULONG const *) Add2ConstPtr(pl, cElems * sizeof(GUID)); + break; + + case VT_BLOB: + case VT_BLOB_OBJECT: + // FALLTHROUGH + + case VT_STREAM: + case VT_STREAMED_OBJECT: + case VT_STORAGE: + case VT_STORED_OBJECT: + if (flags & CPSS_VARIANTVECTOR) + { + fIllegalType = TRUE; + break; + } + // FALLTHROUGH + + case VT_CF: + case VT_BSTR: + case VT_LPSTR: + cbch = sizeof(BYTE); + // FALLTHROUGH + + case VT_LPWSTR: + while (cElems-- > 0) + { + if (cbremain < sizeof(ULONG) || + cbremain < (cb = sizeof(ULONG) + DwordAlign(PropByteSwap(*pl) * cbch))) + { + StatusOverflow(pstatus, "PropertyLength: String/BLOB/CF/Indirect"); + goto Exit; + } + +#ifdef LITTLEENDIAN + PROPASSERT( + (PropByteSwap(pprop->dwType) & VT_TYPEMASK) != VT_LPWSTR + || + IsUnicodeString( (WCHAR const *) &pl[1], + PropByteSwap(*pl) * sizeof(WCHAR))); +#endif + + pl = (ULONG const *) Add2ConstPtr(pl, cb); + cbremain -= cb; + } + break; + + default: + fIllegalType = TRUE; + break; + } + } + if (fIllegalType) + { + StatusInvalidParameter(pstatus, "PropertyLength: Illegal VarType"); + goto Exit; + } + cb = (BYTE *) pl - (BYTE *) pprop; + if (cbbuf < cb) + { + StatusOverflow(pstatus, "PropertyLength: cb"); + goto Exit; + } + + // Make sure PropertyLength works when limited to an exact size buffer. + PROPASSERT(cb == cbbuf || PropertyLengthNoEH(pprop, cb, flags, pstatus) == cb); + + // ---- + // Exit + // ---- + +Exit: + + // Normalize the error return value. + if( !NT_SUCCESS(*pstatus) ) + cb = 0; + + return(cb); +} + + +//+-------------------------------------------------------------------------- +// Function: PropertyLengthAsVariant +// +// Synopsis: compute the size of external memory required to store the +// property as a PROPVARIANT +// +// Arguments: [pprop] -- property value +// [cbprop] -- computed length of pprop in propset stream +// [CodePage] -- property set codepage +// [flags] -- CPropertySetStream flags +// [pstatus] -- pointer to NTSTATUS code +// +// Returns: length of property +//--------------------------------------------------------------------------- + +#if defined(WINNT) && !defined(IPROPERTY_DLL) + +// First, define a wrapper which raises NT Exceptions for compatibility +// with older callers who expect it. + +ULONG +PropertyLengthAsVariant( + IN SERIALIZEDPROPERTYVALUE const *pprop, + IN ULONG cbprop, + IN USHORT CodePage, + IN BYTE flags) +{ + NTSTATUS status; + ULONG ulRet; + + ulRet = PropertyLengthAsVariantNoEH( pprop, cbprop, CodePage, flags, &status ); + + if (!NT_SUCCESS( status )) + RtlRaiseStatus( status ); + + return( ulRet ); +} + +// Now define the body of the function, returning errors with an +// NTSTATUS value instead of raising. + +ULONG +PropertyLengthAsVariantNoEH( + IN SERIALIZEDPROPERTYVALUE const *pprop, + IN ULONG cbprop, + IN USHORT CodePage, + IN BYTE flags, + OUT NTSTATUS *pstatus) +{ + ULONG cElems; + ULONG cbvar = 0; + + *pstatus = STATUS_SUCCESS; + + + PROPASSERT(cbprop == PropertyLengthNoEH(pprop, cbprop, flags, pstatus)); + if( PropByteSwap(pprop->dwType) & VT_VECTOR ) + { + cElems = *(ULONG *) pprop->rgb; + } + switch (pprop->dwType) + { + //default: + //case VT_EMPTY: + //case VT_NULL: + //case VT_I1: + //case VT_UI1: + //case VT_I2: + //case VT_UI2: + //case VT_BOOL: + //case VT_I4: + //case VT_UI4: + //case VT_R4: + //case VT_ERROR: + //case VT_I8: + //case VT_UI8: + //case VT_R8: + //case VT_CY: + //case VT_DATE: + //case VT_FILETIME: + //cbvar = 0; + //break; + + case VT_CLSID: + cbvar = cbprop - sizeof(ULONG); // don't include VARTYPE + break; + + // VT_CF: Round CLIPDATA up to Quad boundary, then drop VARTYPE+size+ + // clipfmt, which get tossed or unmarshalled into CLIPDATA. Round + // byte-granular data size to a Quad boundary when returning result. + + case VT_CF: + cbvar = QuadAlign(sizeof(CLIPDATA)) + cbprop - 3 * sizeof(ULONG); + break; + + case VT_BLOB: + case VT_BLOB_OBJECT: + cbvar = cbprop - 2 * sizeof(ULONG); // don't include VARTYPE & size + break; + + case VT_STREAM: + case VT_STREAMED_OBJECT: + case VT_STORAGE: + case VT_STORED_OBJECT: + + cbvar = cbprop - 2 * sizeof(ULONG); + if (CodePage != CP_WINUNICODE) + { + cbvar *= sizeof(WCHAR); // worst case Unicode conversion + } + + break; + + case VT_BSTR: + + // Don't include the size of the VT field, but leave + // the size of the length field accounted for. + cbvar = cbprop - sizeof(ULONG); + + // Worst-case Ansi->Unicode conversion: + cbvar *= sizeof(OLECHAR); + + break; + + case VT_LPSTR: // Assume Ansi conversion saves no space + case VT_LPWSTR: + cbvar = cbprop - 2 * sizeof(ULONG); + break; + + // Vector properties: + +#ifdef PROPVAR_VT_I1 + case VT_VECTOR | VT_I1: + AssertByteVector(cac); // VT_I1 +#endif + case VT_VECTOR | VT_UI1: + case VT_VECTOR | VT_I2: + case VT_VECTOR | VT_UI2: + case VT_VECTOR | VT_BOOL: + case VT_VECTOR | VT_I4: + case VT_VECTOR | VT_UI4: + case VT_VECTOR | VT_R4: + case VT_VECTOR | VT_ERROR: + 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: + case VT_VECTOR | VT_CLSID: + AssertByteVector(caub); // VT_UI1 + AssertShortVector(cai); // VT_I2 + AssertShortVector(caui); // VT_UI2 + AssertShortVector(cabool); // VT_BOOL + AssertLongVector(cal); // VT_I4 + AssertLongVector(caul); // VT_UI4 + AssertLongVector(caflt); // VT_R4 + AssertLongVector(cascode); // VT_ERROR + AssertLongLongVector(cah); // VT_I8 + AssertLongLongVector(cauh); // VT_UI8 + AssertLongLongVector(cadbl); // VT_R8 + AssertLongLongVector(cacy); // VT_CY + AssertLongLongVector(cadate); // VT_DATE + AssertLongLongVector(cafiletime); // VT_FILETIME + AssertVarVector(cauuid, sizeof(GUID)); + + // don't include VARTYPE and count fields + cbvar = cbprop - 2 * sizeof(ULONG); + break; + + case VT_VECTOR | VT_CF: // add room for each pointer + AssertVarVector(caclipdata, sizeof(CLIPDATA)); // VT_CF + + // don't include VARTYPE and count fields + cbvar = cbprop - 2 * sizeof(ULONG); + + // add room for each CLIPDATA data pointer and enough to Quad align + // every clipdata data element and 1 ULONG to Quad align the + // CLIPDATA array + cbvar += cElems * (sizeof(BYTE *) + sizeof(ULONG)) + sizeof(ULONG); + break; + + case VT_VECTOR | VT_BSTR: // add room for each BSTRLEN + AssertStringVector(cabstr); // VT_BSTR + + // don't include VARTYPE and count fields + cbvar = cbprop - 2 * sizeof(ULONG); + + if (CodePage != CP_WINUNICODE) + { + cbvar *= sizeof(OLECHAR); // worst case Unicode conversion + } + + // add room for each BSTRLEN value and enough to Quad align + // every BSTR and 1 ULONG to Quad align the array of BSTR pointers. + + cbvar += cElems * (sizeof(ULONG) + sizeof(ULONG)) + sizeof(ULONG); + break; + + case VT_VECTOR | VT_LPSTR: // Assume Ansi conversion saves no space + case VT_VECTOR | VT_LPWSTR: + AssertStringVector(calpstr); // VT_LPSTR + AssertStringVector(calpwstr); // VT_LPWSTR + + // don't include VARTYPE and count fields + cbvar = cbprop - 2 * sizeof(ULONG); + + // add enough room to Quad align every string and 1 ULONG to Quad + // align the array of string pointers. + + cbvar += cElems * sizeof(ULONG) + sizeof(ULONG); + break; + + case VT_VECTOR | VT_VARIANT: + { + ULONG const *pl = (ULONG const *) pprop->rgb + 1; + ULONG cbremain = cbprop - 2 * sizeof(ULONG); + + cbvar = cElems * sizeof(PROPVARIANT); + + while (cElems-- > 0) + { + ULONG cbpropElem; + ULONG cbvarElem; + + cbpropElem = PropertyLengthNoEH( + (SERIALIZEDPROPERTYVALUE *) pl, + cbremain, + flags | CPSS_VARIANTVECTOR, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + cbvarElem = PropertyLengthAsVariantNoEH( + (SERIALIZEDPROPERTYVALUE *) pl, + cbpropElem, + CodePage, + flags | CPSS_VARIANTVECTOR, + pstatus); + if( !NT_SUCCESS(*pstatus) ) goto Exit; + + pl = (ULONG const *) Add2ConstPtr(pl, cbpropElem); + cbremain -= cbpropElem; + cbvar += cbvarElem; + } + break; + } + } + + // ---- + // Exit + // ---- + +Exit: + + // Normalize the error return value. + if( !NT_SUCCESS(*pstatus) ) + cbvar = 0; + + return(QuadAlign(cbvar)); +} + +#endif // #if defined(WINNT) && !defined(IPROPERTY_DLL) + + + +//+-------------------------------------------------------------------------- +// Function: PBSCopy +// +// Synopsis: This is a Property Byte-Swap routine. The PBS routines +// only compile in the BIGENDIAN build. In the +// LITTLEENDIAN build, they are inlined with NOOP functions. +// +// This routine copies the source to the destination, +// byte-swapping as it copies. +// +// Arguments: [VOID*] pvDest +// Pointer to the target (swapped) buffer. +// This must be pre-allocated by the caller. +// [VOID*] pvSource +// Pointer to the original buffer. +// [ULONG] cbSize +// Size in bytes of the buffer. +// [ULONG] cbByteSwap +// Size of byte-swapping units. +// +// Returns: None. +// +//--------------------------------------------------------------------------- + +#ifdef BIGENDIAN + +VOID PBSCopy( OUT VOID *pvDest, + IN VOID const *pvSource, + IN ULONG cbCopy, + IN LONG cbByteSwap ) +{ + PROPASSERT( (cbCopy & 1) == 0 ); + PROPASSERT( pvDest != NULL && pvSource != NULL ); + + memcpy( pvDest, pvSource, cbCopy ); + PBSBuffer( pvDest, cbCopy, cbByteSwap ); +} + +#endif // BIGENDIAN + + +//+-------------------------------------------------------------------------- +// Function: PBSAllocAndCopy +// +// Synopsis: This is a Property Byte-Swap routine. The PBS routines +// only compile in the BIGENDIAN build. In the +// LITTLEENDIAN build, they are inlined with NOOP functions. +// +// This routine allocs a buffer, and swaps the bytes from +// the source buffer into the destination. +// +// Arguments: [VOID**] ppvDest (out) +// On success will point to the swapped buffer. +// [VOID*] pvSource (in) +// Pointer to the original buffer. +// [ULONG] cbSize (in) +// Size in bytes of the buffer. +// [LONG] cbByteSwap (in) +// Size of byte-swapping units. +// [NTSTATUS*] pstatus (out) +// NTSTATUS code. +// +// Returns: None. +// +// Note: The caller is responsible for freeing *ppvDest +// (using ::delete). +// +//--------------------------------------------------------------------------- + +#ifdef BIGENDIAN + +VOID PBSAllocAndCopy( OUT VOID **ppvDest, + IN VOID const *pvSource, + ULONG cbSize, + LONG cbByteSwap, + OUT NTSTATUS *pstatus) +{ + // ----- + // Begin + // ----- + + *pstatus = STATUS_SUCCESS; + PROPASSERT( ppvDest != NULL && pvSource != NULL ); + + // Allocate a buffer. + *ppvDest = new BYTE[ cbSize ]; + if( NULL == *ppvDest ) + { + *pstatus = STATUS_NO_MEMORY; + goto Exit; + } + + // Swap/copy the bytes. + PBSCopy( *ppvDest, pvSource, cbSize, cbByteSwap ); + + // ---- + // Exit + // ---- + +Exit: + + return; + +} // PBSAllocAndCopy + +#endif // BIGENDIAN + +//+-------------------------------------------------------------------------- +// Function: PBSInPlaceAlloc +// +// Synopsis: This is a Property Byte-Swap routine. The PBS routines +// only compile in the BIGENDIAN build. In the +// LITTLEENDIAN build, they are inlined with NOOP functions. +// +// This routine takes a WCHAR array, allocates a new buffer, +// and swaps the original array into the new buffer. +// +// +// Arguments: [WCHAR**] ppwszResult +// IN: *ppwszResult points to string to be swapped. +// OUT: *ppwszResult points to the swapped string. +// [WCHAR**] ppwszBuffer +// *ppwszBuffer points to the buffer which was allocated +// for the swapped bytes (should be the same as *ppwszResult). +// *ppwszBuffer must be NULL on input, and must be freed +// by the caller (using ::delete). +// [NTSTATUS*] pstatus +// NTSTATUS code. +// +// Returns: None. +// +// On input, *ppwszResult contains the original string. +// An equivalently sized buffer is allocated in *ppwszBuffer, +// and *ppwszResult is byte-swapped into it. *ppwszResult +// is then set to the new *ppwszBuffer. +// +// It doesn't appear to useful to have both buffer parameters, +// but it makes it easier on the caller in certain circumstances; +// *ppwszResult always points to the correct string, whether the +// build is BIGENDIAN (alloc & swap takes place) or the build +// is LITTLEENDIAN (nothing happes, so *ppwszResult continues +// to point to the proper string). The LITTLEENDIAN version of +// this function is implemented as an inline routine. +// +//--------------------------------------------------------------------------- + +#ifdef BIGENDIAN + +VOID PBSInPlaceAlloc( IN OUT WCHAR** ppwszResult, + OUT WCHAR** ppwszBuffer, + OUT NTSTATUS *pstatus ) +{ + // ------ + // Locals + // ------ + + WCHAR *pwszNewBuffer; + + // Pointers which will walk through the input buffers. + WCHAR *pwszOriginal, *pwszSwapped; + + // ----- + // Begin + // ----- + + *pstatus = STATUS_SUCCESS; + + // Allocate a new buffer. + pwszNewBuffer = new WCHAR[ Prop_wcslen(*ppwszResult) + 1 ]; + if( NULL == pwszNewBuffer ) + { + *pstatus = STATUS_NO_MEMORY; + goto Exit; + } + + // Swap the WCHARs into the new buffer. + + pwszOriginal = *ppwszResult; + pwszSwapped = pwszNewBuffer; + + do + { + *pwszSwapped = PropByteSwap(*pwszOriginal++); + } while( *pwszSwapped++ != L'\0' ); + + // If the caller wants a special pointer to the new buffer, + // set it now. + + if( NULL != ppwszBuffer ) + { + PROPASSERT( NULL== *ppwszBuffer ); + *ppwszBuffer = pwszNewBuffer; + } + + // Also point *ppwszResult to the new buffer. + *ppwszResult = pwszNewBuffer; + + + // ---- + // Exit + // ---- + +Exit: + return; +} // PropByteSwap( WCHAR**, WCHAR**, NTSTATUS*) + +#endif // BIGENDIAN + + +//+-------------------------------------------------------------------------- +// Function: PBSBuffer +// +// Synopsis: This is a Property Byte-Swap routine. The PBS routines +// only compile in the BIGENDIAN build. In the +// LITTLEENDIAN build, they are inlined with NOOP functions. +// +// This routine takes a buffer and byte-swaps it. The caller +// specifies the size of the buffer, and the granularity of +// the byte-swapping. +// +// Arguments: [VOID*] pv +// Pointer to the buffer to be swapped. +// [ULONG] cbSize +// Size in bytes of the buffer. +// [ULONG] cbByteSwap +// Size of byte-swapping units. +// +// Returns: None. +// +// For example, an array of 4 WORDs could be swapped with: +// +// PBSBuffer( (VOID*) aw, 8, sizeof(WORD) ); +// +//--------------------------------------------------------------------------- + +#ifdef BIGENDIAN + +VOID PBSBuffer( IN OUT VOID *pv, + IN ULONG cbSize, + IN ULONG cbByteSwap ) +{ + ULONG ulIndex; + + // What kind of swapping should be do? + + switch( cbByteSwap ) + { + // No swapping required + + case 0: + case( sizeof(BYTE) ): + + // Nothing to do. + break; + + // Swap WORDs + + case( sizeof(WORD) ): + + for( ulIndex = 0; ulIndex < cbSize/sizeof(WORD); ulIndex++ ) + ByteSwap( &((WORD*)pv)[ulIndex] ); + break; + + // Swap DWORDs + + case( sizeof(DWORD) ): + + for( ulIndex = 0; ulIndex < cbSize/sizeof(DWORD); ulIndex++ ) + ByteSwap( &((DWORD*)pv)[ulIndex] ); + break; + + // Swap LONGLONGs + + case( sizeof(LONGLONG) ): + + for( ulIndex = 0; ulIndex < cbSize/sizeof(LONGLONG); ulIndex++ ) + ByteSwap( &((LONGLONG*)pv)[ulIndex] ); + break; + + // Swap GUIDs + + case CBBYTESWAP_UID: + + for( ulIndex = 0; ulIndex < cbSize/sizeof(GUID); ulIndex++ ) + ByteSwap( &((GUID*)pv)[ulIndex] ); + break; + + // Error + + default: + PROPASSERT( !"Invalid generic byte-swap size" ); + } +} // PropByteSwap( VOID*, ULONG, ULONG ) + +#endif // BIGENDIAN + + + + +DEFINE_CBufferAllocator__Allocate diff --git a/private/dcomidl/remact.acf b/private/dcomidl/remact.acf new file mode 100644 index 000000000..8811673a1 --- /dev/null +++ b/private/dcomidl/remact.acf @@ -0,0 +1,5 @@ +[ implicit_handle(handle_t any_handle) ] interface IActivation +{ + [comm_status, fault_status] RemoteActivation(); +} +
\ No newline at end of file diff --git a/private/dcomidl/remact.idl b/private/dcomidl/remact.idl new file mode 100644 index 000000000..66467737d --- /dev/null +++ b/private/dcomidl/remact.idl @@ -0,0 +1,65 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1996. +// +// File: remact.idl +// +// Synopsis: Activation interface implemented by object exporters. +// +// This is the interface that needs to be supported by hosts that support +// remote connection requests and export objects. Only one instance of this +// interface can be exported by the host. +// +// Note that this interface is tied closely to IObjectExporter because +// RemoteActivation includes information otherwise retrieved from ResolveOXID. +// +//-------------------------------------------------------------------------- +[ + uuid(4d9f4ab8-7d1c-11cf-861e-0020af6e7c57), + pointer_default(unique) +] + +interface IActivation +{ + import "obase.idl"; + + const unsigned long MODE_GET_CLASS_OBJECT = 0xffffffff; + + // + // RemoteActivation is called to request interface pointer data for one or + // more interface IIDs from an object server servicing the given CLSID. + // This could result in the launching of a new instance of the server, or + // connection to an already running instance. + // + // Note that the ResolveOxid parameters are included as well, to prevent + // the necessity for a second RPC during a remote activation. However, + // the client receives the OXID as an out param since it is not known + // before connecting to the server. + // + + error_status_t RemoteActivation( + [in] handle_t hRpc, + [in] ORPCTHIS *ORPCthis, + [out] ORPCTHAT *ORPCthat, + [in] const GUID *Clsid, + [in, string, unique] WCHAR *pwszObjectName, + [in, unique] MInterfacePointer *pObjectStorage, + [in] DWORD ClientImpLevel, + [in] DWORD Mode, + [in] DWORD Interfaces, + [in,unique,size_is(Interfaces)] IID *pIIDs, + [in] unsigned short cRequestedProtseqs, + [in, size_is(cRequestedProtseqs)] + unsigned short aRequestedProtseqs[], + [out] OXID *pOxid, + [out] DUALSTRINGARRAY **ppdsaOxidBindings, + [out] IPID *pipidRemUnknown, + [out] DWORD *pAuthnHint, + [out] COMVERSION *pServerVersion, + [out] HRESULT *phr, + [out,size_is(Interfaces)] MInterfacePointer **ppInterfaceData, + [out,size_is(Interfaces)] HRESULT *pResults + ); +} + diff --git a/private/dcomidl/remunk.idl b/private/dcomidl/remunk.idl new file mode 100644 index 000000000..aa8c7fdaf --- /dev/null +++ b/private/dcomidl/remunk.idl @@ -0,0 +1,61 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1995. +// +// File: remunk.idl +// +// The remote version of IUnknown. This interface exists on every +// object that is exported. It is used by clients to query for new +// interfaces, get additional references (for marshalling), and release +// outstanding references. +// +//+------------------------------------------------------------------------- +[ + object, + uuid(00000131-0000-0000-C000-000000000046) +] + +interface IRemUnknown : IUnknown +{ +#ifndef DO_NO_IMPORTS + import "unknwn.idl"; + import "obase.idl"; +#endif + + typedef struct tagREMQIRESULT + { + HRESULT hResult; + STDOBJREF std; + } REMQIRESULT; + + HRESULT RemQueryInterface + ( + [in] REFIPID ripid, + [in] unsigned long cRefs, + [in] unsigned short cIids, + [in, size_is(cIids)] IID *iids, + [out, size_is(,cIids)] REMQIRESULT **ppQIResults + ); + + + typedef struct tagREMINTERFACEREF + { + IPID ipid; + unsigned long cPublicRefs; + unsigned long cPrivateRefs; + } REMINTERFACEREF; + + HRESULT RemAddRef + ( + [in] unsigned short cInterfaceRefs, + [in, size_is(cInterfaceRefs)] REMINTERFACEREF InterfaceRefs[], + [out, size_is(cInterfaceRefs)] HRESULT *pResults + ); + + HRESULT RemRelease + ( + [in] unsigned short cInterfaceRefs, + [in, size_is(cInterfaceRefs)] REMINTERFACEREF InterfaceRefs[] + ); +} diff --git a/private/dcomidl/sources b/private/dcomidl/sources new file mode 100644 index 000000000..a8cb8725a --- /dev/null +++ b/private/dcomidl/sources @@ -0,0 +1,82 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + David Plummer (davepl) 19-Mar-94 + + Modifed by via awk to include global project include file + and to wrap precompiled header line within a conditional + that can be set in this include file. + + Donna Liu (DonnaLi) 19-Dec-1993 + +!ENDIF + +MAJORCOMP = dcom +MINORCOMP = dcomidl + +!include daytona.inc + +# +# This is the name of the target built from the source files specified +# below. The name should include neither the path nor the file extension. +# + +TARGETNAME= dcomidl + +# +# This specifies where the target is to be built. A private target of +# type LIBRARY or DYNLINK should go to obj, whereas a public target of +# type LIBRARY or DYNLINK should go to $(BASEDIR)\public\sdk\lib. +# + +TARGETPATH= obj + +# +# This specifies the type of the target, such as PROGRAM, DYNLINK, LIBRARY, +# etc. +# + +TARGETTYPE= LIBRARY + + +PASS0_HEADERDIR=. +PASS0_SOURCEDIR=obj +MIDL_UUIDDIR=. + +INCLUDES= .;..\cinc;$(BASEDIR)\public\sdk\inc;obj + +C_DEFINES= \ + $(C_DEFINES) + +SOURCES= \ + objex_c.c \ + objex_s.c \ + remact_c.c\ + remact_s.c\ + lclor_c.c \ + lclor_s.c \ + odeth_c.c \ + orcb_c.c \ + orcb_s.c + +UMTYPE= windows +UMAPPL= +UMTEST= +UMLIBS= + +NTTARGETFILE0=allidl diff --git a/private/dcomidl/stgvarb.cxx b/private/dcomidl/stgvarb.cxx new file mode 100644 index 000000000..c5fd2365f --- /dev/null +++ b/private/dcomidl/stgvarb.cxx @@ -0,0 +1,1206 @@ +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1992 - 1992. +// +// File: StgVarB.cxx +// +// Contents: C++ Base wrapper for PROPVARIANT. +// +// History: 01-Aug-94 KyleP Created +// 31-Jul-96 MikeHill - Relaxed assert in IsUnicodeString. +// - Allow NULL strings. +// +//-------------------------------------------------------------------------- + +#include <pch.cxx> + +#include "debtrace.hxx" +#include <propset.h> +#include <propvar.h> + +// These optionally-compiled directives tell the compiler & debugger +// where the real file, rather than the copy, is located. +#ifdef _ORIG_FILE_LOCATION_ +#if __LINE__ != 25 +#error File heading has change size +#else +#line 29 "\\nt\\private\\dcomidl\\stgvarb.cxx" +#endif +#endif + +#if DBGPROP + +BOOLEAN +IsUnicodeString(WCHAR const *pwszname, ULONG cb) +{ + if (cb != 0) + { + ULONG i, cchDoubleAnsi, cchNull; + + cchNull = cchDoubleAnsi = 0; + for (i = 0; pwszname[i] != L'\0'; i++) + { + if ((char) pwszname[i] == '\0' || (char) (pwszname[i] >> 8) == '\0') + { + cchNull++; + if (i > 8 && cchDoubleAnsi > (3*i)/4) + { + // BUGBUG: This is a heuristic that should NOT be left in + // the build for long (even the checked build). + //PROPASSERT(!"IsUnicodeString: Suspicious string: double Ansi chars"); + //return(FALSE); + return(TRUE); + } + } + else + if (isprint((char) pwszname[i]) && isprint((char) (pwszname[i] >> 8))) + { + cchDoubleAnsi++; + } + } + if (cchNull < i/4) + { + // BUGBUG: This is a heuristic that should NOT be left in the + // build for long (even the checked build). + + //BUGBUG: cscdrt stringizes GUIDs, leaving lots of high bytes set. + //The DRT needs to change to avoid case mapping problems. + //Until then, return TRUE + //PROPASSERT(!"IsUnicodeString: Suspicious string: too few zero-extended Ansi chars"); + //return(FALSE); + } + + // If cb isn't MAXULONG we verify that cb is at least as + // big as the string. We can't check for equality, because + // there are some property sets in which the length field + // for a string may include several zero padding bytes. + + PROPASSERT(cb == MAXULONG || (i + 1) * sizeof(WCHAR) <= cb); + } + return(TRUE); +} + + +BOOLEAN +IsAnsiString(CHAR const *pszname, ULONG cb) +{ + if (cb != 0) + { + ULONG i; + + // If the string is NULL, then it's not not an Ansi string, + // so we'll call it an Ansi string. + + if( NULL == pszname ) + return( TRUE ); + + for (i = 0; pszname[i] != '\0'; i++) + { + } + if (i == 1 && isprint(pszname[0]) && + ((ULONG) &pszname[8] & 0xfff) == ((ULONG) pszname & 0xfff) && + isprint(pszname[2]) && pszname[3] == '\0' && + isprint(pszname[4]) && pszname[5] == '\0' && + isprint(pszname[6]) && pszname[7] == '\0') + { + // BUGBUG: This is a heuristic that should NOT be left in the + // build for long (even the checked build). + PROPASSERT(!"IsAnsiString: Suspicious string: looks like Unicode"); + return(FALSE); + } + + // If cb isn't MAXULONG we verify that cb is at least as + // big as the string. We can't check for equality, because + // there are some property sets in which the length field + // for a string may include several zero padding bytes. + + PROPASSERT(cb == MAXULONG || i + 1 <= cb); + } + return(TRUE); +} +#endif + + +//+------------------------------------------------------------------- +// Member: CBaseStorageVariant::UnmarshalledSize, public +// +// Synopsis: Unmarshalls a PROPVARIANT value serialized in a PDeSerStream. +// +// Arguments: [stm] -- serialized stream +// [cb] -- size of *additional* data goes here. Size of +// base PROPVARIANT not included. +// +// Returns: one of the following NTSTATUS values +// STATUS_SUCCESS -- the call was successful. +// STATUS_INVALID_PARAMETER -- unsupported type for unmarshalling. +// +// Notes: The size is computed assuming 4-byte granular allocations. +// +//-------------------------------------------------------------------- + +#if defined(WINNT) && !defined(IPROPERTY_DLL) + +#ifdef KERNEL +NTSTATUS +CBaseStorageVariant::UnmarshalledSize( + PDeSerStream& stm, + ULONG &cb) +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG cElems = 0; + ULONG i; + + cb = 0; + + VARTYPE vt = (VARTYPE) stm.GetULong(); + + switch (vt) + { + case VT_EMPTY: + case VT_NULL: + case VT_UI1: + case VT_I2: + case VT_UI2: + case VT_BOOL: + case VT_I4: + case VT_UI4: + case VT_R4: + case VT_ERROR: + case VT_I8: + case VT_UI8: + case VT_R8: + case VT_CY: + case VT_DATE: + case VT_FILETIME: + break; + + case VT_CLSID: + cb = sizeof(GUID); + break; + + case VT_BLOB: + case VT_BLOB_OBJECT: + cb = stm.GetULong(); + break; + + case VT_CF: + cb = stm.GetULong() + sizeof(CLIPDATA); + break; + + case VT_STREAM: + case VT_STREAMED_OBJECT: + PROPASSERT("Serialization of stream not yet supported!"); + Status = STATUS_INVALID_PARAMETER; // BUGBUG -- better error code ? + break; + + case VT_STORAGE: + case VT_STORED_OBJECT: + PROPASSERT("Serialization of storage not yet supported!"); + Status = STATUS_INVALID_PARAMETER; // BUGBUG -- better error code ? + break; + + case VT_BSTR: + cb = sizeof(ULONG) + stm.GetULong(); + break; + + case VT_LPSTR: + cb = stm.GetULong(); + break; + + case VT_LPWSTR: + cb = stm.GetULong() * sizeof(WCHAR); + break; + + case VT_VECTOR | VT_UI1: + cb = stm.GetULong(); + break; + + case VT_VECTOR | VT_I2: + case VT_VECTOR | VT_UI2: + case VT_VECTOR | VT_BOOL: + cb = stm.GetULong() * sizeof(SHORT); + break; + + case VT_VECTOR | VT_I4: + case VT_VECTOR | VT_UI4: + case VT_VECTOR | VT_R4: + case VT_VECTOR | VT_ERROR: + cb = stm.GetULong() * sizeof(LONG); + 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: + cb = stm.GetULong() * sizeof(LARGE_INTEGER); + break; + + case VT_VECTOR | VT_CLSID: + cb = stm.GetULong() * sizeof(GUID); + break; + + case VT_VECTOR | VT_CF: + cElems = stm.GetULong(); + cb = cElems * sizeof(CLIPDATA); + break; + + case VT_VECTOR | VT_BSTR: + cElems = stm.GetULong(); + cb = cElems * (sizeof(ULONG) + sizeof(LPSTR)); + break; + + case VT_VECTOR | VT_LPSTR: + cElems = stm.GetULong(); + cb = cElems * sizeof(LPSTR); + break; + + case VT_VECTOR | VT_LPWSTR: + cElems = stm.GetULong(); + cb = cElems * sizeof(LPWSTR); + break; + + case VT_VECTOR | VT_VARIANT: + cElems = stm.GetULong(); + cb = cElems * sizeof(PROPVARIANT); + break; + + default: + PROPASSERT(!"Invalid type for PROPVARIANT marshalling"); + Status = STATUS_INVALID_PARAMETER; + break; + } + + cb = (cb + 3) & ~3; + + if (cElems == 0 || Status != STATUS_SUCCESS) + { + return(Status); + } + + // We have a variant with variable sized data which requires + // further unmarshalling. + switch(vt) + { + case VT_VECTOR | VT_CF: + for (i = 0; i < cElems; i++) + { + ULONG len = (stm.GetULong() + 3) & ~3; + + cb += len; + stm.SkipChar(sizeof(ULONG) + len); + } + break; + + case VT_VECTOR | VT_BSTR: + case VT_VECTOR | VT_LPSTR: + for (i = 0; i < cElems; i++) + { + ULONG len = (stm.GetULong() + 3) & ~3; + + cb += len; + stm.SkipChar(len); + } + break; + + case VT_VECTOR | VT_LPWSTR: + for (i = 0; i < cElems; i++) + { + ULONG len = (stm.GetULong() * sizeof(WCHAR) + 3) & ~3; + + cb += len; + stm.SkipWChar(len / sizeof(WCHAR)); + } + break; + + case VT_VECTOR | VT_VARIANT: + for (i = 0; i < cElems; i++) + { + ULONG cbElem = 0; + + Status = CBaseStorageVariant::UnmarshalledSize(stm, cbElem); + if (Status != STATUS_SUCCESS) + { + break; + } + cb += cbElem; + } + break; + } + return(Status); +} +#endif //ifdef KERNEL + + +//+------------------------------------------------------------------- +// Member: CBaseStorageVariant::Unmarshall, public +// +// Synopsis: Unmarshalls a PROPVARIANT value serialized in a PDeSerStream. +// +// Arguments: [stm] -- serialized stream +// [var] -- unmarshalled PROPVARIANT instance +// [MemAlloc] -- memory allocator for unmarshalling +// +// Returns: one of the following NTSTATUS values +// STATUS_SUCCESS -- the call was successful. +// STATUS_INSUFFICIENT_RESOURCES -- out of memory. +// STATUS_INVALID_PARAMETER -- unsupported type for unmarshalling. +//-------------------------------------------------------------------- + +NTSTATUS +CBaseStorageVariant::Unmarshall( + PDeSerStream& stm, + PROPVARIANT& var, + PMemoryAllocator &MemAlloc) +{ +#if DBG + switch (stm.PeekULong()) + { + case VT_EMPTY: + case VT_NULL: + case VT_I1: + case VT_I2: + case VT_UI2: + case VT_BOOL: + case VT_I4: + case VT_UI4: + case VT_R4: + case VT_ERROR: + case VT_I8: + case VT_UI8: + case VT_R8: + case VT_CY: + case VT_DATE: + case VT_FILETIME: + case VT_CLSID: + case VT_BLOB: + case VT_BLOB_OBJECT: + case VT_CF: + case VT_STREAM: + case VT_STREAMED_OBJECT: + case VT_STORAGE: + case VT_STORED_OBJECT: + case VT_BSTR: + case VT_LPSTR: + case VT_LPWSTR: + case VT_VECTOR | VT_UI1: + case VT_VECTOR | VT_I2: + case VT_VECTOR | VT_UI2: + case VT_VECTOR | VT_BOOL: + case VT_VECTOR | VT_I4: + case VT_VECTOR | VT_UI4: + case VT_VECTOR | VT_R4: + case VT_VECTOR | VT_ERROR: + 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: + case VT_VECTOR | VT_CLSID: + case VT_VECTOR | VT_CF: + case VT_VECTOR | VT_BSTR: + case VT_VECTOR | VT_LPSTR: + case VT_VECTOR | VT_LPWSTR: + case VT_VECTOR | VT_VARIANT: + break; + + default: + PROPASSERT(!"Invalid type (peek) for PROPVARIANT unmarshalling"); + break; + } +#endif + + NTSTATUS Status = STATUS_SUCCESS; + ULONG cbAlloc = 0; + VOID **ppv = NULL; + BOOLEAN fZero = FALSE; + + // Zero the entire variant data structure before assembling it together. + memset(&var, 0, sizeof(PROPVARIANT)); + + var.vt = (VARTYPE) stm.GetULong(); + + switch (var.vt) + { + case VT_EMPTY: + case VT_NULL: + break; + + case VT_UI1: + var.bVal = stm.GetByte(); + break; + + case VT_I2: + case VT_UI2: + case VT_BOOL: + var.iVal = stm.GetUShort(); + break; + + case VT_I4: + case VT_UI4: + case VT_R4: + case VT_ERROR: + var.lVal = stm.GetULong(); + break; + + case VT_I8: + case VT_UI8: + case VT_R8: + case VT_CY: + case VT_DATE: + case VT_FILETIME: + stm.GetBlob((BYTE *)&var.hVal, sizeof(LARGE_INTEGER)); + break; + + case VT_CLSID: + cbAlloc = sizeof(GUID); + ppv = (void **)&var.puuid; + break; + + case VT_BLOB: + case VT_BLOB_OBJECT: + var.blob.cbSize = stm.GetULong(); + cbAlloc = var.blob.cbSize; + ppv = (void **)&var.blob.pBlobData; + break; + + case VT_CF: + var.pclipdata = (CLIPDATA *) MemAlloc.Allocate(sizeof(*var.pclipdata)); + if (var.pclipdata == NULL) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + var.pclipdata->cbSize = stm.GetULong(); + cbAlloc = CBPCLIPDATA(*var.pclipdata); + var.pclipdata->ulClipFmt = stm.GetULong(); + ppv = (void **) &var.pclipdata->pClipData; + break; + + case VT_STREAM: + case VT_STREAMED_OBJECT: + PROPASSERT("Serialization of stream not yet supported!"); + Status = STATUS_INVALID_PARAMETER; // BUGBUG -- better error code ? + break; + + case VT_STORAGE: + case VT_STORED_OBJECT: + PROPASSERT("Serialization of storage not yet supported!"); + Status = STATUS_INVALID_PARAMETER; // BUGBUG -- better error code ? + break; + + case VT_BSTR: + cbAlloc = sizeof(ULONG) + stm.GetULong(); + ppv = (void **)&var.bstrVal; + break; + + case VT_LPSTR: + cbAlloc = stm.GetULong(); + ppv = (void **)&var.pszVal; + break; + + case VT_LPWSTR: + cbAlloc = stm.GetULong() * sizeof(WCHAR); + ppv = (void **)&var.pwszVal; + break; + + case VT_VECTOR | VT_UI1: + var.caub.cElems = stm.GetULong(); + cbAlloc = var.caub.cElems * sizeof(BYTE); + ppv = (void **)&var.caub.pElems; + break; + + case VT_VECTOR | VT_I2: + case VT_VECTOR | VT_UI2: + case VT_VECTOR | VT_BOOL: + var.cai.cElems = stm.GetULong(); + cbAlloc = var.cai.cElems * sizeof(SHORT); + ppv = (void **)&var.cai.pElems; + break; + + case VT_VECTOR | VT_I4: + case VT_VECTOR | VT_UI4: + case VT_VECTOR | VT_R4: + case VT_VECTOR | VT_ERROR: + var.cal.cElems = stm.GetULong(); + cbAlloc = var.cal.cElems * sizeof(LONG); + ppv = (void **)&var.cal.pElems; + 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: + var.cah.cElems = stm.GetULong(); + cbAlloc = var.cah.cElems * sizeof(LARGE_INTEGER); + ppv = (void **)&var.cah.pElems; + break; + + case VT_VECTOR | VT_CLSID: + var.cauuid.cElems = stm.GetULong(); + cbAlloc = var.cauuid.cElems * sizeof(GUID); + ppv = (void **)&var.cauuid.pElems; + break; + + case VT_VECTOR | VT_CF: + var.caclipdata.cElems = stm.GetULong(); + cbAlloc = var.caclipdata.cElems * sizeof(CLIPDATA); + ppv = (void **)&var.caclipdata.pElems; + fZero = TRUE; // set all pClipData pointers to NULL + break; + + case VT_VECTOR | VT_BSTR: + var.cabstr.cElems = stm.GetULong(); + cbAlloc = var.cabstr.cElems * sizeof(BSTR); + ppv = (void **)&var.cabstr.pElems; + fZero = TRUE; // set all BSTR pointers to NULL + break; + + case VT_VECTOR | VT_LPSTR: + var.calpstr.cElems = stm.GetULong(); + cbAlloc = var.calpstr.cElems * sizeof(LPSTR); + ppv = (void **)&var.calpstr.pElems; + fZero = TRUE; // set all LPSTR pointers to NULL + break; + + case VT_VECTOR | VT_LPWSTR: + var.calpwstr.cElems = stm.GetULong(); + cbAlloc = var.calpwstr.cElems * sizeof(LPWSTR); + ppv = (void **)&var.calpwstr.pElems; + fZero = TRUE; // set all LPWSTR pointers to NULL + break; + + case VT_VECTOR | VT_VARIANT: + var.capropvar.cElems = stm.GetULong(); + cbAlloc = var.capropvar.cElems * sizeof(PROPVARIANT); + ppv = (void **)&var.capropvar.pElems; + fZero = TRUE; // set all vt pointers to VT_EMPTY + PROPASSERT(VT_EMPTY == 0); + break; + + default: + PROPASSERT(!"Invalid type for PROPVARIANT unmarshalling"); + Status = STATUS_INVALID_PARAMETER; + break; + } + + if (cbAlloc == 0 || Status != STATUS_SUCCESS) + { + // No further work need be done. The Ummarshalling is complete, + // i.e., fixed size variant or no variable length data. + + if (ppv != NULL) + { + *ppv = NULL; + } + return(Status); + } + + // Allocate the desired amount of memory and continue unmarshalling + // if allocation was successfull. + + ULONG i; + + PROPASSERT(ppv != NULL); + *ppv = MemAlloc.Allocate(cbAlloc); + + if (*ppv == NULL) + { + return(STATUS_INSUFFICIENT_RESOURCES); + } + if (fZero) + { + memset(*ppv, 0, cbAlloc); + } + + // We have a variant with variable sized data which requires + // further unmarshalling. + switch(var.vt) + { + case VT_CLSID: + stm.GetBlob((BYTE *)var.puuid, sizeof(CLSID)); + break; + + case VT_BLOB: + case VT_BLOB_OBJECT: + stm.GetBlob(var.blob.pBlobData, var.blob.cbSize); + break; + + case VT_CF: + stm.GetBlob(var.pclipdata->pClipData, CBPCLIPDATA(*var.pclipdata)); + break; + + case VT_BSTR: + cbAlloc -= sizeof(ULONG); + *(ULONG *) var.bstrVal = cbAlloc - sizeof (OLECHAR); + var.bstrVal = (BSTR) ((ULONG *) var.bstrVal + 1); + stm.GetChar((char *) var.bstrVal, cbAlloc); + break; + + case VT_LPSTR: + stm.GetChar(var.pszVal, cbAlloc); + break; + + case VT_LPWSTR: + stm.GetWChar(var.pwszVal, cbAlloc / sizeof(WCHAR)); + break; + + case VT_VECTOR | VT_UI1: + for (i = 0; i < var.caub.cElems; i++) + { + var.caub.pElems[i] = stm.GetByte(); + } + break; + + case VT_VECTOR | VT_I2: + case VT_VECTOR | VT_UI2: + case VT_VECTOR | VT_BOOL: + for (i = 0; i < var.cai.cElems; i++) + { + var.cai.pElems[i] = stm.GetUShort(); + } + break; + + case VT_VECTOR | VT_I4: + case VT_VECTOR | VT_UI4: + case VT_VECTOR | VT_R4: + case VT_VECTOR | VT_ERROR: + for (i = 0; i < var.cal.cElems; i++) + { + var.cal.pElems[i] = stm.GetULong(); + } + 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: + for (i = 0; i < var.cah.cElems; i++) + { + stm.GetBlob((BYTE *)&var.cah.pElems[i], sizeof(LARGE_INTEGER)); + } + break; + + case VT_VECTOR | VT_CLSID: + for (i = 0; i < var.cauuid.cElems; i++) + { + stm.GetBlob((BYTE *)&var.cauuid.pElems[i], sizeof(CLSID)); + } + break; + + case VT_VECTOR | VT_CF: + for (i = 0; i < var.caclipdata.cElems; i++) + { + PROPASSERT(var.caclipdata.pElems[i].pClipData == NULL); + var.caclipdata.pElems[i].cbSize = stm.GetULong(); + cbAlloc = CBPCLIPDATA(var.caclipdata.pElems[i]); + var.caclipdata.pElems[i].ulClipFmt = stm.GetULong(); + if (cbAlloc == 0) + { + Status = STATUS_INVALID_PARAMETER; + break; + } + var.caclipdata.pElems[i].pClipData = + (BYTE *) MemAlloc.Allocate(cbAlloc); + if (var.caclipdata.pElems[i].pClipData == NULL) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + stm.GetBlob(var.caclipdata.pElems[i].pClipData, cbAlloc); + } + break; + + case VT_VECTOR | VT_BSTR: + for (i = 0; i < var.cabstr.cElems; i++) + { + PROPASSERT(var.cabstr.pElems[i] == NULL); + cbAlloc = stm.GetULong(); + if (cbAlloc == 0) + { + Status = STATUS_INVALID_PARAMETER; + break; + } + var.cabstr.pElems[i] = + (BSTR) MemAlloc.Allocate(sizeof(ULONG) + cbAlloc); + if (var.cabstr.pElems[i] == NULL) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + *(ULONG *) var.cabstr.pElems[i] = cbAlloc - sizeof (OLECHAR); + var.cabstr.pElems[i] = (BSTR) ((ULONG *) var.cabstr.pElems[i] + 1); + stm.GetChar((char *) var.cabstr.pElems[i], cbAlloc); + } + break; + + case VT_VECTOR | VT_LPSTR: + for (i = 0; i < var.calpstr.cElems; i++) + { + PROPASSERT(var.calpstr.pElems[i] == NULL); + cbAlloc = stm.GetULong(); + if (cbAlloc == 0) + { + Status = STATUS_INVALID_PARAMETER; + break; + } + var.calpstr.pElems[i] = (LPSTR) MemAlloc.Allocate(cbAlloc); + if (var.calpstr.pElems[i] == NULL) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + stm.GetChar(var.calpstr.pElems[i], cbAlloc); + } + break; + + case VT_VECTOR | VT_LPWSTR: + for (i = 0; i < var.calpwstr.cElems; i++) + { + PROPASSERT(var.calpwstr.pElems[i] == NULL); + cbAlloc = stm.GetULong(); // actually, a count of WCHARs + if (cbAlloc == 0) + { + Status = STATUS_INVALID_PARAMETER; + break; + } + var.calpwstr.pElems[i] = (WCHAR *) MemAlloc.Allocate(cbAlloc * sizeof(WCHAR)); + if (var.calpwstr.pElems[i] == NULL) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + stm.GetWChar(var.calpwstr.pElems[i], cbAlloc); + } + break; + + case VT_VECTOR | VT_VARIANT: + for (i = 0; i < var.capropvar.cElems; i++) + { + PROPASSERT(var.capropvar.pElems[i].vt == VT_EMPTY); + Status = CBaseStorageVariant::Unmarshall( + stm, + var.capropvar.pElems[i], + MemAlloc); + if (Status != STATUS_SUCCESS) + { + break; + } + } + break; + } + return(Status); +} + + +#ifdef ENABLE_MARSHAL_VARIANT +inline void +_Marshall_VT_CF(CLIPDATA *pclipdata, PSerStream &stm) +{ + CLIPDATA clipdata; + + clipdata.cbSize = 0; + clipdata.ulClipFmt = 0; + + if (pclipdata != NULL) + { + clipdata.cbSize = pclipdata->cbSize; + clipdata.ulClipFmt = pclipdata->ulClipFmt; + if (pclipdata->pClipData == NULL) + { + clipdata.cbSize = 0; + } + } + stm.PutULong(clipdata.cbSize); + stm.PutULong(clipdata.ulClipFmt); + if (clipdata.cbSize) + { + stm.PutBlob((BYTE *) pclipdata->pClipData, CBPCLIPDATA(clipdata)); + } +} +#endif //ifdef ENABLE_MARSHAL_VARIANT + + +#ifdef ENABLE_MARSHAL_VARIANT +inline void +_Marshall_VT_BSTR(BSTR bstrVal, PSerStream &stm) +{ + if (bstrVal != NULL) + { + ULONG cc = BSTRLEN(bstrVal) + sizeof (OLECHAR); + + stm.PutULong(cc); + stm.PutChar((char *) bstrVal, cc); + } + else + { + stm.PutULong(0); + } +} +#endif //ifdef ENABLE_MARSHAL_VARIANT + + +#ifdef ENABLE_MARSHAL_VARIANT +inline void +_Marshall_VT_LPSTR(CHAR *pszVal, PSerStream &stm) +{ + if (pszVal != NULL) + { + // Include NULL because OLE 2.0 spec says so. + ULONG cc = strlen(pszVal) + 1; + + stm.PutULong(cc); + stm.PutChar(pszVal, cc); + PROPASSERT(IsAnsiString(pszVal, cc)); + } + else + { + stm.PutULong(0); + } +} +#endif //ifdef ENABLE_MARSHAL_VARIANT + + +#ifdef ENABLE_MARSHAL_VARIANT +inline void +_Marshall_VT_LPWSTR(LPWSTR pwszVal, PSerStream &stm) +{ + if (pwszVal != NULL) + { + // Include NULL because OLE 2.0 spec says so. + + ULONG cc = Prop_wcslen(pwszVal) + 1; + + PROPASSERT(IsUnicodeString(pwszVal, cc * sizeof(WCHAR))); + stm.PutULong(cc); + stm.PutWChar(pwszVal, cc); + } + else + { + stm.PutULong(0); + } +} +#endif //ifdef ENABLE_MARSHAL_VARIANT + + +#ifdef ENABLE_MARSHAL_VARIANT +void +CBaseStorageVariant::Marshall(PSerStream & stm) const +{ + ULONG i; + + stm.PutULong(vt); + + switch (vt) + { + case VT_EMPTY: + case VT_NULL: + break; + + case VT_UI1: + stm.PutByte(bVal); + break; + + case VT_I2: + case VT_UI2: + case VT_BOOL: + stm.PutUShort(iVal); + break; + + case VT_I4: + case VT_UI4: + case VT_R4: + case VT_ERROR: + stm.PutULong(lVal); + break; + + case VT_I8: + case VT_UI8: + case VT_R8: + case VT_CY: + case VT_DATE: + case VT_FILETIME: + stm.PutBlob((BYTE *) &hVal, sizeof(hVal)); + break; + + case VT_CLSID: + stm.PutBlob((BYTE *)puuid, sizeof(CLSID)); + break; + + case VT_BLOB: + case VT_BLOB_OBJECT: + stm.PutULong(blob.cbSize); + stm.PutBlob(blob.pBlobData, blob.cbSize); + break; + + case VT_CF: + _Marshall_VT_CF(pclipdata, stm); + break; + + case VT_STREAM: + case VT_STREAMED_OBJECT: + PROPASSERT("Serialization of stream not yet supported!"); + break; + + case VT_STORAGE: + case VT_STORED_OBJECT: + PROPASSERT("Serialization of storage not yet supported!"); + break; + + case VT_BSTR: + _Marshall_VT_BSTR(bstrVal, stm); + break; + + case VT_LPSTR: + _Marshall_VT_LPSTR(pszVal, stm); + break; + + case VT_LPWSTR: + _Marshall_VT_LPWSTR(pwszVal, stm); + break; + + case VT_VECTOR | VT_UI1: + stm.PutULong(caub.cElems); + for (i = 0; i < caub.cElems; i++) + { + stm.PutByte(caub.pElems[i]); + } + break; + + case VT_VECTOR | VT_I2: + case VT_VECTOR | VT_UI2: + case VT_VECTOR | VT_BOOL: + stm.PutULong(cai.cElems); + for (i = 0; i < cai.cElems; i++) + { + stm.PutUShort(cai.pElems[i]); + } + break; + + case VT_VECTOR | VT_I4: + case VT_VECTOR | VT_UI4: + case VT_VECTOR | VT_R4: + case VT_VECTOR | VT_ERROR: + stm.PutULong(cal.cElems); + for (i = 0; i < cal.cElems; i++) + { + stm.PutULong(cal.pElems[i]); + } + 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: + stm.PutULong(cah.cElems); + for (i = 0; i < cah.cElems; i++) + { + stm.PutBlob((BYTE *) &cah.pElems[i], sizeof(LARGE_INTEGER)); + } + break; + + case VT_VECTOR | VT_CLSID: + stm.PutULong(cauuid.cElems); + for (i = 0; i < cauuid.cElems; i++) + { + stm.PutBlob((BYTE *)&cauuid.pElems[i], sizeof(CLSID)); + } + break; + + case VT_VECTOR | VT_CF: + stm.PutULong(caclipdata.cElems); + for (i = 0; i < caclipdata.cElems; i++) + { + _Marshall_VT_CF(&caclipdata.pElems[i], stm); + } + break; + break; + + case VT_VECTOR | VT_BSTR: + stm.PutULong(cabstr.cElems); + for (i = 0; i < cabstr.cElems; i++) + { + _Marshall_VT_BSTR(cabstr.pElems[i], stm); + } + break; + + case VT_VECTOR | VT_LPSTR: + stm.PutULong(calpstr.cElems); + for (i = 0; i < calpstr.cElems; i++) + { + _Marshall_VT_LPSTR(calpstr.pElems[i], stm); + } + break; + + case VT_VECTOR | VT_LPWSTR: + stm.PutULong(calpwstr.cElems); + for (i = 0; i < calpwstr.cElems; i++) + { + _Marshall_VT_LPWSTR(calpwstr.pElems[i], stm); + } + break; + + case VT_VECTOR | VT_VARIANT: + stm.PutULong(capropvar.cElems); + for (i = 0; i < capropvar.cElems; i++) + { + ((CBaseStorageVariant *) &capropvar.pElems[i])->Marshall(stm); + } + break; + + default: + PROPASSERT(!"Invalid type for PROPVARIANT marshalling"); + break; + + } +} +#endif //ifdef ENABLE_MARSHAL_VARIANT + + +#ifdef OLDSUMCATAPI +void +MarshallVariant(PSerStream &stm, PROPVARIANT &stgvar) +{ + CBaseStorageVariant *pstgvar = (CBaseStorageVariant *)&stgvar; + pstgvar->Marshall(stm); +} +#endif //ifdef OLDSUMCATAPI + + +#ifdef ENABLE_DISPLAY_VARIANT +VOID +CBaseStorageVariant::DisplayVariant( + ULONG ulLevel, + USHORT CodePage) const +{ + char *psz; + + switch (vt) + { + case VT_ILLEGAL: psz = "ILLEGAL"; goto EmptyType; + case VT_EMPTY: psz = "EMPTY"; goto EmptyType; + case VT_NULL: psz = "NULL"; goto EmptyType; + +BlobType: +EmptyType: + DEBTRACE((DBGFLAG "%s", psz)); + break; + + case VT_UI1: + AssertByteField(bVal); // VT_UI1 + DEBTRACE((DBGFLAG "UI1=%hx", bVal)); + break; + + case VT_I2: psz = "I2"; goto ShortType; + case VT_UI2: psz = "UI2"; goto ShortType; + +ShortType: + AssertShortField(iVal); // VT_I2 + AssertShortField(uiVal); // VT_UI2 + DEBTRACE((DBGFLAG "%s=%hx", psz, iVal)); + break; + + case VT_BOOL: + switch (boolVal) + { + case VARIANT_TRUE: + DEBTRACE((DBGFLAG "BOOL=TRUE")); + break; + + case FALSE: + DEBTRACE((DBGFLAG "BOOL=FALSE")); + break; + + default: + DEBTRACE((DBGFLAG "BOOL=%hx???", boolVal)); + break; + } + break; + + case VT_I4: psz = "I4"; goto LongType; + case VT_UI4: psz = "UI4"; goto LongType; + case VT_R4: psz = "R4"; goto LongType; + case VT_ERROR: psz = "ERROR"; goto LongType; + +LongType: + AssertLongField(lVal); // VT_I4 + AssertLongField(ulVal); // VT_UI4 + AssertLongField(fltVal); // VT_R4 + AssertLongField(scode); // VT_ERROR + DEBTRACE((DBGFLAG "%s=%x", psz, lVal)); + break; + + case VT_I8: psz = "I8"; goto LongLongType; + case VT_UI8: psz = "UI8"; goto LongLongType; + case VT_R8: psz = "R8"; goto LongLongType; + case VT_CY: psz = "CY"; goto LongLongType; + case VT_DATE: psz = "DATE"; goto LongLongType; + case VT_FILETIME: psz = "FILETIME"; goto LongLongType; + +LongLongType: + AssertLongLongField(hVal); // VT_I8 + AssertLongLongField(uhVal); // VT_UI8 + AssertLongLongField(dblVal); // VT_R8 + AssertLongLongField(cyVal); // VT_CY + AssertLongLongField(date); // VT_DATE + AssertLongLongField(filetime); // VT_FILETIME + DEBTRACE((DBGFLAG "%s=%x:%x", psz, hVal.HighPart, hVal.LowPart)); + break; + + case VT_CLSID: psz = "CLSID"; goto EmptyType; + + case VT_BLOB: psz = "BLOB"; goto BlobType; + case VT_BLOB_OBJECT: psz = "BLOB_OBJECT"; goto BlobType; + case VT_CF: psz = "CF"; goto BlobType; + + case VT_STREAM: psz = "STREAM"; goto TestUnicode; + case VT_STREAMED_OBJECT: psz = "STREAMED_OBJECT"; goto TestUnicode; + case VT_STORAGE: psz = "STORAGE"; goto TestUnicode; + case VT_STORED_OBJECT: psz = "STORED_OBJECT"; goto TestUnicode; + case VT_LPSTR: psz = "LPSTR"; goto TestUnicode; + +TestUnicode: + AssertStringField(pszVal); // VT_STREAM, VT_STREAMED_OBJECT + AssertStringField(pszVal); // VT_STORAGE, VT_STORED_OBJECT + AssertStringField(pszVal); // VT_LPSTR + DEBTRACE(( + DBGFLAG + CodePage == CP_WINUNICODE? "%s=L'%ws'" : "%s='%s'", + psz, + pszVal)); + break; + + case VT_BSTR: psz = "BSTR"; goto PrintUnicode; + case VT_LPWSTR: psz = "LPWSTR"; goto PrintUnicode; + +PrintUnicode: + AssertStringField(pwszVal); // VT_LPWSTR + AssertStringField(bstrVal); // VT_BSTR + DEBTRACE((DBGFLAG "%s=L'%ws'", psz, pwszVal)); + break; + + default: + if (vt & VT_VECTOR) + { + DEBTRACE((DBGFLAG "UNPRINTABLE VECTOR TYPE=%x(%u)", vt, vt)); + } + else + { + DEBTRACE((DBGFLAG "UNKNOWN TYPE=%x(%u)", vt, vt)); + } + break; + + } +} +#endif //ifdef ENABLE_DISPLAY_VARIANT + +#endif //ifdef WINNT |