Question

I'm trying to load an assembly in a T4 template. While it works perfectly fine when executing the transforms from MSBuild, the T4 engine reports that it cannot find the assembly when running from Visual Studio (using the Build -> Transform All T4 templates menu action or when using transform on save).

I've set it up as recommended in the documentation:

  <PropertyGroup>
    <BuildSystemLibrary>$(SolutionDir)src\Csw.BuildSystem\bin\Release\Csw.BuildSystem.dll</BuildSystemLibrary>
  </PropertyGroup>
  <ItemGroup>
    <T4ParameterValues Include="BuildSystemLibrary">
      <Value>$(BuildSystemLibrary)</Value>
      <Visible>False</Visible>
    </T4ParameterValues>
  </ItemGroup>

Well, with the exception of using SolutionDir instead of MSBuildProjectDirectory, the latter being unavailable in VS (I don't know if the documentation is flawed, or if things changed or even if my environment somehow is different). Anyway, SolutionDir is available for MSBuild also, and as I mentioned already builds go smoothly.

The template is equally uninteresting:

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".html" #>
<#@ assembly name="$(BuildSystemLibrary)" #>
<#@ import namespace="Csw.BuildSystem" #>
...

The error reported in visual Studio's error list is:

Running transformation: System.IO.FileNotFoundException: Could not load file or assembly 'Csw.BuildSystem, Version=1.0.0.3, Culture=neutral, PublicKeyToken=50a5e7eafae5071b' or one of its dependencies. The system cannot find the file specified.
File name: 'Csw.BuildSystem, Version=1.0.0.3, Culture=neutral, PublicKeyToken=50a5e7eafae5071b'

Now, while it does sound like SolutionDir might not be expanding to the correct path in VS, I know for sure that this is not the case. The file is definitely found on the filesystem, as its version number, culture and public key tokens are extracted. This is not a hypothetical cache glitch either, the error will show a new version number if I try to bump it.

I then used the Assembly Binding Log Viewer, which reports the following:

*** Assembly Binder Log Entry  (3/4/2014 @ 10:33:58 AM) ***

The operation failed.
Bind result: hr = 0x80070002. The system cannot find the file specified.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Running under executable  C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\devenv.exe
--- A detailed error log follows. 

=== Pre-bind state information ===
LOG: DisplayName = Csw.BuildSystem, Version=1.0.0.3, Culture=neutral, PublicKeyToken=50a5e7eafae5071b
 (Fully-specified)
LOG: Appbase = file:///C:/Program Files (x86)/Microsoft Visual Studio 12.0/Common7/IDE/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = devenv.exe
Calling assembly : (Unknown).
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\Users\tne\AppData\Local\Microsoft\VisualStudio\12.0\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: Csw.BuildSystem, Version=1.0.0.3, Culture=neutral, PublicKeyToken=50a5e7eafae5071b
LOG: GAC Lookup was unsuccessful.
LOG: Attempting download of new URL file...
...

It then attempts to find it in many local directories.

So, it would seem that it finds the assembly, extracts its strong name, then for some reason looks it up on the GAC instead of just using it.

I've installed the assembly in the GAC using gacutil to confirm this; the T4 transformation subsequently works fine from Visual Studio. Obviously, I'm not exactly keen to require the installation of the assembly in the GAC of all dev machines. Plus, I'd like to understand what's going on.

I've had a look at the VS conf (devenv.exe.config) as well as the machine conf (machine.config) files in search of some "Policy" rules that would forbid the use of arbitrary DLLs on the filesystem from devenv (or that would require assemblies to be in the GAC), but I wouldn't know where to begin.

I also tried to compile the assembly to target both MSIL and x86 (since VS is 32bit), which doesn't change anything.

Because it's supposed to be such a trivial thing to do, I suspect I've done a stupid mistake somewhere (which is why I detailed my process so extensively, sorry for the verbosity). Can anybody catch it?

No correct solution

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top