I follow this post Get Visual Studio to run a T4 Template on every build, using the solution with Visual Studio SDK and Visual Studio 2010 Modeling and Visualization SDK installation.
...BUT I get an error that I cannot solve:
Error 2 Running transformation: System.ArgumentNullException: Value cannot be null.
Parameter name: Could not obtain DTE from host
at Microsoft.VisualStudio.TextTemplatingE035559D977B9B9858AB2036321BC47D.GeneratedTextTransformation.EntityFrameworkTemplateFileManager.VsEntityFrameworkTemplateFileManager..ctor(Object textTemplating)
at Microsoft.VisualStudio.TextTemplatingE035559D977B9B9858AB2036321BC47D.GeneratedTextTransformation.EntityFrameworkTemplateFileManager.Create(Object textTransformation)
at Microsoft.VisualStudio.TextTemplatingE035559D977B9B9858AB2036321BC47D.GeneratedTextTransformation.TransformText()
at Microsoft.VisualStudio.TextTemplating.TransformationRunner.RunTransformation(TemplateProcessingSession session, String source, ITextTemplatingEngineHost host, String& result). Line=0, Column=0 ApmWeb.Web.Client
Following the first part of my script...
<## template language="C#" debug="false" hostspecific="true"#>
<## include file="EF.Utility.CS.ttinclude"#>
<## output extension=".cs"#><#
CodeGenerationTools code = new CodeGenerationTools(this);
MetadataLoader loader = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this);
string inputFile = #"../../ApmWeb.Infrastructure.Data/Model/ApmWebModel.edmx";
MetadataWorkspace metadataWorkspace = null;
bool allMetadataLoaded =loader.TryLoadAllMetadata(inputFile, out metadataWorkspace);
EdmItemCollection ItemCollection =
(EdmItemCollection)metadataWorkspace.GetItemCollection(DataSpace.CSpace);
string namespaceName = code.VsNamespaceSuggestion();
EntityFrameworkTemplateFileManager fileManager =
EntityFrameworkTemplateFileManager.Create(this);
UPDATE
I found that the problem is into "EF.Utility.CS.ttinclude" in this point...
dte = (EnvDTE.DTE) hostServiceProvider.GetService(typeof(EnvDTE.DTE));
if (dte == null)
{
throw new ArgumentNullException("Could not obtain DTE from host");
}
My idea is that you can’t get the DTE object when running the transformation outside the VS Host. This error occurs, for example when running the transformation within a Team Build (the MSBuild Host does not “know” the DTE object). In effect it works using "run custom tool" from VS but configuring the autamatic T4 build as I told in previous post, it doesn't work.
So how can solve? Is it a bug of EF.Utility.CS.ttinclude?
UPDATE
Removing interaction with VS using DTE (define PREPROCESSED_TEMPLATE in EF.Utility.CS.ttinclude) all works, but I loose e.g. capability to add generated file to my project... Is there an other way to put working it?
As you've discovered, you're not going to have access to the DTE when transforming at build time.
One approach to dealing with the generated code not appearing in your project is to include it with a wildcard e.g.
As long as you use a standard output naming convention this should give you a reasonable experience. You can also use the build targets to redirect the generated output to a specific folder and then use a simpler wildcard to include everything in the folder.
You do have to watch out that manual tweaking in the IDE will remove this wildcard and replace it with a point in time outcome of its evaluation.
Related
My custom tool window in a Visual Studio Extension package is supposed to evaluate custom expressions during debug sessions for VS2012 and VS2013. The final goal is to build a graphical watch window. Therefore, I must fetch the value of the expression result from the debug engine.
The evaluation works fine via
IDebugStackFrame2 -> IDebugExpressionContext2 -> ParseText
IDebugExpression2 -> EvaluateSync -> IDebugProperty2
The first problem here: On Visual Studio 2013 ParseText() does not correctly report errors. But the IDebugProperty2 retrieved is valid (for valid expressions). On VS2012 I get proper errors for invalid expressions.
This is where the real problem starts. The result Property2 represents a complex custom object. In order to visualize it, a specific property from one of its base classes needs to get retrieved. In my current attempt, I am using IDebugProperty2.EnumChildren() to walk along its members up to the right place in the class hierarchy, similar as one would do in the Locals window in Visual Studio:
[result] -> base -> Raw -> base -> Non-public Members -> [private field] ... a.s.o.
This is somehow working but introduces the following problems:
Problem 1: Get an internal member of some complex object
The members and their order received from EnumChildren() seem to depend on some factors, which are not obvious to me:
* Does the object has a Debug Visualizer attached? (VS2013 ignores enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_VALUE_RAW in EnumChildren and gives back the beautified version + a "Raw View" node, VS2012 works as expected)
* Is a 'Non-public Members' node shown? Sometimes it is, sometimes not - dunno from what it depends on
* Setting of 'Just my code' (? I read about it in several posts but couldn't really figure out a reliable rule)
* ... there must be other dependencies ?
This turns out to be a really hacky way of aquiring an internal member of some complex expression result object. What could be a better way? Using IDebugProperty3 and some Debugger Visualizer tricks? Any pointer would be great.
Problem 2: VS2012 fails to give memory bytes
Once I have the private member of the result object, I use IDebugProperty2.GetMemoryBytes() in order to 'serialize' its value (a plain System.Array) and transfer it to my Custom Tool Window. While this is working in VS2013, in VS2012 IDebugProperty2.GetMemoryBytes() always returns VS_Constants.S_FALSE. I am really out of ideas here. Since there is a Memory Window in VS2012, there ought to be some way to get this working here also? What am I missing?
The code used to query the memory API:
IDebugMemoryContext2 memCtx = null;
IDebugMemoryBytes2 memBytes = null;
// getting the memory context is working ok
if (tmpProperty2.GetMemoryContext(out memCtx) == VSConstants.S_OK
// VS2012 fails here, always returns S_FALSE, memBytes remains null:
&& (tmpProperty2.GetMemoryBytes(out memBytes) == VSConstants.S_OK)) {
...
Some more context:
OS: Windows 8 and 8.1
Visual Studio: 2012 / 2013
Tool Window built with MPF (C#) in VS2013, (removed all vers. 12 references from the package before deploying to VS2012)
Debug engines: C# and Visual Basic
I am writing a visual studio custom wizard for creating C++ project.
I need to define additional build configuration, that inherits the debug configuration.
I googled a lot, but couldn't find anything.
I guess this should be done in the JScript file (default.js), AddConfig function, by calling proj.Object.AddConfiguration. But I couldn't find examples, nor syntax rules.
The only thing I found is : http://www.codeproject.com/Articles/200039/A-Visual-Studio-Wizard-to-add-more-project-configu but it is way too complicated, and I couldn't figure it out.
Can you please help?
Found it. It can be done using C# code:
solution.SolutionBuild.SolutionConfigurations.Add("Conf", "Debug", true);
//set Conf to be active build configuration
solution.SolutionBuild.SolutionConfigurations.Item("Conf").Activate();
//Get project
VCProject pro = solution.Projects.Item(1).Object;
//Get comiler tool for project
VCCLCompilerTool tool = pro.Configurations.item("Conf").Tools("VCCLCompilerTool");
//set Prprocessor definition for Conf
tool.PreprocessorDefinitions = "NEW";
We have a DLL project which has existed for a long time (maybe as far back as Visual Studio 6) which has been updated for each new version of VS. The project contains a number COM classes implemented using ATL.
After upgrade to VS 2010, the project still builds fine. However, if I try to right-click the project and choose Add -> Class... -> ATL Simple Object, I get an error box that says this:
ATL classes can only be added to MFC EXE and MFC Regular DLL projects or projects with full ATL support.
This worked in VS 2008.
When I look at the project properties, Use of MFC was set to Use Standard Windows Libraries and Use of ATL was set to Not Using ATL. I changed these to Use MFC in a Shared DLL and Dynamic Link to ATL respectively, but still get the same error.
I know how to add new ATL objects without using the wizard, and I could try to recreate the project from scratch using VS 2010 to make it happy. But does anyone know of any easy way to get VS to allow me to use the ATL Simple Object wizard with a project that it doesn't recognize as a project "with full ATL support"?
Check this thread out.
It seems that adding this fragment info your ATL C++ code make it work. You don't need to actually build the project, just remove this stuff away after you are done with the wizard (provided that solution works for you).
// Added fake code begins here
class CAppModule :
public CComModule
{
};
// Added fake code ends here, below is regular ATL project stuff
CAppModule _Module;
This is where it all comes from, in $(VisualStudio)\VC\VCWizards\1033\common.js:
/******************************************************************************
Description: Returns a boolean indicating whether project is ATL-based.
oProj: Project object
******************************************************************************/
function IsATLProject(oProj)
{
try
{
var oCM = oProj.CodeModel;
oCM.Synchronize();
// look for global variable derived from CAtlModuleT
var oVariables = oCM.Variables;
for (var nCntr = 1; nCntr <= oVariables.Count; nCntr++)
{
var oVariable = oVariables(nCntr);
var strTypeString = oVariable.TypeString;
if (strTypeString == "ATL::CComModule" || strTypeString == "ATL::CAutoThreadModule")
{
return true;
}
Same problem here, but the project source already had CComModule _Module;
Fixed it, based on the IsATLProject script shown above, by changing it to
**ATL::**CComModule _Module;
In Visual Studio 2010 I have the following project layout:
Solution
project A
class C
class D
project B
T4 template
The T4 template contains a assembly reference like this:
<## assembly name="$(SolutionDir)\A\bin\Debug\A.dll" #>
The template instantiates an instance of class C. When I run the T4 template the processor loads the project A's dll and correctly creates the output. The error arises when I want to change something in project A, say modify either class C or D.
Unable to copy file "obj\Debug\A.dll"
to "bin\Debug\A.dll". The process
cannot access the file
'bin\Debug\A.dll' because it is being
used by another process.
The only way I found to get rid of this error is to restart Visual Studio. Is there any other way to force the unloading of the A.dll assembly from VS?
Im using VS2010 SP1 and was still getting blocked during build after first build when running a custom T4 template during the POST-BUILD events which accessed instances of classes of the same project.
How I got it to work was to use Reflection to access classes from the Project dll.
I still got the blocking issue when loading the dll directly from the file.
NOTE: The trick was to load the .dll into memory as a byte array and then load the assembly from the raw byte array. DONT load from the file using the Assembly.LoadFrom
This code is from my T4 template file and is accessing a static class "Information" and calling a static Method "Version" to return a string value.
string assemblyPath = Path.Combine(projectPath, #"bin\SampleProject.dll");
byte[] data;
using (var fs = File.OpenRead(assemblyPath))
{
data = new byte[fs.Length];
fs.Read(data, 0, Convert.ToInt32(fs.Length));
}
if (data == null || data.Length == 0)
{
throw new ApplicationException("Failed to load " + assemblyPath);
}
var asm = Assembly.Load(data);
appVersion = (string) asm.GetType("SampleProject.Information").GetField("Version").GetValue(null);
m0sa
This issue has been fixed in Visual Studio 2010 SP1.
If you're not able to use that, there is a VolatileAssembly directive add-on in the T4 Toolbox project on CodeBox (http://t4toolbox.codeplex.com/)
Are there any tools or Visual Studio 2010 extensions which allow me to view the output of a configuration file transformation short of having to publish the entire project? Is the process which performs the transformation directly invokable?
Edit
After a little more Googling I came across this:
Step 4: Generating a new transformed web.config file for “Staging” environment from command line
Open Visual Studio Command prompt by
going to Start --> Program Files –>
Visual Studio v10.0 –> Visual Studio
tools –> Visual Studio 10.0 Command
Prompt
Type “MSBuild “Path to Application
project file (.csproj/.vbproj) ”
/t:TransformWebConfig
/p:Configuration=Staging" and hit
enter as shown below:
Once the transformation is successful
the web.config for the “Staging”
configuration will be stored under obj
-->Staging folder under your project root (In solution explorer you can
access this folder by first un-hiding
the hidden files) :
In the solution explorer click the button to show hidden files
Open the Obj folder
Navigate to your Active configuration (in our current case it is “Staging”)
You can find the transformed web.config there
You can now verify that the new
staging web.config file generated has
the changed connection string section.
Source: Web Deployment: Web.Config Transformation
This isn't really a perfect solution for me as it still requires building the entire project- at least with the command he posted. If anyone knows of way to skip the build step with the MSBuild command that would be helpful (although that sounds somewhat unlikely).
Edit 2
I also found this Config Transformation Tool on CodePlex, which offers some nice functionality to extend the transformation process. This is tool is the closest thing I've seen for the functionality I'm seeking and would be a great starting point for developing an extension which creates previews. It uses the Microsoft.Web.Publishing.Tasks library to perform the transformation and does not depend on building an actual project.
The SlowCheetah VS add-in on the visualstudiogallery allows you to preview the transform results
You can transform a config file by using the same objects the MSBuild task uses, bypassing MSBuild altogether. Web config transform logic is contained in the Microsoft.Web.Publishing.Tasks library.
The following code snippet comes from a simple class library, referencing the Microsoft.Web.Publishing.Tasks library (which is installed on my machine at C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\Web).
The sample loads a source document and transform, applies the transform, and writes the results to a new file.
using System;
using Microsoft.Web.Publishing.Tasks;
// ...
var xmlTarget = new XmlTransformableDocument();
xmlTarget.PreserveWhitespace = true;
xmlTarget.Load("Web.config");
var xmlTransform = new XmlTransformation("Web.Release.config");
if (xmlTransform.Apply(xmlTarget))
xmlTarget.Save("Web.Transformed.config");
else
Console.WriteLine("Unable to apply transform.");
With a little creativity, this simple solution could be integrated into a Visual Studio plugin, perhaps as a context menu item on the web.config file. At the very least, you can make a console utility or script out of it to generate previews.
Good luck!
Old post, but thought I would share what I had found with a quick google (for those that may not have found it or tried here first):
Web.config Transformation Tester - By AppHarbor
Simply paste your original XML along with the transformation XML and see the result instantaneously.
Also, it's open source for anyone who's interested.
Just to extend on this a little.
I needed exactly what is discussed above. To be able to run the transform only.
Then hook that into my build process which happens to be TeamCity in my case.
You will need using Microsoft.Web.Publishing.Tasks, which you can just smash down with Nuget. Well, I was in VS2013 so I could. I'm sure you could acquire the dll otherwise.
Wrote a simple Console App. You may find it useful.
Program.cs
using System;
namespace WebConfigTransform
{
class Program
{
static void Main(string[] args)
{
if (args.Length != 3)
{
Console.WriteLine("Config Gen ... usage -source -transform -destination");
Environment.Exit(-1);
}
Transform t = new Transform(args[0], args[1], args[2]);
t.Run();
}
}
}
Transform.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Security;
using System.Security.Permissions;
using Microsoft.Web.XmlTransform;
namespace WebConfigTransform
{
class Transform
{
private readonly string m_source;
private readonly string m_transform;
private readonly string m_destination;
public Transform(string source, string transform, string destination)
{
m_source = source;
m_transform = transform;
m_destination = destination;
}
private void TransformFiles()
{
var xmlTarget = new XmlTransformableDocument();
xmlTarget.PreserveWhitespace = true;
xmlTarget.Load(m_source);
var xmlTransform = new XmlTransformation(m_transform);
if (xmlTransform.Apply(xmlTarget))
xmlTarget.Save(m_destination);
else
{
Console.WriteLine("Unable to apply transform.");
Environment.Exit(-1);
}
}
private void CheckPermissions()
{
string directoryName = m_destination;
PermissionSet permissionSet = new PermissionSet(PermissionState.None);
FileIOPermission writePermission = new FileIOPermission(FileIOPermissionAccess.Write, directoryName);
permissionSet.AddPermission(writePermission);
if (!(permissionSet.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet)))
{
Console.WriteLine("Cannot write to file : " + m_destination);
Environment.Exit(-1);
}
}
private void CheckFileExistance()
{
List<string> ls = new List<string>();
ls.Add(m_source);
ls.Add(m_transform);
foreach (string item in ls)
{
if (!File.Exists(item))
{
Console.WriteLine("Cannot locate file : " + item);
Environment.Exit(-1);
}
}
}
public void Run()
{
CheckFileExistance();
CheckPermissions();
TransformFiles();
}
}
}