10

We have a C# T4 file named GenerateProxies.tt which calls several command-line codegen utilities. Using the System.Diagnostics Process class, we redirect the standard output to the T4 output text file (GenerateProxies.txt) so that we can review the command-line output for errors.

I added the following simple code to the end of the T4 so that Visual Studio will open the generated text file as the last step in the process (the workingDirectory variable is declared and populated earlier in the template). This does work but it throws a serialization error. Can this error be avoided?

<#@ 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
    );
#>

Again, this does work, it opens the text file, but it generates this error:

Running transformation: System.Runtime.Serialization.SerializationException:
Type 'Microsoft.VisualStudio.Platform.WindowManagement.DTE.WindowBase' in
Assembly 'Microsoft.VisualStudio.Platform.WindowManagement'
is not marked as serializable.
McGuireV10
  • 9,572
  • 5
  • 48
  • 64
  • The call stack might help. –  Dec 06 '16 at 19:38
  • Yeah, but unfortunately it's from a development VM that has no external access at all (which is why I didn't bother to retype all the assembly info, the key GUID and so on). The call stack is huge but it looks like some kind of PInvoke marshalling problem. Apparently DTE is COM. I sort of vaguely suspect it's a threading problem. – McGuireV10 Dec 07 '16 at 12:15
  • Smells to me like something is being accidentally pulled across an AppDomain border. The call stack could identify the source, and you could investigate, at the bottom of your code in the stack, who has a reference to an instance of that type. –  Dec 07 '16 at 14:01
  • I have a similar issue see my answer, comment does not allow that many chars – Tom Deloford Jan 28 '17 at 15:04

1 Answers1

28

The EnvDTE assemblies are COM interop assemblies. Your error can be avoided by creating a Runtime Callable Wrapper, which marshals calls to the COM object based off information in the interop-assembly. Microsoft has provided an extension method within the Microsoft.VisualStudio.TextTemplating namespace:

<#@ 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 templates run in a separate AppDomain, and I believe that is the reason your code is working despite the exception. IServiceProvider.GetService(typeof(DTE)) returns a transparent Proxy Object. This exception is because the proxy requires objects crossing an app domain be decorated with the Serializable attribute. You can confirm the DTE object in your code is a "transparent proxy" like this:

bool isProxy = RemotingServices.IsTransparentProxy(dte); 
FakeSaint
  • 492
  • 5
  • 8
  • 5
    Started getting the error `Running transformation: System.Runtime.Serialization.SerializationException: Type 'Microsoft.VisualStudio.ProjectSystem.VS.Implementation.Package.Automation.OAProjectItem' in Assembly 'Microsoft.VisualStudio.ProjectSystem.VS.Implementation, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' is not marked as serializable` in Visual Studio 2019 for a T4 template that worked just fine in VS 2017. Updating it to use the GetCOMService extension method as mentioned here fixed it! – Duncan Smart Mar 12 '19 at 22:53
  • Same experience as Duncan. I had to adjust the T4 templates after upgrading to VS2019. Curiously, it all worked when debugging, so this was a tad difficult to troubleshoot. I would like to know why they kept the .GetService() method around. It sort of implies there are scenarios when GetCOMService() won't work. – 9Rune5 Dec 12 '19 at 12:57