Recovering from error HRESULT = '8000000A' in CC.Net - visual-studio-2010

I'm seeing "An error occurred while validating. HRESULT = '8000000A'" a lot when building with VS2010 using CC.Net. I've installed the hotfix described therein but am still seeing the error.
The error can almost always be resolved by forcing another build manually. That's a real pain; naturally I'd like to automate it. :^) But I'm having trouble figuring out part of the process. I know I'll need to use a conditional task block. The pseudocode for what I want to do looks like:
if statusCondition == Failure
if failure is caused by HRESULT = '8000000A' <-- this is the part I can't figure out
Use devenv to rebuild vdproj file
endif
endif
How can I use CC.Net to determine that the cause of the failure really is 8000000A? I'm not seeing anything in CC.Net's Integration Properties or Task Condition Blocks that looks like what I need.
Thank you.

OK, using CruiseControl.NET's API I was finally able to solve this problem. Woot!
First I created a C# console application to get the list of failed projects. The log file for each failed project is inspected, and if the project failed due to error 8000000A, a build is forced on that project.
//Due to a race condition in Visual Studio 2010, CCNet builds often fail with error 8000000A.
//See http://stackoverflow.com/questions/8648428/an-error-occurred-while-validating-hresult-8000000a
//
//The build failure can almost always be fixed by manually forcing another build on the failed project.
//
//This program, along with a companion CCNet project, will automate the process of forcing the builds on the failed projects.
// 1. For each CCNet project whose IntegrationStatus == Failure
// 2. Search the most recent log file for the string "8000000A"
// 3. If string found in log file, force a build on the project
using System;
using ThoughtWorks.CruiseControl.Remote;
namespace ForceBuildForError8000000A
{
class Program
{
static void Main(string[] args)
{
CruiseServerRemotingClient ccnetClient = new CruiseServerRemotingClient("tcp://localhost:21234/CruiseManager.rem");
ProjectStatus[] prjStatusList = ccnetClient.GetProjectStatus();
foreach (ProjectStatus prjStatus in prjStatusList)
{
if (prjStatus.BuildStatus == IntegrationStatus.Failure)
{
string latestBuildName = ccnetClient.GetLatestBuildName(prjStatus.Name);
string log = ccnetClient.GetLog(prjStatus.Name, latestBuildName);
if (log.Contains("ERROR: An error occurred while validating. HRESULT = '8000000A'"))
{
Console.WriteLine("Forcing build for " + prjStatus.Name);
ccnetClient.ForceBuild(prjStatus.Name);
}
}
}
}
}
}
Next I created another project in my ccnet.config file. This project uses a Multi-trigger to monitor all the other projects that can fail with the 8000000A error. When any of the trigger projects has a failure, the C# program is run to force a build on any projects that failed due to 8000000A.
<cb:scope ProjectName="$(ForceBuildForError8000000A.Run_ProjectName)">
<project name="$(ProjectName)" >
<cb:ProjectHeaderMacro projectName="$(ProjectName)" />
<cb:AssemblyVersionLabellerMacro Major="1" Minor="0" Build="0" />
<triggers>
<multiTrigger>
<triggers>
<projectTrigger project="$(ProjectName1)" triggerStatus="Failure" />
<projectTrigger project="$(ProjectName2)" triggerStatus="Failure" />
<projectTrigger project="$(ProjectName3)" triggerStatus="Failure" />
<projectTrigger project="$(ProjectName4)" triggerStatus="Failure" />
<projectTrigger project="$(ProjectName5)" triggerStatus="Failure" />
<projectTrigger project="$(ProjectName6)" triggerStatus="Failure" />
</triggers>
</multiTrigger>
</triggers>
<tasks>
<exec>
<executable>$(ForceBuildForError8000000A.exe)</executable>
<baseDirectory>$(MyWorkingTrunk)</baseDirectory>
<buildTimeoutSeconds>120</buildTimeoutSeconds>
</exec>
</tasks>
</project>
</cb:scope>
Note I didn't end up using a conditional task block as I originally thought would be needed, but rather a conditional trigger.
There's probably a way to do this using NANT instead of C# but I could never figure out how.

Related

Why does overriding the Build target in msbuild work for C++ projects but fail for C# projects?

