You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VS2022扩展调用IVsObjectList2.GetText崩溃,如何阻止CLR自动释放内存?

IVsObjectList2.GetText调用致VS崩溃:封送错误分析与修复方案

问题背景

在C#开发Visual Studio 2022扩展时,调用COM接口IVsObjectList2.GetText后出现堆损坏导致的VS崩溃,根源在于官方互操作代码的封送定义与原生COM接口规范冲突。

接口定义对比

原生COM接口规范

根据官方文档,GetText的第三个参数ppszText由接口实现方分配内存,调用者仅需保留引用,绝对不能释放,原生定义如下:

HRESULT IVsObjectList2::GetText(  
   [in] ULONG Index,   
   [in] VSTREETEXTOPTIONS tto,   
   [out] const WCHAR **ppszText  
);

官方C#互操作定义问题

反编译Visual Studio提供的互操作代码后,发现GetText的定义存在错误:

[MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
int GetText(
  [In][ComAliasName("Microsoft.VisualStudio.OLE.Interop.ULONG")] uint index,
  [In][ComAliasName("Microsoft.VisualStudio.Shell.Interop.VSTREETEXTOPTIONS")] VSTREETEXTOPTIONS tto,
  [MarshalAs(UnmanagedType.LPWStr)] out string ppszText);

使用[MarshalAs(UnmanagedType.LPWStr)]标记out string参数时,CLR会自动调用CoTaskMemFree释放该字符串内存,这完全违反了原生接口“不能释放”的要求,最终导致堆损坏崩溃——你的调用栈中RtlValidateHeap和RtlFreeHeap的报错也验证了这一点。

修复方案:自定义接口封送

可以通过重新定义接口并修改参数封送方式解决问题,具体步骤如下:

1. 自定义IVsObjectList2接口

在你的代码中重新定义接口,将第三个参数改为IntPtr,避免CLR自动释放内存。注意必须保证接口GUID和方法顺序与原生接口完全一致(COM接口依赖VTable顺序,不能出错):

[ComImport]
[Guid("B64D79E0-243B-4B0B-B97A-5E5D0A13434A")] // IVsObjectList2的官方GUID
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICustomIVsObjectList2
{
    // 按原生接口顺序实现所有方法,此处仅示例GetText
    int GetItemCount(out uint pCount);
    // ... 其他方法省略,需与原生IVsObjectList2方法顺序完全匹配
    [MethodImpl(MethodImplOptions.PreserveSig | MethodImplOptions.InternalCall)]
    int GetText(
        [In] uint index,
        [In] VSTREETEXTOPTIONS tto,
        out IntPtr ppszText);
    // ... 其他方法省略
}

2. 转换接口并调用

将获取到的IVsObjectList2实例转换为自定义接口,手动将IntPtr转换为字符串,避免自动释放:

IVsObjectManager2 objectManager2 = MyExtensionPackage.GetGlobalService(typeof(SVsObjectManager)) as IVsObjectManager2;
if (objectManager2.FindLibrary(new Guid(BrowseLibraryGuids80.VC), out IVsLibrary2 lib) == VSConstants.S_OK) {
  var criteria = new VSOBSEARCHCRITERIA2();
  if (lib.GetList2(
        (uint)_LIB_LISTTYPE.LLT_CLASSES, 
        (uint)_LIB_LISTFLAGS.LLF_DONTUPDATELIST, 
        new[] { criteria }, 
        out IVsObjectList2 list) == VSConstants.S_OK
      && list != null) {
    if (list.GetItemCount(out uint count) == VSConstants.S_OK && count > 0) {
        var customList = list as ICustomIVsObjectList2;
        if (customList != null)
        {
            for (uint idx = 0; idx < count; ++idx) {
                if (customList.GetText(idx, VSTREETEXTOPTIONS.TTO_PREFIX, out IntPtr textPtr) == VSConstants.S_OK)
                {
                    // 手动转换字符串,不触发自动释放
                    string prefix = Marshal.PtrToStringUni(textPtr);
                    // 使用prefix
                }
            }
        }
    }
  }
}

此方案完全遵循原生COM接口的内存管理规则,可彻底解决堆损坏导致的VS崩溃问题。


内容的提问来源于stack exchange,提问作者Sedenion

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 07:24:57