Loading assemblies dynamically using the AssemblyResolve Event fails at random - assembly-loading

Following the example of the following KB article we have implemented the AssemblyResolve event in order to point .Net to the correct folder to load assemblies from: http://support.microsoft.com/kb/837908
This is our current implementation:
public static class AssemblyLoader
{
/// <summary>
/// A custom AssemblyResolver that will search for missing assemblies in the root and subfolders of the executing assembly
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
/// <returns></returns>
public static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string assemblyPath = string.Empty;
string assemblyFileName = string.Empty;
try
{
// This handler is called only when the common language runtime tries to bind to the assembly and fails.
string rootProbingPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
// Loop through the referenced assembly names.
assemblyFileName = args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll";
// Search for the filename in the root and subfolders of the rootProbingPath
string[] matchingAssemblies = Directory.GetFiles(rootProbingPath, assemblyFileName, SearchOption.AllDirectories);
// If a match is found, awesomeness was achieved!
if (matchingAssemblies.Length > 0)
assemblyPath = matchingAssemblies[0];
// Throw a clear exception when the assembly could not be found.
if (string.IsNullOrEmpty(assemblyPath))
throw new FileNotFoundException(string.Format("AssemblyLoader: Could not find assembly '{0}' in '{1}' or its subfolders.", assemblyFileName, rootProbingPath));
Console.WriteLine(string.Format("[" + DateTime.Now.ToString() + "] AssemblyLoader: Assembly '{0}' found at '{1}'", assemblyFileName, assemblyPath));
// Load the assembly from the specified path.
return Assembly.LoadFrom(assemblyPath, AppDomain.CurrentDomain.Evidence);
}
catch (Exception ex)
{
throw new Exception(string.Format("[" + DateTime.Now.ToString() + "] The assemblyloader could not load the assembly '{0}' from path '{1}': " + ex.Message, assemblyFileName, assemblyPath), ex);
}
}
}
We use this in all of our batch procedures which often run in parallel and using mostly the same assemblies.
Periodically a batch will crash leaving the following trail:
[26/04/2013 12:35:01] AssemblyLoader: Assembly
'COMPANY.DistributieOrderFacade.dll' found at
'C:\COMPANY_Batch\MDS\Executables\MDS\FACADE\COMPANY.DistributieOrderFacade.dll'
[26/04/2013 12:35:01] AssemblyLoader: Assembly
'COMPANY.DOCUMENTCENTERDOCS.dll' found at
'C:\COMPANY_Batch\MDS\Executables\MDS\CLIENT\COMPANY.DOCUMENTCENTERDOCS.dll'
26/04/2013 12:35:01: Create COMPANYDocument...in
queue:\rug.adroot\dfsroot\mds\Data\queue_new\ 26/04/2013 12:35:01
Could not load file or assembly 'COMPANY.DistributieOrderBRDA,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its
dependencies. General Exception (Exception from HRESULT: 0x80131500)
The assembly in the error is indeed located in one of the folders which the AssemblyLoader searches. And if I run the procedure again, it does succeed.
I've tried to see if the dll's are getting locked by using this assembly loader in two console apps and access the same dll's at the same time, but this did not seem to be a problem.
I should also mention that this is just a log from one of the batches. There are others, and it's rarely the same assembly that is not being loaded.
I don't know where to start looking for a solution next.

It was due to an update script that updated the dll's before each batch was running. When two batches were running at the same and there was a dll that needed updating, locks were occurring on the dll's which was causing the problem.
We've removed this script for a week now and haven't seen any problems yet. So our conclusion is that this was the problem.

Related

Resolving a TypeReference to a TypeDefinition in Mono.Cecil fails with Assembly Resolution Error

