none
Why does IDebugProperty2::EnumChildren() pass a non-writable ref Guid arg? RRS feed

  • Question

  • Hi folks,

    I'm not sure if the forum is the right place to post this issue but here goes.

    It appears that Visual Studio 2015 passes a memory protected ref Guid argument in to the IDebugProperty2::EnumChildren() method.  This presents itself as an AccessViolationException if I merely try to write to the guidFilter argument.

    This may not seem like a big deal because there doesn't seem to be any reason that an IDebugProperty2 implementation would want to write to this argument but it is causing me BIG headaches because we are using an Aspect Oriented Framework that has to be general, thus it always writes back any ref arguments.

    Minimum repro:

    int EnumChildren ( 
       enum_DEBUGPROP_INFO_FLAGS   dwFields,
       uint                        dwRadix,
       ref Guid                    guidFilter,
       uint                        dwAttribFilter,
       string                      pszNameFilter,
       uint                        dwTimeout,
       out IEnumDebugPropertyInfo2 ppEnum
    )
    {
      // This results in an AccessViolationException.
      guidFilter = Guid.NewGuid();
    }

    I haven't had a chance to see if this is a problem in later version of Visual Studio but I'm assuming it is.

    Can someone please:

    • advise if this is the forum is the right spot to raise such an issue?
    • advise if/when this bug may be fixed?

    Thanks,

    Ben

    Tuesday, August 20, 2019 1:47 PM

