|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
// Description:
// Class which acts as adaptor between IManagedFilter and IFilter.
// Used by PackageFilter for XamlFilter and CorePropertiesFilter.
// Used by EncryptedPackageFilter for CorePropertiesFilter.
//
using System.Runtime.InteropServices;
using System.Windows; // for ExceptionStringTable
using MS.Internal.Interop; // for STAT_CHUNK, etc.
namespace MS.Internal.IO.Packaging
{
#region IndexingFilterMarshaler
/// <summary>
/// Adapter for IManagedFilter. Used to obtain an IFilter interface from an IManagedFilter.
/// </summary>
/// <remarks>
/// IManagedFilter is supported by filters which have purely managed implementation and don't work
/// with interop entities. Callers like PackageFilter and EncryptedPackageFilter which support
/// IFilter interfaces interact with IManagedFilters like XamlFilter, PackageCorePropertiesFilter
/// and EncryptedPackageCorePropertiesFilter through IndexingFilterMarshaler.
/// </remarks>
internal class IndexingFilterMarshaler : IFilter
{
#region Static members.
/// <summary>
/// pre-defined GUID for storage properties on file system files (as per MSDN)
/// </summary>
internal static Guid PSGUID_STORAGE = new Guid(0xb725f130, 0x47ef, 0x101a, 0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac);
/// Cache frequently used size values to incur reflection cost just once.
internal const Int32 _int16Size = 2;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="managedFilter">IManagedFilter implementation</param>
internal IndexingFilterMarshaler(IManagedFilter managedFilter)
{
ArgumentNullException.ThrowIfNull(managedFilter);
_implementation = managedFilter;
}
/// <summary>
/// Init
/// </summary>
/// <param name="cAttributes">length of aAttributes</param>
/// <param name="aAttributes">array of FULLPROPSPEC structs</param>
/// <returns>managed array</returns>
internal static ManagedFullPropSpec[] MarshalFullPropSpecArray(
uint cAttributes, // length of aAttributes
FULLPROPSPEC[] aAttributes)
{
// If there are attributes, these override the flags
if (cAttributes > 0)
{
// Attributes count and array should match.
// This has already been checked for by XpsFilter.
Invariant.Assert(aAttributes != null);
ManagedFullPropSpec[] initAttributes = new ManagedFullPropSpec[checked((int)cAttributes)];
// convert to managed equivalents to isolate the marshaling effort
for (int i = 0; i < cAttributes; i++)
{
// convert and add to local list
initAttributes[i] = new ManagedFullPropSpec(aAttributes[i]);
}
return initAttributes;
}
else
return null;
}
/// <summary>
/// StringToPtr
/// </summary>
/// <remarks>Converts a managed string into the format useful for IFilter.GetText</remarks>
/// <param name="s">string to convert</param>
/// <param name="bufCharacterCount">maximum number of characters to convert</param>
/// <param name="p">pointer to write to</param>
internal static void MarshalStringToPtr(string s, ref uint bufCharacterCount, IntPtr p)
{
// bufCharacterCount is never supposed to be zero at this level.
Invariant.Assert(bufCharacterCount != 0);
// ensure the interface rules are followed
// string must also be null terminated so we restrict the length to buf size - 1
if ((uint)(s.Length) > bufCharacterCount - 1)
throw new InvalidOperationException(SR.FilterGetTextBufferOverflow);
// Return the number of characters written, including the terminating null.
bufCharacterCount = (UInt32)s.Length + 1;
// convert string to unmanaged string and write into provided buffer
Marshal.Copy(s.ToCharArray(), 0, p, s.Length);
// null terminate (16bit's of zero to replace one Unicode character)
Marshal.WriteInt16(p, s.Length * _int16Size, 0);
}
/// <summary>
/// Marshal Managed to Native PROPSPEC
/// </summary>
/// <param name="propSpec"></param>
/// <param name="native"></param>
internal static void MarshalPropSpec(ManagedPropSpec propSpec, ref PROPSPEC native)
{
native.propType = (uint)propSpec.PropType;
switch (propSpec.PropType)
{
case PropSpecType.Id:
native.union.propId = (uint)propSpec.PropId;
break;
case PropSpecType.Name:
native.union.name = Marshal.StringToCoTaskMemUni(propSpec.PropName);
break;
default:
Invariant.Assert(false); // propSpec.PropType is set by internal code in the filter logic.
break;
}
}
/// <summary>
/// Marshal Managed to Native FULLPROPSPEC
/// </summary>
/// <param name="fullPropSpec"></param>
/// <param name="native"></param>
internal static void MarshalFullPropSpec(ManagedFullPropSpec fullPropSpec, ref FULLPROPSPEC native)
{
native.guid = fullPropSpec.Guid;
MarshalPropSpec(fullPropSpec.Property, ref native.property);
}
/// <summary>
/// GetChunk
/// </summary>
/// <returns>An interop STAT_CHUNK from a ManagedChunk</returns>
internal static STAT_CHUNK MarshalChunk(ManagedChunk chunk)
{
STAT_CHUNK native = new STAT_CHUNK();
native.idChunk = chunk.ID;
Invariant.Assert(chunk.BreakType >= CHUNK_BREAKTYPE.CHUNK_NO_BREAK && chunk.BreakType <= CHUNK_BREAKTYPE.CHUNK_EOC);
native.breakType = chunk.BreakType;
Invariant.Assert(
chunk.Flags >= 0
&&
chunk.Flags <= (CHUNKSTATE.CHUNK_TEXT | CHUNKSTATE.CHUNK_VALUE | CHUNKSTATE.CHUNK_FILTER_OWNED_VALUE));
native.flags = chunk.Flags;
native.locale = chunk.Locale;
native.idChunkSource = chunk.ChunkSource;
native.cwcStartSource = chunk.StartSource;
native.cwcLenSource = chunk.LenSource;
MarshalFullPropSpec(chunk.Attribute, ref native.attribute);
return native;
}
/// <summary>
/// MarshalPropVariant
/// </summary>
/// <param name="obj">Object to marshal, should be DateTime or String</param>
/// <returns>newly allocated PROPVARIANT structure</returns>
internal static IntPtr MarshalPropVariant(Object obj)
{
IntPtr pszVal = IntPtr.Zero;
IntPtr pNative = IntPtr.Zero;
try
{
PROPVARIANT v;
if (obj is string)
{
pszVal = Marshal.StringToCoTaskMemAnsi((string)obj);
v = new PROPVARIANT();
v.vt = VARTYPE.VT_LPSTR;
v.union.pszVal = pszVal;
}
else if (obj is DateTime)
{
v = new PROPVARIANT();
v.vt = VARTYPE.VT_FILETIME;
long longFileTime = ((DateTime)obj).ToFileTime();
v.union.filetime.dwLowDateTime = (Int32)longFileTime;
v.union.filetime.dwHighDateTime = (Int32)((longFileTime >> 32) & 0xFFFFFFFF);
}
else
{
throw new InvalidOperationException(
SR.FilterGetValueMustBeStringOrDateTime);
}
// allocate an unmanaged PROPVARIANT to return
pNative = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(PROPVARIANT)));
// Per MSDN, AllocCoTaskMem never returns null: check for IntPtr.Zero instead.
Invariant.Assert(pNative != IntPtr.Zero);
// marshal the managed PROPVARIANT into the unmanaged block and return it
Marshal.StructureToPtr(v, pNative, false);
}
catch
{
if (pszVal != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(pszVal);
}
if (pNative != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(pNative);
}
throw;
}
return pNative;
}
#endregion Static members.
#region IFilter implementation
/// <summary>
/// Init
/// </summary>
/// <param name="grfFlags">usage flags</param>
/// <param name="cAttributes">length of aAttributes</param>
/// <param name="aAttributes">array of FULLPROPSPEC structs</param>
/// <returns>flags</returns>
public IFILTER_FLAGS Init(IFILTER_INIT grfFlags, // IFILTER_INIT value
uint cAttributes, // length of aAttributes
FULLPROPSPEC[] aAttributes) // restrict responses to the specified attributes
{
ManagedFullPropSpec[] managedArray = MarshalFullPropSpecArray(
cAttributes, aAttributes);
return _implementation.Init(grfFlags, managedArray);
}
/// <summary>
/// GetChunk
/// </summary>
/// <returns>the next chunk</returns>
public STAT_CHUNK GetChunk()
{
// Get the managed chunk
ManagedChunk managedChunk = _implementation.GetChunk();
if (managedChunk == null)
{
// End of chunks.
if (ThrowOnEndOfChunks)
{
// Throw exception.
throw new COMException(SR.FilterEndOfChunks,
(int)FilterErrorCode.FILTER_E_END_OF_CHUNKS);
}
// Return STAT_CHUNK with idChunk as 0.
STAT_CHUNK chunk = new STAT_CHUNK();
chunk.idChunk = 0;
return chunk;
}
// Valid chunk. Return corresponding STAT_CHUNK.
return MarshalChunk(managedChunk);
}
/// <summary>
/// GetText
/// </summary>
/// <param name="bufCharacterCount">Buffer size in Unicode characters (not bytes)</param>
/// <param name="pBuffer">Pre-allocated buffer for us to write into. String must be null-terminated.</param>
public void GetText(ref uint bufCharacterCount, IntPtr pBuffer)
{
// NOTE: bufCharacterCount and pBuffer are already validated by XpsFilter.
// In future, if this class is exposed publicly or used in a way other than
// through XpsFilter, proper checks needs to be present either here or in the caller.
// get the managed string and marshal
MarshalStringToPtr(_implementation.GetText((int)bufCharacterCount - 1),
ref bufCharacterCount, pBuffer);
}
/// <summary>
/// GetValue
/// </summary>
/// <returns>newly allocated PROPVARIANT structure</returns>
public IntPtr GetValue()
{
return MarshalPropVariant(_implementation.GetValue());
}
/// <summary>
/// BindRegion
/// </summary>
/// <param name="origPos"></param>
/// <param name="riid"></param>
/// <remarks>
/// The MSDN specification requires this function to return E_NOTIMPL for the time being.
/// </remarks>
public IntPtr BindRegion(FILTERREGION origPos, ref Guid riid)
{
// The following exception maps to E_NOTIMPL.
throw new NotImplementedException(SR.FilterBindRegionNotImplemented);
}
#endregion IFilter implementation
#region Properties
/// <summary>
/// If set to true, FILTER_E_END_OF_CHUNKS exception is thrown
/// by IFilter.GetChunk() on end of chunks.
/// If false, a STAT_CHUNK with idChunk as 0 is returned instead.
/// </summary>
internal bool ThrowOnEndOfChunks
{
get
{
return _throwOnEndOfChunks;
}
set
{
_throwOnEndOfChunks = value;
}
}
#endregion Properties
#region Fields
private IManagedFilter _implementation;
private bool _throwOnEndOfChunks = true;
#endregion Fields
}
#endregion IndexingFilterMarshaler
}
|