I'm trying to get a Mono.Cecil TypeDefinition from a .NET type and not having any luck.
I'm using code like this:
var type = typeof(MarkdownMonster.AppConfiguration);
var a = AssemblyDefinition.ReadAssembly(type.Assembly.Location);
var tr = a.MainModule.Import(type); // this seems to work
var td = tr.Resolve(); // fails
but it fails with an assembly resolution error:
GetConfigurationPropertiesTest [0:29.990] Failed: Mono.Cecil.AssemblyResolutionException : Failed to resolve assembly: 'MarkdownMonster, Version=1.18.11.0, Culture=neutral, PublicKeyToken=null'
Mono.Cecil.AssemblyResolutionException : Failed to resolve assembly: 'MarkdownMonster, Version=1.18.11.0, Culture=neutral, PublicKeyToken=null'
at Mono.Cecil.BaseAssemblyResolver.Resolve(AssemblyNameReference name, ReaderParameters parameters)
at Mono.Cecil.DefaultAssemblyResolver.Resolve(AssemblyNameReference name)
at Mono.Cecil.MetadataResolver.Resolve(TypeReference type)
at Mono.Cecil.TypeReference.Resolve()
at Westwind.TypeImporter.TypeParser.ParseObject(Type type, Boolean dontParseMembers)
The assembly is obviously there, since the TypeReference import seems to work and produces a valid TypeReference.
The assembly in question is an EXE, and just for kicks I renamed it to a DLL but that had no effect.
After a bit of back and forth experimenting I found one (ugly) solution is to create a custom type resolver and basically forcing a hard type reference into it. It seems Mono.Cecil is able to resolve transient dependencies once it's found the main assembly, but not the top level reference.
To make this work I basically pass in the already resolved assembly reference. In my case I know the only reference I will need to return will be the top level reference so I hard code this. A more realistic example will have to use AssemblyDefinition.ReadAssembly() to read an assembly off disk or from a stream.
Here's is the code to create the AssemblyResolver:
public class MonoAssemblyResolver : IAssemblyResolver
{
public AssemblyDefinition AssemblyDefinition;
public AssemblyDefinition Resolve(AssemblyNameReference name)
{
return AssemblyDefinition;
}
public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
{
return AssemblyDefinition;
}
public void Dispose()
{
AssemblyDefinition = null;
}
}
Note the cheat to pass in an already resolved assembly reference.
To resolve I can now use the following code:
var a = AssemblyDefinition.ReadAssembly(type.Assembly.Location,
new ReaderParameters() { AssemblyResolver = resolver });
// assign the resolvedr
var resolver = new MonoAssemblyResolver();
resolver.AssemblyDefinition = a;
var tr = a.MainModule.Import(type: type);
var td = tr.Resolve(); // works now
This is crazy hacky, but can be adapted to be more generic (in my case not needed).
Still it would be much nicer if Mono.Cecil could automatically resolve the assembly - I don't understand why it's not finding the assembly in the first place since it lives in the current bin folder and the TypeReference can find it.
This is how it's normally done:
var assembly = #"c:\myassembly.dll";
var resolver = new DefaultAssemblyResolver();
// add .NET runtime dir for the sake of security
foreach (var dir in Directory.GetDirectories(RuntimeEnvironment.GetRuntimeDirectory(), "*", SearchOption.AllDirectories))
{
resolver.AddSearchDirectory(dir);
}
// add the assembly's directory
resolver.AddSearchDirectory(Path.GetDirectoryName(assembly));
var mod = AssemblyDefinition.ReadAssembly(assembly, new ReaderParameters { AssemblyResolver = resolver }).MainModule;
Regards

Can't build UWP in release mode

