none
VS 2010 T4 - Assembly Relative Path, Inherits and VS Automation RRS feed

  • Question

  • Hi everyone,

    The documentation on MSDN says that relative paths are supported in the assembly parameter of T4 templates.  In the community content section, somebody was nice enough to mention that $(SolutionDir) is required for relative paths to work.

    I'm able to get the T4 engine to resolve my assembly using $(SolutionDir) in the path, but I'm running into another assembly binding issue that is related to VS automation.

    Here's an example of the template header that I'm using:

    <#@ template language="C#" hostSpecific="true" inherits="CustomNamespace.CustomTextTransformation" #>
    <#@ assembly name="$(SolutionDir)External References\CustomTextTemplating.dll" #>

    If I remove the inherits parameter then I can see that the assembly does in fact resolve successfully, although of course we don't get the output that we need.

    With the inherits parameter present and the fuslogvw program configured to log binding failures, the following is shown in the Error List in VS when attempting to save changes to the .tt file:

     

     


     

     

    Errors were generated when initializing the transformation object. The transformation will not be run. The following Exception was thrown:
    
    System.IO.FileNotFoundException: Could not load file or assembly 'CustomTextTemplating, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d1160779447de4a4' or one of its dependencies. The system cannot find the file specified.
    
    File name: 'CustomTextTemplating, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d1160779447de4a4'
     at System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
     at System.Reflection.RuntimeAssembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
     at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection, Boolean suppressSecurityChecks)
     at System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
     at System.Reflection.Assembly.Load(String assemblyString)
     at System.UnitySerializationHolder.GetRealObject(StreamingContext context)
     at System.IServiceProvider.GetService(Type serviceType)
     at CustomTextTemplating.VisualStudio.set_Host(ITextTemplatingEngineHost value) in C:\...\VisualStudio.cs:line 33
     at CustomTextTemplating.CustomTextTransformation.Initialize() in C:\...\CustomTextTransformation.cs:line 491
     at Microsoft.VisualStudio.TextTemplating.TransformationRunner.RunTransformation(TemplateProcessingSession session, String source, ITextTemplatingEngineHost host, String& result)
    
    Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
    
    Running under executable C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe
    
    --- A detailed error log follows. 
    
    === Pre-bind state information ===
    LOG: User = [name]
    LOG: DisplayName = CustomTextTemplating, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d1160779447de4a4
     (Fully-specified)
    LOG: Appbase = file:///C:/Program Files (x86)/Microsoft Visual Studio 10.0/Common7/IDE/
    LOG: Initial PrivatePath = NULL
    
    Calling assembly : (Unknown).
    
    ===
    LOG: This bind starts in default load context.
    LOG: Using application configuration file: C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe.Config
    LOG: Using host configuration file: 
    LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
    LOG: Post-policy reference: CustomTextTemplating, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d1160779447de4a4
    LOG: The same bind was seen before, and was failed with hr = 0x80070002.

     

     


     

     

    The code at line 33 in my VisualStudio.cs file is as follows:

    Environment = (DTE2) services.GetService(typeof(DTE));

    Does anybody know why VS cannot find my assembly even though the T4 engine can?

    If you need more info, please let me know.

    Using VS 2008, our developers have to manually execute a batch file to install our T4 assemblies into their GAC every time we change them.  This is error-prone and frustrating.  Now that we're moving to VS 2010, I'd like it very much if there's a way to use a relative path for an assembly and have VS automatically bind to it so that we can use it for automation as well.

    Thanks,
    Dave


    http://davesexton.com/blog
    Wednesday, June 23, 2010 12:20 PM

Answers

  • Hi everyone,

    The problem is that the transformation is running in a different AppDomain than Visual Studio, thus Visual Studio doesn't have my assembly loaded.

    Visual Studio is trying to load my assembly because of the following line of code:

    Environment = (DTE2) services.GetService(typeof(DTE));

    More specifically, it's typeof(DTE) that is causing Visual Studio to try to load my assembly.  My assembly is a .NET 4.0 assembly, and by default the reference to the automation assembly, envdte, was added with the NoPIA feature enabled.  This causes the compiler to embed the interop types of envdte into my assembly.  Therefore, typeof(DTE) is resolving to the DTE type in my assembly, which causes Visual Studio to require my assembly to be loaded to resolve the DTE type!

    The solution is simple:

    1. Open the References folder for my project (Visual Studio 2010, .NET 4.0).
    2. For each reference to an automation assembly; e.g., envdte, envdte80, vslangproj, vslangproj2, vslangproj80, etc...
    3. Select the reference and open the Properties window.
    4. Change the Embed Interop Types value to False.

    - Dave


    http://davesexton.com/blog
    • Marked as answer by Dave Sexton Thursday, June 24, 2010 1:23 PM
    Thursday, June 24, 2010 1:22 PM

All replies

  • Hi everyone,

    I've attached the debugger and can see that Assembly.Load is trying to find the assembly within the IDE path.  Here are a few examples from the fusion log:

    LOG: Attempting download of new URL file:///C:/Program Files (x86)/Microsoft Visual Studio 10.0/Common7/IDE/CustomTextTemplating.DLL.
    LOG: Attempting download of new URL file:///C:/Program Files (x86)/Microsoft Visual Studio 10.0/Common7/IDE/CustomTextTemplating/CustomTextTemplating.DLL.
    LOG: Attempting download of new URL file:///C:/Program Files (x86)/Microsoft Visual Studio 10.0/Common7/IDE/PublicAssemblies/CustomTextTemplating.DLL.
    ...

    I'm assuming the issue is that the assembly has already been loaded into a different context (i.e., the LoadFrom context).  I'm about to find out.  If so, I'll try adding a handler for AppDomain.AssemblyResolve to see if I can point it to the assembly that has already been loaded.

    - Dave


    http://davesexton.com/blog
    Thursday, June 24, 2010 12:44 PM
  • Hi everyone,

    The problem is that the transformation is running in a different AppDomain than Visual Studio, thus Visual Studio doesn't have my assembly loaded.

    Visual Studio is trying to load my assembly because of the following line of code:

    Environment = (DTE2) services.GetService(typeof(DTE));

    More specifically, it's typeof(DTE) that is causing Visual Studio to try to load my assembly.  My assembly is a .NET 4.0 assembly, and by default the reference to the automation assembly, envdte, was added with the NoPIA feature enabled.  This causes the compiler to embed the interop types of envdte into my assembly.  Therefore, typeof(DTE) is resolving to the DTE type in my assembly, which causes Visual Studio to require my assembly to be loaded to resolve the DTE type!

    The solution is simple:

    1. Open the References folder for my project (Visual Studio 2010, .NET 4.0).
    2. For each reference to an automation assembly; e.g., envdte, envdte80, vslangproj, vslangproj2, vslangproj80, etc...
    3. Select the reference and open the Properties window.
    4. Change the Embed Interop Types value to False.

    - Dave


    http://davesexton.com/blog
    • Marked as answer by Dave Sexton Thursday, June 24, 2010 1:23 PM
    Thursday, June 24, 2010 1:22 PM