EnvDTE not found in VS2012 works in VS2010 - t4

I'm using EnvDTE to do some code generation within my T4 Templates.
I have the code working correctly in Visual Studio 2010, however I've just started using Visual Studio 2012 and now when I try to run my templates I get the following error
Compiling transformation: Metadata file 'EnvDTE.dll' could not be found
I don't actually have a reference to EnvDTE in my project as its a Silverlight class library and I wasn't able to add the DLL, however it finds the DLL somehow.
I'm not sure what is difference is between 10 and 12 to cause this.
The following are my imports and assembly definitions from the start of my ttinclude file.
<## template debug="true" hostSpecific="true" #>
<## output extension=".generated.cs" #>
<## Assembly Name="EnvDTE.dll" #>
<## Assembly Name="System.Data" #>
<## import namespace="EnvDTE" #>
<## import namespace="System.Data" #>
<## import namespace="System.Data.SqlClient" #>
<## import namespace="System.IO" #>
<## import namespace="System.Text.RegularExpressions" #>
Is there anything I have to do differently to get it working for Visual Studio 2012

It appears that VS12 can't figure out where EnvDTE is. Its odd that (as you mentioned in a comment) fusion didn't pick that up. Perhaps it did, but you weren't reading it correctly?
As an aside, when the fusion log lets you down, its time to break out Process Monitor when you can't figure out why an application can't find something that should be there.
You can give a full path for assembly references in T4 templates. In your case, it would be
<## Assembly Name="C:\Program Files (x86)\Common Files\microsoft shared\MSEnv\PublicAssemblies\envdte.dll" #>
(assuming you have EnvDTE in the correct spot). I wouldn't consider this a true solution, and would open a Connect issue with MS about this. Seems like a bug.

After stumbling about the same error i searched a little deeper and found this Microsoft Connect entry.
To fix the problem simply remove the .dll from the assembly name and it works as expected:
<## Assembly Name="EnvDTE" #>
Also ensure that the EnvDTE assembly is located within the GAC under C:\Windows\assembly. This will normally automaticaly happen when you install Visual Studio on a machine.
Example
Here is an example that should work out of the box:
<## template language="C#" debug="true" hostSpecific="true" #>
<## output extension=".txt" #>
<## Assembly Name="System.Core" #>
<## Assembly Name="System.Design" #>
<## Assembly Name="System.Drawing" #>
<## Assembly Name="System.Windows.Forms" #>
<## Assembly Name="EnvDTE" #>
<## import namespace="System" #>
<## import namespace="System.CodeDom.Compiler" #>
<## import namespace="System.Collections.Generic" #>
<## import namespace="System.Drawing" #>
<## import namespace="System.IO" #>
<## import namespace="System.Linq" #>
<## import namespace="System.Resources" #>
<## import namespace="System.Resources.Tools" #>
<## import namespace="EnvDTE" #>
<## import namespace="Microsoft.CSharp" #>
All projects currently available within this solution:
<#
//System.Diagnostics.Debugger.Launch();
EnvDTE.DTE dte = (EnvDTE.DTE)((IServiceProvider)this.Host)
.GetService(typeof(EnvDTE.DTE));
EnvDTE.Projects projects = dte.Solution.Projects;
foreach (EnvDTE.Project project in projects)
{
#>
<#= project.Name #>
<#
}
#>
This file was generated at: <#= System.DateTime.Now.ToShortDateString() #> <#= DateTime.Now.ToLongTimeString() #>

I was facing the issue related to EnvDTE80 on my visual studio 2019 while loading an application.
Error displayed the following message:
"Reference.svcmap: Could not load file or assembly "'EnvDTE," Version=8.0.0.0, Culture=neutral..."
I cleaned the solution and installed the nuget package for version 8.0.0.0. Then rebuilt the solution. In that way my visual studio was able to load the application.

Related

VS2022 17.2.0 Preview 2.0: T4 template serialization exception when accessing projects, etc