I am overriding the Build target like this in my file OverrideBuild.targets:
<Target Name="OriginalBuild" DependsOnTargets="$(BuildDependsOn)">
<Message Text="Finished running target OriginalBuild" Importance="High" />
</Target>
<Target Name="Build" >
<CheckArtifacts ProjectGuid = "$(ProjectGuid)" SolutionPath = "$(SolutionPath)" >
<Output PropertyName = "ArtifactsHaveChanged" TaskParameter = "Result" />
</CheckArtifacts>
<Message Text="ArtifactsHaveChanged = $(ArtifactsHaveChanged)" Importance="high" />
<!-- if the artifacts.props file has not just been updated then we can run the original build target -->
<Message Condition="'$(ArtifactsHaveChanged)' == 'false'" Text="Running target OriginalBuild" Importance="High" />
<CallTarget Condition="'$(ArtifactsHaveChanged)' == 'false'" Targets="OriginalBuild" />
<!-- Otherwise we need to run a new msbuild to avoid using an out-of-date cached version of the artifacts.props file.
To force the msbuild process not to use the cached values from this process we must pass at least one property.
-->
<Message Condition="'$(ArtifactsHaveChanged)' == 'true'" Text="Running target OriginalBuild in nested msbuild" Importance="High" />
<MSBuild Condition="'$(ArtifactsHaveChanged)' == 'true'" Targets="OriginalBuild"
Projects="$(MSBuildProjectFullPath)" Properties="InNestedMsbuild=true" />
<!-- Visual Studio doesn't pick up on the modified artifacts.props file unless we force it to reload the solution -->
<Touch Condition="'$(ArtifactsHaveChanged)' == 'true' and '$(BuildingInsideVisualStudio)' == 'true'" Files = "$(SolutionPath)" />
<Message Text="Finished running build target override" Importance="High" />
</Target>
and each of my .vcxproj or .csproj files includes this file at the end:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\OverrideBuild.targets" />
</Project>
This works as I want it to for the C++ projects but fails with the C# projects. When building a C# project via msbuild it fails because the command line to the C# compiler is missing reference arguments for local assemblies. For example, a C# file that has a line like this at the top of the file:
using My.Utils.Common;
fails with the following error message:
error CS0234: The type or namespace name 'Common' does not exist in the namespace 'My.Utils' (are you missing an assembly reference?)
And looking at the compiler command used it is missing this line:
/reference:C:\Code\scratch\Build\My.Utils.Common\Bin\Release\My.Utils.Common.dll
That missing line is present when I comment out my override of the Build target. And weirdly enough it will build fine from within Visual Studio even with my Build override in place. It only fails when building using msbuild from the command line and only for C# projects.
I thought that the way I had overriden the Build target would be completely transparent but apparently it isn't. Can anybody shed some light on what is going wrong ?
It seems that when project A depends on project B with a project reference, the outputs of the Build target of B are used to deduce what should be passed as a reference to the compiler when building A. This is presumably somewhere in the ResolveAssemblyReferences logic.
Therefore to get your replacement Build target working, you need to make its outputs match those of the standard Build.
Here is how you can achieve this:
<Target
Name="Build"
Condition=" '$(_InvalidConfigurationWarning)' != 'true' "
DependsOnTargets="GetTargetPathWithTargetPlatformMoniker"
Returns="#(TargetPathWithTargetPlatformMoniker)" >
</Target>
Here Returns="#(TargetPathWithTargetPlatformMoniker)" is what the Returns of the standard Build in the SDK is. But the item array #(TargetPathWithTargetPlatformMoniker) is initially empty, so you need to run the Target GetTargetPathWithTargetPlatformMoniker to populate it before hand.
These are implementation details of the build system, so they may vary by SDK version, but you can always inspect the logic in C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.target or equivalent.
Note that this cannot be used directly with C++ projects, their default Build target is a bit different. You may need to vary by the project type to support both. The Condition on a Target does not stop it from overwriting the existing one, it only stops it from executing, so if you need a target overwrite to differ, you need to put the alternatives in files and import them conditionally. I don't know of a more convenient way, but that at least works.
Why does overriding the Build target in msbuild work for C++ projects but fail for C# projects?
After test your sample, I found the error not comes from the overriden the Build target, it should be related to the project type which you referenced.
Because I have tried comment the import line in the HelloWorld project file:
<Import Project="..\..\OverrideBuild.targets" />
Then MSBuild command line still throw that error.
Besides, I found your referenced project HelloWorldHelper is a Console application project, which output type is Class library.
To resolve this issue, I have created a new Class library instead of Console application, then build it from MSBuild command line, it works fine.
So, please try to convert your referenced project to Class library.
Hope this helps.

Get TFS ChangeSet in running application

