提交 c44d3f9e 编写于 作者: P Paul Harrington

Use Visual Studio's ComWrapperFactory if available.

上级 4b56f40e
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Interop
{
internal static class WrapperPolicy
{
private static int s_aggregatedObjectsCreatedSinceLastCleanup = 0;
private static void OnAggregatedObjectCreated()
[ComImport, Guid("436b402a-a479-41a8-a093-9713ce3ad111"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IComWrapperFactory
{
// The VS main thread disables eager COM object cleanup, which means that
// COM objects are only cleaned up when Marshal.CleanupUnusedObjectsInCurrentContext
// is called. The shell normally does this on idle, but in long lived
// code model operations, we end up creating enough code model objects that
// we can run out of memory before an idle+gc actually happens.
//
// To work around this, we track when we create aggregated objects (currently only)
// really used for the codemodel RCW's, and call
// Marshal.CleanupUnusedObjectsInCurrentContext periodically. This is safe because
// we're only cleaning things up for a given context, which means that there is no
// need to pump to get to the right context, so it shouldn't cause unexpected
// re-entrancy issues.
//
// The "1000" is completely arbitrary, except that it seems to work well when looking at
// http://vstfdevdiv:8080/WorkItemTracking/WorkItem.aspx?artifactMoniker=711863
if (s_aggregatedObjectsCreatedSinceLastCleanup++ == 1000)
{
Marshal.CleanupUnusedObjectsInCurrentContext();
s_aggregatedObjectsCreatedSinceLastCleanup = 0;
}
IComWrapper CreateAggregatedObject(object managedObject);
}
/// <summary>
/// Factory object for creating IComWrapper instances. Uses the Visual Studio implementation,
/// if available, or falls back to using our own implementation based on <see cref="BlindAggregatorFactory"/>
/// </summary>
private static readonly IComWrapperFactory s_ComWrapperFactory =
PackageUtilities.CreateInstance(typeof(IComWrapperFactory).GUID) as IComWrapperFactory
?? new ComWrapperFactory();
internal static object CreateAggregatedObject(object managedObject)
{
Contract.ThrowIfNull(managedObject, "managedObject");
return s_ComWrapperFactory.CreateAggregatedObject(managedObject);
}
private class ComWrapperFactory : IComWrapperFactory
{
private int _aggregatedObjectsCreatedSinceLastCleanup = 0;
// 1. Create our native COM object that will aggregate "managedObject"
var wrapperUnknown = BlindAggregatorFactory.CreateWrapper(); // AddRef'ed
private void OnAggregatedObjectCreated()
{
// The VS main thread disables eager COM object cleanup, which means that
// COM objects are only cleaned up when Marshal.CleanupUnusedObjectsInCurrentContext
// is called. The shell normally does this on idle, but in long lived
// code model operations, we end up creating enough code model objects that
// we can run out of memory before an idle+gc actually happens.
//
// To work around this, we track when we create aggregated objects (currently only)
// really used for the codemodel RCW's, and call
// Marshal.CleanupUnusedObjectsInCurrentContext periodically. This is safe because
// we're only cleaning things up for a given context, which means that there is no
// need to pump to get to the right context, so it shouldn't cause unexpected
// re-entrancy issues.
//
// The "1000" is completely arbitrary, except that it seems to work well when looking at
// http://vstfdevdiv:8080/WorkItemTracking/WorkItem.aspx?artifactMoniker=711863
try
if (_aggregatedObjectsCreatedSinceLastCleanup++ == 1000)
{
Marshal.CleanupUnusedObjectsInCurrentContext();
_aggregatedObjectsCreatedSinceLastCleanup = 0;
}
}
public IComWrapper CreateAggregatedObject(object managedObject)
{
// 2. Ask the CLR to create an object supporting aggregation for "managedObject"
var innerUnknown = Marshal.CreateAggregatedObject(wrapperUnknown, managedObject); // AddRef'ed
Contract.ThrowIfNull(managedObject, "managedObject");
// 1. Create our native COM object that will aggregate "managedObject"
var wrapperUnknown = BlindAggregatorFactory.CreateWrapper(); // AddRef'ed
try
{
// 3. Create a GC Handle to the managed object for later retrieval
var handle = GCHandle.Alloc(managedObject, GCHandleType.Normal);
var freeHandle = true;
// 2. Ask the CLR to create an object supporting aggregation for "managedObject"
var innerUnknown = Marshal.CreateAggregatedObject(wrapperUnknown, managedObject); // AddRef'ed
try
{
// 4. Now, link our native (aggregator) with the IUnknown of the CLR inner object, the
// GC Handle managed and the GC handle of the managed object.
// GC handle will be free by the native wrapper.
BlindAggregatorFactory.SetInnerObject(wrapperUnknown, innerUnknown, GCHandle.ToIntPtr(handle));
freeHandle = false;
}
finally
{
if (freeHandle)
// 3. Create a GC Handle to the managed object for later retrieval
var handle = GCHandle.Alloc(managedObject, GCHandleType.Normal);
var freeHandle = true;
try
{
handle.Free();
// 4. Now, link our native (aggregator) with the IUnknown of the CLR inner object, the
// GC Handle managed and the GC handle of the managed object.
// GC handle will be free by the native wrapper.
BlindAggregatorFactory.SetInnerObject(wrapperUnknown, innerUnknown, GCHandle.ToIntPtr(handle));
freeHandle = false;
}
finally
{
if (freeHandle)
{
handle.Free();
}
}
}
OnAggregatedObjectCreated();
OnAggregatedObjectCreated();
// 5. All done: Ask the CLR to create an RCW for the native aggregator
object wrapperRCW = Marshal.GetObjectForIUnknown(wrapperUnknown);
return (IComWrapper)wrapperRCW;
// 5. All done: Ask the CLR to create an RCW for the native aggregator
object wrapperRCW = Marshal.GetObjectForIUnknown(wrapperUnknown);
return (IComWrapper)wrapperRCW;
}
finally
{
Marshal.Release(innerUnknown);
}
}
finally
{
Marshal.Release(innerUnknown);
Marshal.Release(wrapperUnknown);
}
}
finally
{
Marshal.Release(wrapperUnknown);
}
}
/// <summary>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册