Using VS2022 17.2.0 Preview 2.0 to generate data layer using T4 templates.
Part of the T4 uses VS interop / DTE to access projects in solution.
The following T4 is a test:
<## template debug="false" hostspecific="true" language="C#" #>
<## assembly name="Microsoft.VisualStudio.Shell.Interop"#>
<## import namespace="Microsoft.VisualStudio.Shell"#>
<## import namespace="Microsoft.VisualStudio.Shell.Interop"#>
<## import namespace="EnvDTE" #>
<## import namespace="EnvDTE80" #>
<## output extension=".txt" #>
<#
var hostServiceProvider = Host as IServiceProvider;
var dte = hostServiceProvider.GetService(typeof(DTE)) as DTE2;
foreach (Project project in dte.Solution)
{
#><#= project.Name #>
<#
}
#>
This produces following exception:
Error Running transformation: System.Runtime.Serialization.SerializationException: Type 'Microsoft.VisualStudio.CommonIDE.Solutions.CMiscProject' in Assembly 'Microsoft.VisualStudio.CommonIDE, Version=17.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' is not marked as serializable.
Issue did not exist in Preview 1.0 or in VS2019.
I have had a look around and pulled in nuget package for Microsoft.VisualStudio.Interop, version 17.1.32210.191, but problem persists when accessing anything through the EnvDTE.DTE(2).
I know I'm jumping the gun on this as it is a preview version, but has anyone had this issue and solved it? Is there a different approach needed to access projects in the solution from the T4 template?
The error does not occur when debugging the T4 template.
I had a bit of a play (and a lot of a Google) and found the following solved it for me under VS 2022:
Ensure you have the following assemblies and namespaces
<## assembly name="Microsoft.VisualStudio.Interop" #>
<## import namespace="EnvDTE" #>
<## import namespace="EnvDTE80" #>
<## import namespace="Microsoft.VisualStudio.TextTemplating" #>
and then swap out the IServiceProvider's GetService for GetCOMService
//var dte = hostServiceProvider.GetService(typeof(DTE)) as DTE2;
var dte = hostServiceProvider.GetCOMService(typeof(DTE)) as DTE2;
Mostly from answer here: https://stackoverflow.com/a/53346767/2797450

t4 template linq issue

How can I use Linq in a T4 template
This is my software environment information
vs2012
.net version 4.0
This is t4 template:
<## templatedebug="true" hostSpecific="true" #>
<## output extension=".cs" #>
<## Assembly Name="System.Core.dll" #>
<## import namespace="System" #>
<## import namespace="System.Linq" #>
When I call engine.ProcessTemplate(inputTemplate, host), it returns the contents of
ErrorGeneratingOutput. Why is that?
Old question I know, but I've just found the same thing.
When you reference System.Core, don't include .dll:
<## assembly name="System.Core" #>
You need to look at the error in the errors window of visual studio to see more information.
Also you can right click the .tt file and say debug template.

Can Conditional compilation symbols be used within T4 templates

I have a T4 template that is used with the TextTemplatingFilePreprocessor to generate a class that I can then use to generate the output of the template.
At the start of the T4 template I import several namespaces. E.g.
<## import namespace="Company.ProductX.Widgets" #>
<## import namespace="Company.ProductX.Services" #>
//...
I'd like to use Preprocessor Directives to switch out these imports with another set of namespaces (which provide the same interfaces but differing functionality to ProductX). E.g.
<#
#if(ProductX)
{
#>
<## import namespace="Company.ProductX.Widgets" #>
<## import namespace="Company.ProductX.Services" #>
//...
<#
}
#endif
#>
<#
#if(ProductY)
{
#>
<## import namespace="Company.ProductY.Widgets" #>
<## import namespace="Company.ProductY.Services" #>
//...
<#
}
#endif
#>
With the above example the imports seem to create the corresponding using statements in the class regardless of the preprocessor directive. E.g.
using Company.ProductX.Widgets
using Company.ProductX.Services
using Company.ProductY.Widgets
using Company.ProductY.Services
Is there another way to use Preprocessors in T4 templates to affect the template itself rather than just the template output?
In your example the preprocessor directive is injected into the generated output. What you could potentially do is having a ProductX.tt file that imports the correct namespace and uses <## include #> to include the template code.
Something like this (ProductX.tt):
<## import namespace="Company.ProductX.Widgets" #>
<## import namespace="Company.ProductX.Services" #>
<## include file="TheTemplateCode.ttinclude" #>
(ProductY.tt):
<## import namespace="Company.ProductY.Widgets" #>
<## import namespace="Company.ProductY.Services" #>
<## include file="TheTemplateCode.ttinclude" #>
I am not sure if this helps you but to be honest I am struggling a little bit with the use-case here.
New idea for an old question.
It might be possible to use a Custom T4 Text Template Directive Processor to pass through arbitrary code to the T4 output.
The custom directive processor would need to be registered on each machine to use it.

Missing Assemblies in T4 becomes GAC Hell?