I have searched over the net for about 2 day now for this. Maybe i dont use the right terms. What i wish to do is to compile my project with the current TFS ChangeSet varriable avaliable. I'm not using TFS Build.
The only thing i have try, without success, is to use MSBuild Task with MSBuild Extension Pack to get the current ChangeSet(Not only the and put it into the assembly version(Revision). It fail because i dont use Build ... Here is the sample i have try
It's the first time i use this "Task" kind of programming and i dont know if i can do this without setuping TFS Builds. I can't setup Builds because i use external dlls and projects, wish seem to be complicated to setup.
(My next step is to check more about TFS Builds) ...
The raison i need this value at runtime, it's because i need this value in our error report feature. I wish to make this process automatic to avoid errors.
(Visual studio 2013, TFS [VisualStudio.com], Asp.net MVC 5)
Solution:
Tanks to Giulio Vian
This get the TFS ChangeSet of the current solution, not the newest, and write it in a file "TFS.cs".
the output file look like this:
static class TFS { public static string ChangeSet { get { return "436"; }}}
i have created a file "TFS.targets" into the root of my project:
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!--
add this line to Target to run this only on Release:
Condition="'$(Configuration)' == 'Release'" -->
<Target Name="BeforeBuild" >
<!-- Get information on the latest build -->
<Message Importance="High" Text="Getting TFS Chanset"/>
<Exec Command=""C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\tf.exe" info "$(SolutionPath)" | FIND "Changeset"" ConsoleToMSBuild="true">
<Output TaskParameter="ConsoleOutput" PropertyName="ChangesetLine" />
</Exec>
<PropertyGroup>
<ChangesetNumber>$(ChangesetLine.Substring(13, $([MSBuild]::Subtract( $(ChangesetLine.IndexOf(';')) , 13 ))))</ChangesetNumber>
</PropertyGroup>
<Message Importance="High" Text="ChangeSet: '$(ChangesetNumber)'. Writing TFS.cs ..."/>
<Exec Command="echo static class TFS { public static string ChangeSet { get { return "$(ChangesetNumber)"; }}} > TFS.cs" Outputs="TFS.cs">
<!--Add this file to the list to compile-->
<!--<Output ItemName="Compile" TaskParameter="Outputs" />-->
</Exec>
</Target>
</Project>
Then i have edited the project file and added this line in the Project node:
<Import Project="$(MSBuildProjectDirectory)\TFS.targets"/>
(Search for "Import Project" you will find some in your project)
on the first build, the file will not be in your project. So include it to use it. (Show all files).
if you wish to include it in the version, replace the exec echo by the code in this question but you will need to install MSBuildCommunityTasks
An other update
After some testing, it seem it dont show the last ChangeSet for the directory/solution. the correct command is
tf history "c:\YourSolutionDir" /noprompt /recursive /stopafter:1
the problem with this is it give you a response who is hard to extract with MsBuild.
i have writed an console application who will return only the changeset:
static void Main(string[] args)
{
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = #"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\tf.exe";
p.StartInfo.Arguments = "history \"" + args[0] + "\" /noprompt /recursive /stopafter:1";
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// p.WaitForExit();
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
var lines = output.Split(Environment.NewLine.ToArray(), StringSplitOptions.RemoveEmptyEntries);
var last = lines.Last();
var s = last.Split(' ');
Console.WriteLine(s[0]);
}
You can call the tf command to get information about TFVC files and folders. By running the tf info command in a local directory mapped to a workspace, you get lot of useful data, e.g.
Local information:
Local path : D:\Workspaces\xyz\mine.sln
Server path: $/DefaultCollection/xyz/mine.sln
Changeset : 6611
Change : none
Type : file
Server information:
Server path : $/DefaultCollection/xyz/mine.sln
Changeset : 6611
Deletion ID : 0
Lock : none
Lock owner :
Last modified: Saturday, March 14, 2015 09:38:44
Type : file
File type : utf-8
Size : 1994
Embedding the command in an MSBuild file is not hard as the following snippet shows
<Exec Command="tf info "$(myChangesSetReferenceFile)" | FIND "Changeset"" ConsoleToMSBuild="true">
<Output TaskParameter="ConsoleOutput" PropertyName="ChangesetLine" />
</Exec>
<PropertyGroup>
<ChangesetNumber>$(ChangesetLine.Substring(13, $([MSBuild]::Subtract( $(ChangesetLine.IndexOf(';')) , 13 ))))</ChangesetNumber>
</PropertyGroup>
The tf command comes installed with Team Explorer.
The TeamBuild task can only retrieve ChangeSets for a TFS build. You said you aren't using TFS builds yet so that won't work.
You could use the TFS API to pull changesets from TFS.
Honestly though that will be more difficult than just using the TFS build system if you were going to go to that anyways.

Merging Visual Studio Code Coverage fails with ImageNotFoundException

I'm trying to export visual studio code coverage files (data.coverage) into xml as described in this blog post from the code analysis team. I've moved the code example in that post into a custom MSBuild task. My custom task references the Microsoft.VisualStudio.Coverage.Analysis.dll located in the PrivateAssemblies folder of Visual Studio.
Right off the bat, trying to load the code coverage file throws an code analysis typed exception ImageNotFoundException, stating that the "Image file fully-qualified-file-path-to-dll could not be found."
// the following line throws an exception
CoverageInfo current =
CoverageInfo.CreateFromFile( "c:\path\testresults\x\y\z\data.coverage");
The path is fully qualified and the DLL it refers to does exist. My testsettings has this file listed as the assembly to instrument and the "Instrument in place" checkbox is set. I can view code coverage within Visual Studio, so I know coverage is working.
I'm running my MSBuild script from the Visual Studio command line. It looks like this:
<Project ToolsVersion="4.0" DefaultTargets="Default;"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="CustomTasks.MergeCoverageTask"
AssemblyFile="CustomTasks.dll"
/>
<Target Name="Default">
<ItemGroup>
<CoverageFiles Include="**\data.coverage" />
</ItemGroup>
<MergeCoverageTask
CoverageFiles="#(CoverageFiles)"
OutputFile="output.xml"
/>
</Target>
</Project>
Can anyone suggest what I need to do to get this working correctly?
5 hours later and this is a tumbleweed. I found some additional detail here, which helped get me further down the path.
In order for this to work, you need to include a few additional files alongside the custom task and supply folder locations for the pdb's and instrumented dll's.
Regarding additional files, you need the following:
The custom build task must reference Microsoft.VisualStudio.Coverage.Analysis.dll
Your bin folder must contain the following additional files:
Microsoft.VisualStudio.Coverage.Symbols.dll
dbghelp.dll
(If you don't have visual studio installed, you must perform regsvr32.exe on msdia100.dll)
Regarding paths to assemblies and symbols, the CreateFromFile method takes a collection of folders to search. What seems really strange is that error complains about not being able to locate missing instrumented assemblies, and it specifies the full path..
Image file c:\project\output\Assembly.dll could not be found.
...but if you specify that path, it doesn't work.
CoverageInfo current =
CoverageInfo.CreateFromFile( "c:\project\testresults\x\In\data.coverage",
new string[] { "c:\project\output" },
new string[] { "c:\project\output" });
However, changing the path to be the folder of the TestResults output works fine:
CoverageInfo current =
CoverageInfo.CreateFromFile( "c:\project\testresults\x\In\data.coverage",
new string[] { "c:\project\testresults\x\Out" },
new string[] { "c:\project\testresults\x\Out" });
I question whether "instrument in place" really means in that folder, or instrument and copy to the MS Test run folder.
Well dear SO folk, if you're reading this, you get a cookie.

run a custom msbuild target from VisualStudio

Suppose I add a custom target to a csproj file. Is there a way to run that target from visual studio? I don't want it make it a prebuild or postbuild step, I just want to be able to run this target (and its dependencies) from visual studio.
There is a simple way (though not all that satisfying) using a custom external tool.
Assuming your project file has the following modification:
<Target Name="CalledFromIde">
<Error Text="Called from the IDE!" />
</Target>
Go to Tools | External Tools and add one like this:
Title: Called from IDE
Command: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe
Arguments: $(ProjectDir)$(ProjectFileName) /t:CalledFromIde
Initial directory: $(ProjectDir)
Use Output window: checked
Running this produces output as:
Build FAILED.
"F:\Code\CsProject\CsProject.csproj" (CalledFromIde target) (1) ->
(CalledFromIde target) ->
F:\Code\CsProject\CsProject.csproj(57,5): error : Called from the IDE!
What you are doing is calling out to MSBuild as an external tool and having it run the target directly. You have to supply the full path to MSBuild because the IDE doesn't maintain the same properties that the build environment it creates has available.
You can hook this up to a shortcut by figuring out which command # it is in the set Tools.ExternalCommand#.
If you're looking for a solution with more sophistication, it is a bit more involved. Here it is in a nutshell (for VS2010):
1) Create a VS Addin (File | New | Project | Other Project Types | Extensibility | Visual Studio Add-in). I'm not sure if you have to have the VS SDK installed to get this, it is available in the extension manager.
Select the following options in the wizard:
- Microsoft Visual Studio 2010
- Yes, create a 'Tools' menu item
- Load when the Application starts
- My Add-in will never put up modal UI, and can be used with command line builds.
2) Add references to Microsoft.Build and Microsoft.Build.Framework
3) Find the implementation of Exec in the Connect.cs file
4) Replace it with this code:
public void Exec(
string commandName,
vsCommandExecOption executeOption,
ref object varIn,
ref object varOut,
ref bool handled)
{
handled = false;
if (executeOption != vsCommandExecOption.vsCommandExecOptionDoDefault)
return;
if (commandName != "BuildAddin.Connect.BuildAddin")
return;
var doc = _applicationObject.ActiveDocument;
var projectItem = doc.ProjectItem;
var project = projectItem.ContainingProject;
var evalProject =
Microsoft.Build.Evaluation.ProjectCollection
.GlobalProjectCollection.LoadProject(project.FullName);
var execProject = evalProject.CreateProjectInstance();
bool success = execProject.Build("CalledFromIde", null);
var window = _applicationObject.Windows.Item(Constants.vsWindowKindOutput);
var output = (OutputWindow)window.Object;
OutputWindowPane pane = output.OutputWindowPanes.Add("BuildAddin");
pane.OutputString(success ? "built /t:CalledFromIde" : "build failed");
handled = true;
return;
}
5) A better custom target while debugging, since the previous one errors:
<Target Name="CalledFromIde">
<WriteLinesToFile File="CalledFromIde.txt" Lines="Called from the IDE!" />
</Target>
6) The code above has no error checking for brevity, you'll want to be much cleaner since it will be running in the IDE. The addin will place a menu item on your Tools menu. As written above, it simply looks for the project containing the currently active editor document, which would need some better plumbing for whatever you are cooking up.
This technique gets the build engine instance from within the IDE and has it execute a build on a separate instance of the project.
If you are running the build inside of Visual Studio there will be a build variable of VisualStudioDir during the build.
To execute only is a VS build session do this:
<Target Name="Test" BeforeTargets="Build" Condition="'$(VisualStudioDir)' != ''>
</Target>
To execute only in a build outside of VS do this:
<Target Name="Test" BeforeTargets="Build" Condition="'$(VisualStudioDir)' == ''>
</Target>
You will need to include your custom targets file in one of two ways.
Set the environment variable CustomBeforeMicrosoftCommonTargets
Edit you project file to include your custom targets file by adding an import
<Imports Project="CustomBuildTasks.Targets"><Imports/>
You don't have to code with the Exec, although that is one way to do it. The easier way is to do the following:
Change the DefaultTargets="Build" attribute to a custom Target you create, say "All" like so:
DefaultTargets="All"
Then in your custom "All" target, you can use the DependsOnTargets attribute, like the following:
"<Target Name="All" DependsOnTargets="ZipOutputFiles;Build">
</Target>"
This will then build, and out put zip files in your custom "All" target.
Some time ago I had the same problem and decided to write VS add-in.
Try it:
https://github.com/Serg046/MsBuildTaskExplorer
https://marketplace.visualstudio.com/items?itemName=saaseev.MsBuildTaskExplorer

Gallio and MbUnit in NAnt

I am trying to use Gallio (v3.1)/MbUnit/NCover to run a unit test in my C# code, as part of the build process for my continuous integration system.
I can get Gallio.Echo.exe to execute the tests and output an XML file (albeit it does seem to be checking all .dll files in the folder == approx. 6.5MB .xml file!!), but when I try to get NCover to link in also, it goes bang.
THEN: I tried to use the NAnt task using instructions from here, such as:
<gallio result-property="testrunner.exit-code"
application-base-directory="bin/debug"
runner-type="NCover"
failonerror="false"
report-name-format="gallio-MyTestProject"
report-types="xml"
report-directory="bin/debug">
<runner-property value="NCoverArguments='//q //ea CoverageExcludeAttribute //a MyTestProject.dll'" />
<runner-property value="NCoverCoverageFile='coverage-MyTestProject.xml'" />
<assemblies>
<include name="bin/debug" />
</assemblies>
</gallio>
but I get the following error on my command-line:
Element Required! There must be a least one 'files' element for <gallio ... />.
I have tried to specify the .dll file that I'd like to check, but it still comes up with this message. Any suggestions are most appreciated!
<assemblies> has been changed to <files>

Resources