Visual Studio T4 使用DTE 打开生成的文件时出现序列化错误

Visual Studio serialization error when T4 uses DTE to open generated file

我们有一个名为 GenerateProxies.tt 的 C# T4 文件,它调用了几个命令行代码生成实用程序。使用 System.Diagnostics 进程 class,我们将标准输出重定向到 T4 输出文本文件 (GenerateProxies.txt),以便我们可以检查命令行输出是否有错误。

我在 T4 的末尾添加了以下简单代码,以便 Visual Studio 将打开生成的文本文件作为该过程的最后一步(workingDirectory 变量已在前面声明和填充在模板中)。这确实有效,但会引发序列化错误。这个错误可以避免吗?

<#@ assembly name="EnvDTE" #>
<#@ import namespace="EnvDTE" #>
<#
    IServiceProvider vssp = (IServiceProvider)this.Host;
    DTE dte = vssp.GetService(typeof(DTE)) as DTE;
    dte.ItemOperations.OpenFile(
        string.Format(@"{0}\GenerateProxies.txt", workingDirectory),
        Constants.vsViewKindTextView
    );
#>

同样,这个 确实 工作,它打开了文本文件,但它生成了这个错误:

Running transformation: System.Runtime.Serialization.SerializationException:
Type 'Microsoft.VisualStudio.Platform.WindowManagement.DTE.WindowBase' in
Assembly 'Microsoft.VisualStudio.Platform.WindowManagement'
is not marked as serializable.

这不是答案,但是 OP 无法提供评论中要求的堆栈跟踪。

当我尝试在我的 tt 文件中执行一个函数以写入输出时,我抛出了类似的异常 window(ST 太长,无法评论)

private void WriteToOutput(string output)
{
      if (_host == null)
        throw new Exception("Host property returned unexpected value (null)");

      EnvDTE.DTE dte = (EnvDTE.DTE)_host.GetService(typeof(EnvDTE.DTE));

      if (dte == null)
        throw new Exception("Unable to retrieve DTE");

      Window window = dte.Windows.Item(EnvDTE.Constants.vsWindowKindOutput);
      window.Activate();

      var outputWindow = (EnvDTE.OutputWindow) window.Object;
      outputWindow.ActivePane.Activate();

      outputWindow.ActivePane.OutputString(output);
      outputWindow.ActivePane.OutputString("\n");
    }

Severity Code Description Project File Line Suppression State Error Running transformation: System.Runtime.Serialization.SerializationException: Type 'Microsoft.VisualStudio.Platform.WindowManagement.DTE.Windows' in Assembly 'Microsoft.VisualStudio.Platform.WindowManagement, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' is not marked as serializable.

Server stack trace: at System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type) at System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context) at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo() at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder) at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder) at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
at System.Runtime.Remoting.Channels.CrossAppDomainSerializer.SerializeMessageParts(ArrayList argsToSerialize) at System.Runtime.Remoting.Messaging.SmuggledMethodReturnMessage..ctor(IMethodReturnMessage mrm) at System.Runtime.Remoting.Messaging.SmuggledMethodReturnMessage.SmuggleIfPossible(IMessage msg) at System.Runtime.Remoting.Channels.CrossAppDomainSink.DoDispatch(Byte[] reqStmBuff, SmuggledMethodCallMessage smuggledMcm, SmuggledMethodReturnMessage& smuggledMrm) at System.Runtime.Remoting.Channels.CrossAppDomainSink.DoTransitionDispatchCallback(Object[] args)

Exception rethrown at [0]: at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) at EnvDTE._DTE.get_Windows() at Microsoft.VisualStudio.TextTemplating8CBB5A87F4A34D52835396F51C533E1D8E9F22BC6977A9510B46C012D01E08C8AD263AC5BA030600D92BC0F39E7F1C3B6AA67D8CE545627E10A7F993E06C0D02.GeneratedTextTransformation.TypeMapper.WriteToOutput(String output) in C:\ViewModels\BaseGridViewModels\BaseViewModels.tt:line 581 at Microsoft.VisualStudio.TextTemplating8CBB5A87F4A34D52835396F51C533E1D8E9F22BC6977A9510B46C012D01E08C8AD263AC5BA030600D92BC0F39E7F1C3B6AA67D8CE545627E10A7F993E06C0D02.GeneratedTextTransformation.TypeMapper.GetItemsToGenerate[T](IEnumerable`1 itemCollection) in C:\ViewModels\BaseGridViewModels\BaseViewModels.tt:line 566 at Microsoft.VisualStudio.TextTemplating8CBB5A87F4A34D52835396F51C533E1D8E9F22BC6977A9510B46C012D01E08C8AD263AC5BA030600D92BC0F39E7F1C3B6AA67D8CE545627E10A7F993E06C0D02.GeneratedTextTransformation.TransformText() in C:\ViewModels\BaseGridViewModels\BaseViewModels.tt:line 33 C:\ViewModels\BaseGridViewModels\BaseViewModels.tt 581

EnvDTE 程序集是 COM 互操作程序集。通过创建 Runtime Callable Wrapper 可以避免您的错误,它根据互操作程序集中的信息编组对 COM 对象的调用。微软在 Microsoft.VisualStudio.TextTemplating 命名空间:

<#@ template hostspecific="true" language="C#" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#
  IServiceProvider serviceProvider = (IServiceProvider)this.Host;
  EnvDTE.DTE dte = (EnvDTE.DTE) serviceProvider.GetCOMService(typeof(EnvDTE.DTE));
 #>

T4 模板 运行 在一个单独的 AppDomain 中,我相信这就是您的代码尽管存在异常但仍能正常工作的原因。 IServiceProvider.GetService(typeof(DTE))returns一个透明的Proxy Object。此异常是因为代理要求跨应用程序域的对象使用 Serializable 属性进行修饰。您可以确认代码中的 DTE 对象是 "transparent proxy",如下所示:

bool isProxy = RemotingServices.IsTransparentProxy(dte);