- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在自定义工具窗口中使用IVsInvisibleEditor将t4文件加载到托管vs编辑器中。我调用传入t4文件的IVsInvisibleEditorManager.RegisterInvisibleEditor方法,就像here一样。然后,我使用GetDocData方法获取文件内容,然后将其设置为编辑器的缓冲区。我通过将getdocdata的结果转换为IVsPersistDocData实例来保存编辑器中的更改,然后调用save方法。关闭工具窗口后,我尝试通过在IVsPersistDocData实例上调用close来清理资源。当我尝试再次打开同一文件的工具窗口时,尝试再次在不可见的编辑器上调用getdocdata时出现异常。如果我不调用IVsPersistDocData的关闭消息,它将起作用。如何正确关闭所有这些资源(IVsInvisibleEditor,IVsInvisibleEditorManager,IVsPersistDocData),以便在尝试再次使用它们时不会出现异常?
最佳答案
IVsInvisibleEditor
上没有Close方法,因为它仅使用COM引用计数:当对象获取它时,它是对IUnknown.Release()的最终调用,它使用该提示来关闭基础文件。如果您使用C++编写扩展程序,那么这很容易:只要确保将其发布就可以了。但是我猜您是用托管代码编写的,这要困难得多。 CLR使处理此类对象变得很痛苦。我将假设您不是COM编组专家,因此我为冗长的讨论深表歉意,但是了解所有这些工作原理非常重要。
背景:每当您尝试使用托管代码中的COM对象时,CLR都会创建所谓的“运行时可调用包装程序”或RCW。这是一个小的托管对象,是本地对象的包装。在内部,它保留IUnknown指针,并“拥有”该对象的AddRef / Release。这个想法是当托管代码不再使用RCW时,RCW会收集垃圾,当这种情况发生时,CLR然后在基础对象上调用Release()。
当您调用IVsInvisibleEditorManager.RegisterInvisibleEditor
时,VS中的本机代码会将指向该对象的指针移回托管代码。然后,CLR将对象包装在RCW中,这意味着,除非我们对Marshal.ReleaseComObject
采取特殊步骤,否则不可见的编辑器将漂浮在周围,直到GC确定RCW消失了,是时候释放它了。不是您所需要的。
因此,只需调用Marshal.ReleaseComObject,然后完成,对吗?错误!通常,使用Marshal.ReleaseComObject should be considered dangerous是因为CLR在此还有另一个棘手的行为。假设您要打开文件的不可见编辑器,而在打开文件的同时,Visual Studio中的另一个组件也将打开相同的文件,并且管理器将IVsInvisibleEditor
的本机实例交还给您。您已经拥有本机对象实例的RCW。对于其他组件,CLR会“加油!其他人已经有了该对象”,并将与您相同的RCW交给他们。如果他们调用Marshal.ReleaseComObject
并销毁了COM对象,则意味着您手头的对象刚刚被僵尸化。这就是ReleaseComObject
危险的原因:只有知道自己是唯一持有该RCW的人,您才能调用它,但是默认情况下,CLR在需要此RCW的任何人之间共享RCW。
这就是为什么您不能从托管代码中正确使用IVsInvisibleEditorManager
的原因:调用RegisterInvisibleEditor
时,您将获得共享的RCW。您不能在不破坏其他人的情况下调用`ReleaseComObject。要获得独特的RCW,没有简单的方法。
正确解决此问题的第一步是为IVsInivisibleEditorManager
定义我们自己的接口。这就是我们在托管VS代码的某些部分中定义它的方式:
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("14439CDE-B6CF-4DD6-9615-67E8B3DF380D")]
internal interface IIntPtrReturningVsInvisibleEditorManager
{
int RegisterInvisibleEditor(
[MarshalAs(UnmanagedType.LPWStr)] string pszMkDocument,
IVsProject pProject,
uint dwFlags,
IVsSimpleDocFactory pFactory,
out IntPtr ppEditor);
}
IVsInvisibleEditorManager
接口,然后将其转换为自己的接口。之所以可行,是因为将RCW强制转换为COM接口是神奇的:只要基础对象说它支持该接口(由指定的GUID查找),然后CLR会伪造它并说RCW可以强制转换为该接口-甚至你定义了自己。然后,您可以调用
RegisterInvisibleEditor
并获取一个IntPtr,然后为其创建唯一的RCW。这是获取文档数据的代码:
var invisibleEditorManager = (IIntPtrReturningVsInvisibleEditorManager)serviceProvider.GetService(typeof(SVsInvisibleEditorManager));
var invisibleEditorPtr = IntPtr.Zero;
Marshal.ThrowExceptionForHR(invisibleEditorManager.RegisterInvisibleEditor(filePath, null, 0, null, out invisibleEditorPtr));
try
{
this.invisibleEditor = (IVsInvisibleEditor)Marshal.GetUniqueObjectForIUnknown(invisibleEditorPtr);
var docDataPtr = IntPtr.Zero;
Marshal.ThrowExceptionForHR(invisibleEditor.GetDocData(fEnsureWritable: 0, riid: typeof(IVsTextLines).GUID, ppDocData: out docDataPtr));
try
{
var docData = Marshal.GetObjectForIUnknown(docDataPtr);
// use docData how you want, probably by getting the text of it
}
finally
{
Marshal.Release(docDataPtr);
}
}
finally
{
// Since we have a unique RCW holding onto the object, we must release our direct pointer as well
Marshal.Release(invisibleEditorPtr);
}
Marshal.ReleaseComObject(this.invisibleEditor)
关于editor - 使用IVSInvisibleEditor和IVSPersistDocData,但如何释放它们?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25406780/
我是一名优秀的程序员,十分优秀!