File: CodeModel\CodeModelProjectCache.CacheEntry.cs
Web Access
Project: src\src\VisualStudio\Core\Impl\Microsoft.VisualStudio.LanguageServices.Implementation.csproj (Microsoft.VisualStudio.LanguageServices.Implementation)
// 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.
 
#nullable disable
 
using Microsoft.VisualStudio.LanguageServices.Implementation.Interop;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel;
 
internal sealed partial class CodeModelProjectCache
{
    private readonly struct CacheEntry
    {
        // NOTE: The logic here is a little bit tricky.  We can't just keep a WeakReference to
        // something like a ComHandle, since it's not something that our clients keep alive.
        // instead, we keep a weak reference to the inner managed object, which we know will
        // always be alive if the outer aggregate is alive.  We can't just keep a WeakReference
        // to the RCW for the outer object either, since in cases where we have a DCOM or native
        // client, the RCW will be cleaned up, even though there is still a native reference
        // to the underlying native outer object.
        //
        // Instead we make use of an implementation detail of the way the CLR's COM aggregation 
        // works.  Namely, if all references to the aggregated object are released, the CLR 
        // responds to QI's for IUnknown with a different object.  So, we store the original
        // value, when we know that we have a client, and then we use that to compare to see
        // if we still have a client alive.
        //
        // NOTE: This is _NOT_ AddRef'd.  We use it just to store the integer value of the
        // IUnknown for comparison purposes.
        private readonly WeakComHandle<EnvDTE80.FileCodeModel2, FileCodeModel> _fileCodeModelWeakComHandle;
 
        public CacheEntry(ComHandle<EnvDTE80.FileCodeModel2, FileCodeModel> handle)
            => _fileCodeModelWeakComHandle = new WeakComHandle<EnvDTE80.FileCodeModel2, FileCodeModel>(handle);
 
        public EnvDTE80.FileCodeModel2 FileCodeModelRcw
        {
            get
            {
                return _fileCodeModelWeakComHandle.ComAggregateObject;
            }
        }
 
        internal bool TryGetFileCodeModelInstanceWithoutCaringWhetherRcwIsAlive(out FileCodeModel fileCodeModel)
            => _fileCodeModelWeakComHandle.TryGetManagedObjectWithoutCaringWhetherNativeObjectIsAlive(out fileCodeModel);
 
        public ComHandle<EnvDTE80.FileCodeModel2, FileCodeModel>? ComHandle
        {
            get
            {
                return _fileCodeModelWeakComHandle.ComHandle;
            }
        }
    }
}