| File: System\Windows\Forms\ActiveX\AxHost.PropertyBagStream.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; using System.Formats.Nrbf; using System.Private.Windows.BinaryFormat; using System.Private.Windows.Nrbf; using System.Runtime.Serialization.Formatters.Binary; using Windows.Win32.System.Com; using Windows.Win32.System.Com.StructuredStorage; using Windows.Win32.System.Variant; namespace System.Windows.Forms; public abstract unsafe partial class AxHost { internal class PropertyBagStream : IPropertyBag.Interface { private readonly Hashtable _bag; internal PropertyBagStream() => _bag = []; internal PropertyBagStream(Stream stream) { long position = stream.Position; try { SerializationRecord rootRecord = stream.DecodeNrbf(); if (rootRecord.TryGetPrimitiveHashtable(out _bag!)) { return; } } catch (Exception ex) when (!ex.IsCriticalException()) { // Don't usually expect to fall into this case as we should usually not have anything // in the stream other than primitive VARIANTs, which are handled. If there are arrays or // interface pointers we'd hit this. Debug.WriteLine($"PropertyBagStream: {nameof(NrbfDecoder)} failed with {ex.Message}"); } try { stream.Position = position; #pragma warning disable SYSLIB0011 // Type or member is obsolete #pragma warning disable CA2300 // Do not use insecure deserializer BinaryFormatter #pragma warning disable CA2301 // Do not call BinaryFormatter.Deserialize without first setting BinaryFormatter.Binder #pragma warning disable CA2302 // Ensure BinaryFormatter.Binder is set before calling BinaryFormatter.Deserialize _bag = (Hashtable)new BinaryFormatter().Deserialize(stream); // CodeQL[SM03722, SM04191] : BinaryFormatter is intended to be used as a fallback for unsupported types. Users must explicitly opt into this behavior" } catch (Exception inner) when (!inner.IsCriticalException()) { Debug.Fail($"PropertyBagStream: {nameof(BinaryFormatter)} failed with {inner.Message}"); #pragma warning restore CA2300 #pragma warning restore CA2301 #pragma warning restore CA2302 #pragma warning restore SYSLIB0011 // Error reading. Just init an empty hashtable. _bag = []; } } HRESULT IPropertyBag.Interface.Read(PCWSTR pszPropName, VARIANT* pVar, IErrorLog* pErrorLog) { if (pVar is null || pszPropName.Value is null) { return HRESULT.E_POINTER; } string name = pszPropName.ToString(); if (!_bag.Contains(name)) { *pVar = default; return HRESULT.E_INVALIDARG; } object? value = _bag[name]; *pVar = VARIANT.FromObject(value); // The EE returns a VT_EMPTY for a null. The problem is that Visual Basic 6 expects the caller to respect // the "hint" it gives in the VariantType. For eg., for a VT_BSTR, it expects that the callee will null // out the BSTR field of the variant. Since the EE or us cannot do anything about this, we will return // a E_INVALIDARG rather than let Visual Basic 6 crash. return (*pVar).Equals(default(VARIANT)) ? HRESULT.E_INVALIDARG : HRESULT.S_OK; } HRESULT IPropertyBag.Interface.Write(PCWSTR pszPropName, VARIANT* pVar) { if (pVar is null || pszPropName.Value is null) { return HRESULT.E_POINTER; } string name = pszPropName.ToString(); object? value = pVar->ToObject(); _bag[name] = value; return HRESULT.S_OK; } internal void Save(Stream stream) { long position = stream.Position; try { BinaryFormatWriter.WritePrimitiveHashtable(stream, _bag); } catch (Exception ex) when (!ex.IsCriticalException()) { Debug.WriteLine($"PropertyBagStream.Save: {nameof(NrbfDecoder)} failed with {ex.Message}"); stream.Position = position; #pragma warning disable SYSLIB0011 // Type or member is obsolete new BinaryFormatter().Serialize(stream, _bag); #pragma warning restore SYSLIB0011 } } } }