I was trying to follow Oleg Sych's tutorials on T4 http://www.olegsych.com/2008/09/t4-tutorial-creatating-your-first-code-generator/, kinduva "Hello, Northwind!" but right away in the second step (at bottom) I got ten "missing assembly" errors like this
Error 1 Compiling transformation: The type >'Microsoft.SqlServer.Management.Sdk.Sfc.ISfcValidate' is defined in an assembly that is >not referenced. You must add a reference to assembly >'Microsoft.SqlServer.Management.Sdk.Sfc, Version=10.0.0.0, Culture=neutral, >PublicKeyToken=89845dcd8080cc91'.
I made sure all the sql server assemblies from (list below) are in %windir%\assembly (I think that’s the GAC), but no good. I added the assemblies to the visual-studio project; no good. I guess the only thing I can do is add absolute paths to the assemblies, but that is a HORRIBLE solution since I can’t share the solution with programmers who don’t have exactly the same absolute paths. Any ideas how to fix this, please & thanks?
<## template language="C#" #>
<## output extension="SQL" #>
<## assembly name="Microsoft.SqlServer.ConnectionInfo" #>
<## assembly name="Microsoft.SqlServer.Smo" #>
<## import namespace="Microsoft.SqlServer.Management.Smo" #>
<#
Server server = new Server();
Database database = new Database(server, "Northwind");
Table table = new Table(database, "Products");
table.Refresh();
#>
create procedure Products_Delete
#ProductID int
as
delete from Products
where ProductID = #ProductID
Here's stuff I put in the GAC
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.WmiEnum.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.ReportingServices.Interfaces.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.ConnectionInfo.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.ConnectionInfoExtended.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.Dmf.Adapters.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.Dmf.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.DmfSqlClrWrapper.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.Management.Collector.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.Management.CollectorEnum.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.Management.Sdk.Sfc.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.Management.Utility.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.Management.UtilityEnum.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.PolicyEnum.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.RegSvrEnum.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.ServiceBrokerEnum.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.Smo.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.SmoExtended.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.SqlEnum.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.SqlWmiManagement.dll"
"C:\Program Files\Microsoft SQL Server\100\SDK\Assemblies\Microsoft.SqlServer.SString.dll"
You can reference GACd assemblies by using the partial strongname (i.e. omitting the ".dll")
for example, use
<# assembly name="Microsoft.SqlServer.Management.Sdk.Sfc" #>
I was recieving the same problem, my fix was adding
<## assembly name="Microsoft.SqlServer.Management.Sdk.Sfc" #>
That seemed to work for me
Mine looks like this now
<## template language="C#v3.5" hostspecific="True" debug="True" #>
<## output extension="SQL" #>
<## include file="T4Toolbox.tt" #>
<## assembly name="Microsoft.SqlServer.ConnectionInfo" #>
<## assembly name="Microsoft.SqlServer.Management.Sdk.Sfc" #>
<## assembly name="Microsoft.SqlServer.Smo" #>
<## import namespace="Microsoft.SqlServer.Management.Smo" #>
<#
Server server = new Server(#"localhost\mssql2008");
Database database = new Database(server, "BreakAway");
Table table = new Table(database, "Contact");
table.Refresh();
#>
I had the same issue. I could resolve it adding some assemblies ...
<## template language="C#" Debug="true"#>
<## output extension="SQL" #>
<## assembly name="System.Xml" #>
<## assembly name="Microsoft.SqlServer.ConnectionInfo" #>
<## assembly name="Microsoft.SqlServer.Management.Sdk.Sfc" #>
<## assembly name="Microsoft.SqlServer.Smo" #>
<## import namespace="Microsoft.SqlServer.Management.Smo" #>
<#
Server server = new Server(#"SQLInstanceName");
Database database = new Database(server, "DBName");
Table table = new Table(database, "Countries");
table.Refresh();
#>
I ran into a similar problem, while trying to get started working with T4. Adding a reference to the Microsoft.SqlServer.Scripting NuGet package fixed the assembly-related issues I was running into.

How to open a file on relative path using T4?

I'm trying to run a T4 template that opens a XML file and uses it contents to generate a code artifact. However, I'm getting the an error message when I try to run a T4 template similar to the one below
<## template debug="false" hostspecific="false" language="C#" #>
<## assembly name="System.Xml.dll" #>
<## assembly name="System.Xml.Linq.dll" #>
<## import namespace="System.IO" #>
<## import namespace="System.Xml" #>
<## import namespace="System.Xml.Linq" #>
<## output extension=".cs" #>
namespace ConsoleApplication1
{
<# XElement fragment = XElement.Load("data.xml"); #>
...
Visual Studio 2010 error list is showing the following message
Running transformation: System.IO.FileNotFoundException: Could not find file 'C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\data.xml'.
It is trying to open the file on the path where the TextTemplateFileGenerator custom tool runs. I'd like it to open the file relative to my project path, because other developers on my team use different folder structures. Does anyone know if it is something possible to accomplish?
Change hostspecific option in template directive to "true" and call Host.ResolvePath("data.xml").
I had a similar problem but Host.ResolvePath didn't work for me because my relative path contained "..\.." in it. I worked around it by doing this:
string ttpath = this.Host.TemplateFile;
string resolvedPath = Path.GetFullPath(Path.GetDirectoryName(ttpath) + #"..\..\<Path To File>");

Resources