我正在用C#构建一个Visual Studio2022扩展,目前正在使用VS API.我正在试着拨打IVsObjectList2.GetText(),这是一个COM接口.然而,VS在try 这样做时崩溃了.我怀疑VS API中的编组定义是错误的.

documentation个声明调用方应该释放GetText()(字符串)的第三个参数NOT.即本机接口被定义为

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

GetText()的调用者不能释放ppszText.然而,在C#代码的反编译过程中,我可以看到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);

据我所知,default marshalling behavior导致CLR自动释放字符串参数ppszText.我的理解正确吗?

如果我是正确的,因为这与API定义不一致,我可以以某种方式绕过这个问题吗?我的意思是,我能以某种方式在我自己的代码中纠正GetText()的编组定义吗?(我认为第三个参数should be an IntPtr instead?)例如,如果这是C++,我可能会对自己的定义做一个IVsObjectList2的"represtrate_cast",然后这个定义是正确的;在C#中这样的事情可能吗?

调用C#代码:

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) {
      for (uint idx = 0; idx < count; ++idx) {
        // The following crashes eventually.
        list.GetText(idx, VSTREETEXTOPTIONS.TTO_PREFIX, out string prefix);
        // Use "prefix"
      }
    }
  }
}

list.GetText()调用不会立即崩溃,但最终会崩溃,因为下面的调用堆栈出现了一些堆损坏:

ntdll.dll!RtlpBreakPointHeap()
ntdll.dll!RtlpValidateHeapEntry()
ntdll.dll!RtlValidateHeap()
AcLayers.dll!NS_FaultTolerantHeap::APIHook_RtlFreeHeap(void *,unsigned long,void *)
mscorlib.ni.dll!00007ffe31eb071f()
Microsoft.VisualStudio.Interop.ni.dll!00007ffe2d8bfc53()
Microsoft.VisualStudio.Interop.ni.dll!00007ffe2d8bfbe7()
[Managed to Native Transition]
My own code in C#, line with GetText()

推荐答案

我的意思是,我可以在自己的代码中以某种方式纠正GetText()的编组定义吗?

在.NET中通常可以做的一件事是,您可以使用相同的GUID属性和相同的方法顺序定义您自己的IVsObjectList2接口副本,然后将对象强制转换为您的接口.这将允许您使用更合适的签名重新定义GetText方法.不幸的是,这可能是一种痛苦,因为您需要小心地做一些事情,比如保持接口成员与底层IDL的顺序相同.

Csharp相关问答推荐

Microsoft.AspNetCore.Mvc. Controller Base.用户:属性或索引器Controller Base.用户无法分配给--它是只读的

如何从顶部提取发票号作为单词发票后的第一个匹配

需要深入了解NpgSQL DateTimeOffset处理

ASP.NET Core:如何在IPageFilter中注入ApplicationDbContext

在FilePath中搜索一个词,并根据First Match从左到右提取文件路径

使用C#中的SDK在Microsoft Graph API上使用SubscribedSkus的问题

创建临时Collection 最有效的方法是什么?堆栈分配和集合表达式之间的区别?

使用C#HttpClient以多部分形式数据发送带有非ASCII文件名的文件的问题

在具有不同属性名称的两个类之间创建关系

C# CompareTo()和Compare()可以返回除-1和1以外的整数吗?

在.NET 7.0 API控制器项目中通过继承和显式调用基类使用依赖项注入

HttpClient SendAsync与多线程环境中的ArchiveZip

Google OAuth令牌交换在.Net中不起作用

当空判断结果赋给变量时,为什么会出现可能空异常警告的解引用?

使用switch 类型模式时出现奇怪的编译器行为

将FileStream的特定部分作为字节数组读取

在.NET8中如何反序列化为私有字段?

如何在C#中从MongoDB IPipelineStageDefinition中获取聚合命令的字段/选项?

如何在Cake脚本中设置MSBuild.exe的绝对路径

读取测试项目中的应用程序设置