All replies

  • Hi Ben,

    Unfortunately, the definition of the IDebugProperty2::EnumChildren is pretty much set it stone. IDebugProperty2 is a COM interface (defined in the msdbg.idl file included with the VSSDK), and per COM rules cannot be changed once published. This particular interface is passed to many other interface methods defined through the Visual Studio SDK, effectively making it impossible to change. 

    Please note this isn't a bug. The interface is defined as:

    [

    object,
    uuid(a7ee3e7e-2dd2-4ad7-9697-f4aae3427762),
    pointer_default(unique)
    ]
    interface IDebugProperty2: IUnknown
    {
    // Get the DEBUG_PROPERTY_INFO that describes this property
    HRESULT GetPropertyInfo(
    [in] DEBUGPROP_INFO_FLAGS dwFields,
    [in] DWORD dwRadix,
    [in] DWORD dwTimeout,
    [in, ptr, size_is(dwArgCount), length_is(dwArgCount)] IDebugReference2** rgpArgs,
    [in] DWORD dwArgCount,
    [out] DEBUG_PROPERTY_INFO* pPropertyInfo);

    // Set the value of this property
    HRESULT SetValueAsString(
    [in] LPCOLESTR pszValue,
    [in] DWORD dwRadix,
    [in] DWORD dwTimeout);

    // Set the value of this property
    HRESULT SetValueAsReference(
    [in, ptr, size_is(dwArgCount), length_is(dwArgCount)] IDebugReference2** rgpArgs,
    [in] DWORD dwArgCount,
    [in] IDebugReference2* pValue,
    [in] DWORD dwTimeout);

    // Enum the children of this property
    HRESULT EnumChildren(
    [in] DEBUGPROP_INFO_FLAGS dwFields,
    [in] DWORD dwRadix,
    [in] REFGUID guidFilter,
    [in] DBG_ATTRIB_FLAGS dwAttribFilter,
    [in, ptr] LPCOLESTR pszNameFilter,
    [in] DWORD dwTimeout,
    [out] IEnumDebugPropertyInfo2** ppEnum);

    // Get the parent of this property
    HRESULT GetParent(
    [out] IDebugProperty2** ppParent);

    // Get the property that describes the derived most property of this property
    HRESULT GetDerivedMostProperty(
    [out] IDebugProperty2** ppDerivedMost);

    // Get the memory bytes that contains this property
    HRESULT GetMemoryBytes(
    [out] IDebugMemoryBytes2** ppMemoryBytes);

    // Get a memory context for this property within the memory bytes returned by GetMemoryBytes
    HRESULT GetMemoryContext(
    [out] IDebugMemoryContext2** ppMemory);

    // Get the size (in bytes) of this property
    HRESULT GetSize(
    [out] DWORD* pdwSize);

    // Get a reference for this property
    HRESULT GetReference(
    [out] IDebugReference2** ppReference);

    // Get extended info for this property
    HRESULT GetExtendedInfo(
    [in] REFGUID guidExtendedInfo,
    [out] VARIANT* pExtendedInfo);
    };

    Where REFGUID is defined as:

       #define REFGUID const GUID * const  

    So short answer is, this will likely never change. You will probably need to modify your framework or scenario to better accommodate const parameters like the REFGUID above.

    Sincerely,



    Ed Dore

    Wednesday, August 21, 2019 4:53 PM
    Moderator
  • Hi Ed,

    Thanks for the reply.  I am slightly confused though because when I navigate to the C# definition of IDebugProperty2 I get the following where you can see the type is ref Guid guidFilter, whereas the COM definition as you point out is [in] REFGUID guidFilter (or const GUID * const).  It would seem to me that there is a bug with the C# definition, no?  Should the COM type const GUID * const really map to the C# type ref Guid?

    #region Assembly Microsoft.VisualStudio.Debugger.InteropA, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
    // C:\Program Files (x86)\Microsoft Visual Studio 14.0\VSSDK\VisualStudioIntegration\Common\Assemblies\v2.0\Microsoft.VisualStudio.Debugger.InteropA.dll
    #endregion
    
    using System;
    using System.Runtime.InteropServices;
    
    namespace Microsoft.VisualStudio.Debugger.Interop
    {
        [Guid("A7EE3E7E-2DD2-4AD7-9697-F4AAE3427762")]
        [InterfaceType(1)]
        public interface IDebugProperty2
        {
            int EnumChildren([ComAliasName("Microsoft.VisualStudio.Debugger.Interop.DEBUGPROP_INFO_FLAGS")] enum_DEBUGPROP_INFO_FLAGS dwFields, uint dwRadix, ref Guid guidFilter, [ComAliasName("Microsoft.VisualStudio.Debugger.Interop.DBG_ATTRIB_FLAGS")] enum_DBG_ATTRIB_FLAGS dwAttribFilter, string pszNameFilter, uint dwTimeout, out IEnumDebugPropertyInfo2 ppEnum);
            int GetDerivedMostProperty(out IDebugProperty2 ppDerivedMost);
            int GetExtendedInfo(ref Guid guidExtendedInfo, out object pExtendedInfo);
            int GetMemoryBytes(out IDebugMemoryBytes2 ppMemoryBytes);
            int GetMemoryContext(out IDebugMemoryContext2 ppMemory);
            int GetParent(out IDebugProperty2 ppParent);
            int GetPropertyInfo([ComAliasName("Microsoft.VisualStudio.Debugger.Interop.DEBUGPROP_INFO_FLAGS")] enum_DEBUGPROP_INFO_FLAGS dwFields, uint dwRadix, uint dwTimeout, IDebugReference2[] rgpArgs, uint dwArgCount, [ComAliasName("Microsoft.VisualStudio.Debugger.Interop.DEBUG_PROPERTY_INFO")] DEBUG_PROPERTY_INFO[] pPropertyInfo);
            int GetReference(out IDebugReference2 ppReference);
            int GetSize(out uint pdwSize);
            int SetValueAsReference(IDebugReference2[] rgpArgs, uint dwArgCount, IDebugReference2 pValue, uint dwTimeout);
            int SetValueAsString(string pszValue, uint dwRadix, uint dwTimeout);
        }
    }

    Thanks,

    Ben

    Thursday, August 22, 2019 9:40 AM
  • I suspect it's because ref Guid is probably the closest construct you can get with managed code. A lot of those interfaces were defined before C# was even a thing :-).

    Sincerely,


    Ed Dore

    Saturday, August 24, 2019 8:18 AM
    Moderator
  • In C# 7.2, they added the in modifier for parameters. Its metadata representation uses a reference with ReadOnlyAttribute and in some cases modreq[InAttribute]. Perhaps this can eventually lead to TLBIMP being changed to emit the same, and then the AOP framework could detect those.

    Until then, is it possible to give the AOP framework a set of overrides so that it does not rely solely on how the parameters were originally defined? Something like XmlAttributeOverrides for XmlSerializer.

    Thursday, August 29, 2019 11:14 PM