summaryrefslogtreecommitdiffstats
path: root/private/dcomidl
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/dcomidl
downloadNT4.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.h52
-rw-r--r--private/dcomidl/daytona.inc39
-rw-r--r--private/dcomidl/dce.idl17
-rw-r--r--private/dcomidl/iface.idl60
-rw-r--r--private/dcomidl/lclor.acf22
-rw-r--r--private/dcomidl/lclor.idl199
-rw-r--r--private/dcomidl/makefile10
-rw-r--r--private/dcomidl/makefile.inc74
-rw-r--r--private/dcomidl/ntprop.cxx409
-rw-r--r--private/dcomidl/ntpropb.cxx2378
-rw-r--r--private/dcomidl/obase.idl266
-rw-r--r--private/dcomidl/objex.acf9
-rw-r--r--private/dcomidl/objex.idl85
-rw-r--r--private/dcomidl/odeth.acf14
-rw-r--r--private/dcomidl/odeth.idl99
-rw-r--r--private/dcomidl/orcb.acf6
-rw-r--r--private/dcomidl/orcb.idl27
-rw-r--r--private/dcomidl/propstm.cxx7971
-rw-r--r--private/dcomidl/propvar.cxx3281
-rw-r--r--private/dcomidl/remact.acf5
-rw-r--r--private/dcomidl/remact.idl65
-rw-r--r--private/dcomidl/remunk.idl61
-rw-r--r--private/dcomidl/sources82
-rw-r--r--private/dcomidl/stgvarb.cxx1206
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 = &prop;
+ 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