I have a project that is working well in debug mode, but not working at all in release mode.
The solution contains 3 projects
Shared project
windows phone 8.1 project
UWP project
here is the error output
2>C:\Program Files (x86)\MSBuild\Microsoft\.NetNative\x86\ilc\IlcInternals.targets(887,5): error : System.InvalidOperationException: Unable to generate a temporary class (result=1).
2>C:\Program Files (x86)\MSBuild\Microsoft\.NetNative\x86\ilc\IlcInternals.targets(887,5): error : error CS0012: The type 'Windows.UI.Xaml.Visibility' is defined in an assembly that is not referenced. You must add a reference to assembly 'Windows.Foundation.UniversalApiContract, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime'.
2>C:\Program Files (x86)\MSBuild\Microsoft\.NetNative\x86\ilc\IlcInternals.targets(887,5): error : error CS0030: Cannot convert type 'Windows.UI.Xaml.Visibility' to 'Windows.UI.Xaml.Visibility [e:\MyApp\obj\x86\Release\ilc\in\WinMetadata\Windows.winmd]'
2>C:\Program Files (x86)\MSBuild\Microsoft\.NetNative\x86\ilc\IlcInternals.targets(887,5): error : error CS0029: Cannot implicitly convert type 'Windows.UI.Xaml.Visibility [e:\MyApp\obj\x86\Release\ilc\in\WinMetadata\Windows.winmd]' to 'Windows.UI.Xaml.Visibility'
2>C:\Program Files (x86)\MSBuild\Microsoft\.NetNative\x86\ilc\IlcInternals.targets(887,5): error :
2>C:\Program Files (x86)\MSBuild\Microsoft\.NetNative\x86\ilc\IlcInternals.targets(887,5): error : at System.Xml.Serialization.Compiler.Compile(String ns, XmlSerializerCompilerParameters xmlParameters, Evidence evidence, String outputDir, String intermediateDir, Boolean loadAssembly)
2>C:\Program Files (x86)\MSBuild\Microsoft\.NetNative\x86\ilc\IlcInternals.targets(887,5): error : at System.Xml.Serialization.TempAssembly.GenerateAssembly(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, Evidence evidence, XmlSerializerCompilerParameters parameters, Hashtable assemblies, String outputDir, String intermediateDir, Boolean loadAssembly)
2>C:\Program Files (x86)\MSBuild\Microsoft\.NetNative\x86\ilc\IlcInternals.targets(887,5): error : at System.Xml.Serialization.XmlSerializer.GenerateSerializer(Type[] types, XmlMapping[] mappings, CompilerParameters parameters, String outputDir, String intermediateDir, Boolean loadAssembly)
2>C:\Program Files (x86)\MSBuild\Microsoft\.NetNative\x86\ilc\IlcInternals.targets(887,5): error : at System.Xml.Serialization.XmlSerializer.GenerateSerializer(Type[] types, String outputDir, String intermediateDir, List`1 wcfSerializers, Boolean loadAssembly)
2>C:\Program Files (x86)\MSBuild\Microsoft\.NetNative\x86\ilc\IlcInternals.targets(887,5): error : at SerializationAssemblyGenerator.Program.Main(String[] args)
2>C:\Program Files (x86)\MSBuild\Microsoft\.NetNative\x86\ilc\IlcInternals.targets(887,5): error : Internal compiler error: One or more errors occurred.
The nuget packages I'm using are
"GoogleAnalyticsSDK": "1.2.12",
"HockeySDK.UWP": "4.0.0",
"HtmlAgilityPack": "1.4.9",
"Microsoft.ApplicationInsights": "1.0.0",
"Microsoft.ApplicationInsights.PersistenceChannel": "1.0.0",
"Microsoft.ApplicationInsights.WindowsApps": "1.0.0",
"Microsoft.NETCore.UniversalWindowsPlatform": "5.1.0",
"MvvmLightLibs": "5.1.1",
"Newtonsoft.Json": "8.0.3",
"WriteableBitmapEx": "1.5.0"
Update
I tried to add the Windows.Foundation.UniversalApiContract manually, but got this
Update 2
After I have unchecked the Compile with .Net Native tool chain feature, it worked.
Any way to build it using the .net native tool chain ?
I also hit a very similar issue. I had a type, let's say Foo, referencing a winmd type Windows.UI.Xaml.Visibility, see below.
public class Foo
{
public Windows.UI.Xaml.Visibility Visibility;
}
I got the exact same error at compile time when I tried to serialize an instance of type Foo using XmlSerializer. It seems that .Net Native tools have problem with generating XmlSerializer for types like Foo.
Here's my workaround. I created my own Visibility type, MyVisibility, and changed the existing Visibility field to a get only property (so that XmlSerializer would not serialize the property).
public class Foo
{
public Windows.UI.Xaml.Visibility Visibility
{
get
{
return (Visibility)myVisibility;
}
}
public MyVisibility myVisibility;
}
public enum MyVisibility
{
Visible = 0,
Collapsed = 1
}
Here's my test code for serializing a Foo instance,
public static void Test()
{
var foo = new Foo();
foo.myVisibility = MyVisibility.Collapsed;
var serializer = new XmlSerializer(typeof(Foo));
using (var stream = new MemoryStream())
{
serializer.Serialize(stream, foo);
stream.Position = 0;
var foo1 = (Foo)serializer.Deserialize(stream);
Assert.True(foo.Visibility == foo1.Visibility);
}
}
Hope this helps.

Revit Api Load Command - Auto Reload

I'm working with the revit api, and one of its problems is that it locks the .dll once the command's run. You have to exit revit before the command can be rebuilt, very time consuming.
After some research, I came across this post on GitHub, that streams the command .dll into memory, thus hiding it from Revit. Letting you rebuild the VS project as much as you like.
The AutoReload Class impliments the revit IExteneralCommand Class which is the link into the Revit Program.
But the AutoReload class hides the actual source DLL from revit. So revit can't lock the DLL and lets one rebuilt the source file.
Only problem is I cant figure out how to implement it, and have revit execute the command. I guess my C# general knowledge is still too limited.
I created an entry in the RevitAddin.addin manifest that points to the AutoReload Method command, but nothing happens.
I've tried to follow all the comments in the posted code, but nothing seems to work; and no luck finding a contact for the developer.
Found at: https://gist.github.com/6084730.git
using System;
namespace Mine
{
// helper class
public class PluginData
{
public DateTime _creation_time;
public Autodesk.Revit.UI.IExternalCommand _instance;
public PluginData(Autodesk.Revit.UI.IExternalCommand instance)
{
_instance = instance;
}
}
//
// Base class for auto-reloading external commands that reside in other dll's
// (that Revit never knows about, and therefore cannot lock)
//
public class AutoReload : Autodesk.Revit.UI.IExternalCommand
{
// keep a static dictionary of loaded modules (so the data persists between calls to Execute)
static System.Collections.Generic.Dictionary<string, PluginData> _dictionary;
String _path; // to the dll
String _class_full_name;
public AutoReload(String path, String class_full_name)
{
if (_dictionary == null)
{
_dictionary = new System.Collections.Generic.Dictionary<string, PluginData>();
}
if (!_dictionary.ContainsKey(class_full_name))
{
PluginData data = new PluginData(null);
_dictionary.Add(class_full_name, data);
}
_path = path;
_class_full_name = class_full_name;
}
public Autodesk.Revit.UI.Result Execute(
Autodesk.Revit.UI.ExternalCommandData commandData,
ref string message,
Autodesk.Revit.DB.ElementSet elements)
{
PluginData data = _dictionary[_class_full_name];
DateTime creation_time = new System.IO.FileInfo(_path).LastWriteTime;
if (creation_time.CompareTo(data._creation_time) > 0)
{
// dll file has been modified, or this is the first time we execute this command.
data._creation_time = creation_time;
byte[] assembly_bytes = System.IO.File.ReadAllBytes(_path);
System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(assembly_bytes);
foreach (Type type in assembly.GetTypes())
{
if (type.IsClass && type.FullName == _class_full_name)
{
data._instance = Activator.CreateInstance(type) as Autodesk.Revit.UI.IExternalCommand;
break;
}
}
}
// now actually call the command
return data._instance.Execute(commandData, ref message, elements);
}
}
//
// Derive a class from AutoReload for every auto-reloadable command. Hardcode the path
// to the dll and the full name of the IExternalCommand class in the constructor of the base class.
//
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
[Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
public class AutoReloadExample : AutoReload
{
public AutoReloadExample()
: base("C:\\revit2014plugins\\ExampleCommand.dll", "Mine.ExampleCommand")
{
}
}
}
There is an easier approach: Add-in Manager
Go to Revit Developer Center and download the Revit SDK, unzip/install it, the check at \Revit 2016 SDK\Add-In Manager folder. With this tool you can load/reload DLLs without having to modify your code.
There is also some additional information at this blog post.
this is how you can use the above code:
Create a new VS class project; name it anything (eg. AutoLoad)
Copy&Paste the above code in-between the namespace region
reference revitapi.dll & revitapiui.dll
Scroll down to AutoReloadExample class and replace the path to point
your dll
Replace "Mine.ExampleCommand" with your plugins namespace.mainclass
Build the solution
Create an .addin manifest to point this new loader (eg.
AutoLoad.dll)
your .addin should include "FullClassName" AutoLoad.AutoReloadExample
This method uses reflection to create an instance of your plugin and prevent Revit to lock your dll file! You can add more of your commands just by adding new classes like AutoReloadExample and point them with seperate .addin files.
Cheers

TFS 2010 Building Sharepoint 2010 Solution With Custom Outputs

I have a very similar question to this SO post: TFS Build 2010 - Custom Binary Location and SharePoint WSP. There's no marked answer, but the only answer provided seemed to be the path to go.
I'm building several solutions and need the solutions and projects to be placed into their own folders. This lead to the build output change to the MSBuild call in the template that I'm using. I've been using this for sometime without any issues.
Recently a developer complained that the .wsp files were not being generated in our daily build. I looked into this and came across the fore mentioned SO post.
I followed the instructions and now have a new error:
C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\SharePointTools\Microsoft.VisualStudio.SharePoint.targets (411): Method not found: 'Boolean Microsoft.VisualStudio.SharePoint.PathUtils.HasIllegalDeploymentPathCharacters(System.String)'.
I've looked at this line (411) in the targets file:
<PackageFiles LayoutPath="$(LayoutPath)%(EnumeratedFiles.Package)\" PackagePath="$(BasePackagePath)%(EnumeratedFiles.Package).$(PackageExtension)" />
The PackageFiles target is defined:
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="PackageFiles" />
I checked the GAC and didn't see it there so I added it. The TFS 2010 Build machine has Visual Studio 2010 and Sharepoint 2010 installed on it. I don't think I need to do anything other than changing this task:
<CreateSharePointProjectService Configuration="$(Configuration)"
Platform="$(Platform)"
ProjectFile="$(MSBuildProjectFile)"
ProjectReferences="#(SharePointProjectReference)"
OutDir="$(TargetDir)">
<Output PropertyName="ProjectService" TaskParameter="ProjectService" />
</CreateSharePointProjectService>
So that OutDir points to $(TargetDir).
Am I missing something as to why I'm getting this error where now a method cannot be found? This error is very exasperating as there is no information on the web regardless of the Google Fu employed!
Update
I've pulled apart the Microsoft.VisualStudio.SharePoint.dll that's on the build server. There is no PathUtils class or Namespace. Could I possibly have a bad version of this file? How can I detect this? Should I install the Sharepoint SDK on the build server. It already has Sharepoint 2010 installed on it.
Update 2
I checked the GAC. The Microsoft.VisualStudio.Sharepoint assembly shows up. However, I can only find it when I'm running the x64 version of the Visual Studio Command Prompt. When I run the normal one I get no assembly back. I'm assuming that is because the Sharepoint assembly is 64 bit. As far as I know TFS is setup to be 64bit. Is this going to be my problem?
The PathUtils.HasIllegalDeploymentPathCharacters method is present in version 10.0.40219.1 of Microsoft.VisualStudio.SharePoint.Designers.Models.dll and not in version 10.0.30319.1 (where I was seeing this error).
You are missing the assembly "Microsoft.VisualStudio.SharePoint.Designers.Models.dll"
The following assemblies must be copied to the GAC of the build system:
Microsoft.VisualStudio.SharePoint.Designers.Models.dll
Microsoft.VisualStudio.SharePoint.Designers.Models.Features.dll
Microsoft.VisualStudio.SharePoint.Designers.Models.Packages.dll
Microsoft.VisualStudio.SharePoint.dll
Please refer to the following article for more information about the required assemblies:
http://msdn.microsoft.com/en-us/ff622991.aspx
Regards,
Wes MacDonald
I found a solution to this issue. I don't think anyone has ever encountered this so I'm doubtful there will be a "correct" solution. I will post here what I have done to allow my .wsp files to build in the solution.
By all means, please post an answer (or comment on either this answer or the original question) if you think there is a better solution or if my manner of solving the problem is not up to par.
I will explain this in steps that I came up with to solve the problem.
First Step
The task PackageFiles was giving me the issue. This task was unable to find a method to invoke. Looking at the file C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\SharePointTools\Microsoft.VisualStudio.SharePoint.targets we can find this on line 56:
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="PackageFiles" />
I know knew where to look for the PackageFiles task/class.
Step Two
After knowing where to look I decompiled the task. I used Telerik's JustDecompile but I also came up with the same code in Reflector.
I could clearly see the line:
if (PathUtils.HasIllegalDeploymentPathCharacters(str2))
Which was erroring.
Step Three
I ended up deciding that the PathUtils.HasIllegalDeploymentPathCharacters method was just there as a safety check. I could recreate this task in my own custom library and then insert it into a custom targets file.
Here was the class I came up with:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.SharePoint.Tasks;
using Microsoft.Build.Framework;
namespace SharepointTaskLibrary
{
public class PackageFiles : BuildTask
{
[Required]
public ITaskItem LayoutPath
{
get;
set;
}
[Required]
public ITaskItem PackagePath
{
get;
set;
}
public PackageFiles()
{
}
protected override void OnCheckParameters()
{
if (this.LayoutPath == null)
{
throw new InvalidOperationException(Strings.GetString("LayoutPathNotSpecified"));
}
if (this.PackagePath == null)
{
throw new InvalidOperationException(Strings.GetString("PackagePathNotSpecified"));
}
}
protected override void OnExecute()
{
object[] objArray;
object[] objArray2;
object[] objArray3;
string metadata = this.LayoutPath.GetMetadata("FullPath");
string str1 = this.PackagePath.GetMetadata("FullPath");
Assembly sharepointTasksAss = Assembly.Load("Microsoft.VisualStudio.SharePoint.Tasks");
if (sharepointTasksAss != null)
base.Log.LogMessage(MessageImportance.High, "Found Tasks assembly!");
else
{
base.Log.LogError("Couldn't find the tasks assembly");
return;
}
if (!Directory.Exists(metadata))
{
base.Log.LogErrorFromResources("LayoutPathDoesNotExist", new object[] { metadata });
}
else
{
MethodInfo createCabMethod = GetStaticMethod(sharepointTasksAss, "Microsoft.VisualStudio.SharePoint.Tasks.Utilities.CabCreator", "CreateCabinet");
if (createCabMethod == null)
{
base.Log.LogError("the method could not be retrieved on type.");
return;
}
else
base.Log.LogMessage(MessageImportance.High, "Found method: " + createCabMethod.Name);
IEnumerable<string> strs = createCabMethod.Invoke(null, new object[] { metadata, str1 }) as IEnumerable<string>;
/*
* The following code would error in the original task.
*/
//foreach (string str2 in strs)
//{
// if (PathUtils.HasIllegalDeploymentPathCharacters(str2))
// {
// base.Log.LogWarningFromResources("FileNameContainsIllegalDeploymentPathCharacters", new object[] { str2 });
// }
//}
base.Log.LogMessage(MessageImportance.High, Strings.GetString("PackageCreatedSuccessfully"), new object[] { str1 });
}
Type codeMarkersType = null;
try
{
codeMarkersType = sharepointTasksAss.GetType("Microsoft.Internal.Performance.CodeMarkers", true);
}
catch (Exception e)
{
base.Log.LogErrorFromException(e, true);
}
if (codeMarkersType == null)
{
base.Log.LogError("Couldn't get the CodeMarkers class!");
return;
}
else
base.Log.LogMessage(MessageImportance.High, "Found the type: " + codeMarkersType.FullName);
/*
* This has yet to be added back in.
*/
//CodeMarkers.Instance.CodeMarker(CodeMarkerEvent.perfSharePointPackageWspPackageEnd);
}
private MethodInfo GetStaticMethod(Assembly assembly, string typeName, string methodName)
{
Type type = null;
try
{
type = assembly.GetType(typeName, true);
}
catch (Exception e)
{
base.Log.LogErrorFromException(e, true);
}
if (type == null)
{
base.Log.LogError("Couldn't get the type: " + typeName);
return null;
}
else
base.Log.LogMessage(MessageImportance.High, "Found the type: " + type.FullName);
MethodInfo methodInfo = type.GetMethod(methodName, BindingFlags.Static);
if (methodInfo == null)
{
MethodInfo[] methods = type.GetMethods().Union(type.GetMethods(BindingFlags.Static)).ToArray();
base.Log.LogWarning(string.Format("Wasn't able to find {0} directly. Searching through the static {1} method(s) on {2}", methodName, methods.Length, type.FullName));
foreach (MethodInfo info in methods)
{
if (info.Name == methodName && methodInfo == null)
methodInfo = info;
}
if (methodInfo == null)
{
MemberInfo[] members =
type.GetMembers().Union(type.GetMembers(BindingFlags.Static | BindingFlags.NonPublic)).Union(type.GetMembers(BindingFlags.NonPublic)).ToArray();
base.Log.LogWarning(string.Format("Wasn't able to find {0}. Searching through the {1} members(s) on {2}", methodName, methods.Length, type.FullName));
MemberInfo createCabMember = null;
foreach (MemberInfo member in members)
{
if (member.Name == methodName)
{
createCabMember = member;
break;
}
else
base.Log.LogMessage(MessageImportance.High, "Found member: " + member.Name);
}
if (createCabMember == null)
base.Log.LogError("Still wasn't able to find " + methodName + " in the members!");
}
}
return methodInfo;
}
}
}
Since most of the classes and methods are marked as internal I had to make use reflection to get the type and method needed to actually build the cab/wsp files. This is done in the method: GetStaticMethod
Step Four
If you read over the decompiled code and my custom version of the class you'll notice the Strings class. It appears to be a resource accessor class. I decided that I'd just decompile that code as well and use it in my solution that makes the custom task instead of reflecting every time I wanted to access a string resource. This file ended up not being a straight decompile as it has a line this.GetType().Assembly it uses to get the current assembly containing the resources. This works fine within the original assembly but causes a problem in this custom assembly.
The original line:
internal Strings()
{
this.resources = new ResourceManager("Strings", this.GetType().Assembly);
}
This line had to be changed to:
Assembly sharepointTasksAss = Assembly.Load("Microsoft.VisualStudio.SharePoint.Tasks");
this.resources = new ResourceManager("Strings", sharepointTasksAss);
Step Five
After I had a custom build task that mimics the original I needed to now place that into the targets file. I then backed up the original targets file and made a custom one replacing the UsingTask section like this:
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="CreateSharePointProjectService" />
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="EnumerateFiles" />
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="EnumerateFeature" />
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="EnumeratePackage" />
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="EnumerateProjectItem" />
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="LayoutFiles" />
<!-- The next task is a mimic of the one from the other assembly. I decompiled it and recreated it so it wouldn't error. LOL -->
<UsingTask AssemblyFile="C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\SharePointTools\SharepointTaskLibrary.dll" TaskName="PackageFiles" />
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="ResolveProjectMember" />
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="SetPackagingProperties" />
<UsingTask AssemblyFile="Microsoft.VisualStudio.SharePoint.Tasks.dll" TaskName="ValidatePackage" />
This made the task point to my DLL which contained the custom task. Specifically, this line:
<UsingTask AssemblyFile="C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\SharePointTools\SharepointTaskLibrary.dll" TaskName="PackageFiles" />
FINALLY
I dropped the compiled DLL and edited targets file into the C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\SharePointTools directory (again backing up the original targets file).
This allowed me to build via TFS 2010 with custom outputs the wsp files generated by the SharePoint solutions!
I used this site as a resource:
http://blogs.like10.com/2011/08/04/team-build-2010-customized-output-directories-sharepoint-2010-wsps/
(I may have used another one or two sites as a resource, but I can find them in the browser history at the moment).
Your mileage may vary, but please let me know if anyone has this similar issue and is able to fix it in a non "hacked" way.
UPDATE
This whole issue seems to have came from the original TFS install I was administering. I recently moved our team to a proper TFS server (2012) with a completely fresh OS install and a new database server. Once I migrated the databases over and ran the upgrade tasks in TFS I was able to do some small build edits to make my build work with 2012 and I did not encounter this issue a second time. I believe that because the original 2010 TFS was on a converted dev machine it caused this problem.

.NET build error

Having added the subsonic 2.2 subcommander sonic.exe as an external tool I can generate my DAL classes in my defined \dataaccess\generated\ folder but when I build the project I get an error in the following file:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\subsonictest\bdf9ac02\aff68c1c\App_Code.2ygn7ole.0.cs in the following:
Code:
/// <summary>
/// Creates an object wrapper for the iData_sp_GenerateDataSQL Procedure
/// </summary>
public static StoredProcedure IDataSpGenerateDataSQL(string TABLE, string IDENTITYCOL)
{
SubSonic.StoredProcedure sp = new
SubSonic.StoredProcedure("iData_sp_GenerateDataSQL",
DataService.GetInstance("KLA"),
"PUZZLE\mnolan");
sp.Command.AddParameter("#TABLE", TABLE, DbType.AnsiString, null, null);
sp.Command.AddParameter("#IDENTITYCOL", IDENTITYCOL, DbType.AnsiString, null, null);
return sp;
}
The error message is - error CS1009 Unrecognized escape sequence and shows the error is associated with the PUZZLE\mnolan string.
I can escape the sequence with '\' but this won't help because this is a temporary build file and is regenerated.
Thanks for the help,
Mike
Try :
#"PUZZLE\mnolan"
Backslashes are special characters in C# strings. The # tells C# to treat them literally. You could double the backslash instead.

Resources