I have an XNA project I have that compiles perfectly fine in Visual Studio 2015, that makes use of C# 6 features.
Previously, I had some tools coded in C# that would auto-compile the project when it was using C# 5 under VS 2013, but the change to C# 6 has broken things somehow.
To reproduce this I created a new console application, referenced Microsoft.Build, Microsoft.Build.Framework, and Microsoft.Build.Utilities.Core assemblies from C:\Program Files (x86)\MSBuild\14.0\Bin.
I then created my C# compiler project with the following code:
namespace CompileXna
{
class Program
{
static void Main(string[] args)
{
const string solutionPath = #"D:\Code\My Projects\FrbTest\FrbTest.sln";
var properties = new Dictionary<string, string>()
{
{"Configuration", "Debug"},
{"Platform", "x86" }
};
var parameters = new BuildParameters
{
Loggers = new ILogger[] {new BuildLogger()}
};
var request = new BuildRequestData(solutionPath, properties, null, new string[] {"Build"}, null);
var manager = BuildManager.DefaultBuildManager.Build(parameters, request);
Console.WriteLine("Done");
Console.ReadLine();
}
}
class BuildLogger : Logger
{
public override void Initialize(IEventSource eventSource)
{
eventSource.ErrorRaised += (sender, args) =>
Console.WriteLine($"Error: File {args.File} line {args.LineNumber} message {args.Message}");
eventSource.WarningRaised += (sender, args) =>
Console.WriteLine($"Warning: File {args.File} line {args.LineNumber} message {args.Message}");
}
}
}
Unfortunately, when this runs I get the following error:
Error: File C:\Program Files (x86)\MSBuild\Microsoft\XNA Game Studio\v4.0\Microsoft.Xna.GameStudio.ContentPipeline.targets line 78 message The "BuildContent" task could not be instantiated from the assembly "Microsoft.Xna.Framework.Content.Pipeline, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553". Please verify the task assembly has been built using the same version of the Microsoft.Build.Framework assembly as the one installed on your computer and that your host application is not missing a binding redirect for Microsoft.Build.Framework. Unable to cast object of type 'Microsoft.Xna.Framework.Content.Pipeline.Tasks.BuildContent' to type 'Microsoft.Build.Framework.ITask'.
I tried looking in the details of the build output in Visual Studio 2015 when it successfully compiles my XNA project, but even with Detailed logging all I could find (seemingly) relevant was:
1>Using "BuildContent" task from assembly "Microsoft.Xna.Framework.Content.Pipeline, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553".
1>Task "BuildContent"
1>Done executing task "BuildContent".
Anyone have any success on this?
Related
The application is in Azure Functions,
The error that we are getting from container Pod logs is "Could not load type 'Microsoft.Azure.WebJobs.Host.Scale.ConcurrencyManager' from assembly 'Microsoft.Azure.WebJobs.Host, Version=3.0.26.0".
In our application version all of the dll ver is 3.0.30.0
In the "dll" folder of debug is having the version with 3.0.30.0
And in this version 3.0.30.0, it has the class "Microsoft.Azure.WebJobs.Host.Scale.ConcurrencyManager"
Not sure, where this "assembly 'Microsoft.Azure.WebJobs.Host, Version=3.0.26.0" is coming from.
For me this was happening because Azure Functions Core Tools version mismatched due to upgradation of Visual Studio to latest version.
Removing the Azure Function Tools from the system path C:\Users\user.name\AppData\Local\AzureFunctionsTools and Let Visual Studio automatically install Azure Functions Core Tools fixed the issue.
I had the same issue as below log.
Could not load type 'Microsoft.Azure.WebJobs.Host.Scale.ConcurrencyManager' from assembly 'Microsoft.Azure.WebJobs.Host, Version=3.0.25.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.
It was due to my base image for azure functions was old. using the newer base image with below tag(mcr.microsoft.com/azure-functions/dotnet:3.4.2) has fixed my issue.
FROM mcr.microsoft.com/azure-functions/dotnet:3.4.2 AS base
WORKDIR /home/site/wwwroot
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:3.1.416 AS build
WORKDIR /src
This is not direct answer to your question but a tool that would answer it for you. As I had a lot of this kind of errors I have written a helper code to do just that. Its written for the .net framework but with minor changes you can have same thing on core.
//folder where dependencies should be found
var dir = #"C:\Repos\Project\bin";
//dll or exe that you want to inspect
var dll = #"C:\Repos\Project\bin\Project.dll";
var asm = Assembly.ReflectionOnlyLoadFrom(dll);
var stack = new Stack<Data>();
stack.Push(new Data {
ReferencesPath = Array.Empty<Assembly>(),
Assembly = asm
});
List<AssemblyName> visited = new List<AssemblyName>();
while (stack.Any())
{
var current = stack.Pop();
var dependencies = current.Assembly.GetReferencedAssemblies();
visited.Add(current.Assembly.GetName());
foreach (var item in dependencies)
{
if (!visited.Any(x => x.FullName == item.FullName))
{
Assembly dependency;
try
{
dependency = Assembly.ReflectionOnlyLoad(item.FullName);
}
catch
{
var path = Path.Combine(dir, item.Name) + ".dll";
dependency = Assembly.ReflectionOnlyLoadFrom(path);
}
if (dependency.GetName().Version != item.Version)
{
; // put breakpoint here and inspect dependency
// and item when you find your dll in wrong version
// you can inspect current.ReferencesPath to see dependencies
// chain that causes the error
}
stack.Push(new Data
{
Assembly = dependency,
ReferencesPath = current.ReferencesPath.Concat(
new[] { current.Assembly }).ToArray()
});
}
}
}
class Data
{
public Assembly[] ReferencesPath { get; set; }
public Assembly Assembly { get; internal set; }
}
I have some server app. This app run, read some files, create C# classes, build this and load assembly. This app can work in two modes - one mode is window desktop application, and other mode - as windows service but core in dll is common.
Sometimes when this app work long time as service, and machine server has long timeup, they can't build anything. I attach to debugger, and debug. I debug .NET source (CompileAssemblyFromSource), and I see, that .NET classes just run csc.exe process with some params (CSharpCodeProvider class), but csc.exe run, return no errors or exceptions, output is blank and nothing is happend. No assembly is build.
I wrote some dump test service to compile code:
namespace CompilerService
{
public class Compiler
{
private Task _compilerTask;
public Compiler()
{
_compilerTask = Task.Run(() => CompileHalloWorld());
}
private const string _workingDir = #"C:\tmp";
private void CompileHalloWorld()
{
System.Threading.Thread.Sleep((30000));
if (!Directory.Exists(_workingDir))
{
Directory.CreateDirectory(_workingDir);
}
Directory.SetCurrentDirectory(_workingDir);
var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true);
parameters.GenerateExecutable = true;
CompilerResults results = null;
try
{
results = csc.CompileAssemblyFromSource(parameters,
#"using System;
class Program {
public static void Main(string[] args) {
Console.WriteLine(""Hallo World!"");
}
}");
}
catch (Exception e)
{
int a = 2;
}
results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));
}
}
}
This dump service is fail too with build hallo world in this state of machine.
After restart machine, all work again ok, compile and load assembly all the time. After few weeks, problem come back, and we must reset server. This problem is on only one machine. On otger machines this service and csc.exe work perfect from years.
If machine is in this wird state, csc.exe dont build in windows service app, but when We run this app as Windows Desktop App all work fine, and csc.exe build normal...
Can you tell me, this is some known issue, oraz is some solution of don't compile csc.exe without machine restart?
I created one console application to create visual studio project pragmatically, here i am not able install Nuget packages, always
var componentModel = (IComponentModel)Package.GetGlobalService(typeof(SComponentModel)); statement returns null values. for your reference i added my code below. Help me to resolve this issue.
static void Main(string[] args)
{
//InstallNuGetPackages.InstallNuGet("");
string ProjectName = "WebAPIProj";
string SolutionName = "EmptyTemplate";
System.Type type = System.Type.GetTypeFromProgID("VisualStudio.DTE.11.0");
Object obj = System.Activator.CreateInstance(type, true);
EnvDTE.DTE dte = (EnvDTE.DTE)obj;
dte.MainWindow.Visible = true; // optional if you want to See VS doing its thing
// create a new solution
dte.Solution.Create("C:\\"+ SolutionName + "\\", SolutionName);
var solution = dte.Solution;
// create a C# WinForms app
solution.AddFromTemplate(#"C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\ProjectTemplatesCache\CSharp\Web\1033\EmptyWebApplicationProject40\EmptyWebApplicationProject40.vstemplate",
#"C:\NewSolution\"+ ProjectName, ProjectName);
InstallNuGetPackages.InstallNuGet(dte);
foreach (var p in dte.Solution.Projects)
{
InstallNuGetPackages.InstallNuGet((Project)p, "Microsoft.AspNet.WebApi version1.1");
}
// save and quit
dte.ExecuteCommand("File.SaveAll");
dte.Quit();
}
Code to install Nuget Packages
public bool InstallNuGetPackage(Project project, string package)
{
bool installedPkg = true;
try
{
var componentModel = (IComponentModel)Package.GetGlobalService(typeof(SComponentModel)); //Always this statement returns null
IVsPackageInstallerServices installerServices = componentModel.GetService();
if (!installerServices.IsPackageInstalled(project, package))
{
var installer = componentModel.GetService();
installer.InstallPackage(null, project, package, (System.Version)null, false);
}
}
catch (Exception ex)
{
installedPkg = false;
}
return installedPkg;
}
(Turned this into an answer for better readability and more room)
created one console application - you only have access to the ServiceProvider from Visual Studio if you run your code inside of it, i.e. from an extension and/or package.
Running this from a console application cannot work. Visual Studio internally does a lot more setup for all the services and general environment than creating an instance of DTE.
To persue your route, although I'm not sure how feasible that is, invoke nuget.exe or NuGet.Core code to achieve similar.
I have a Visual Studio extensions that use Roslyn to get a project in current opened solution, compile it and run methods from it. The project can be modified by the programmer.
I have successfully compiled a project in a Visual Studio extension from the current VisualStudioWorkspace.
private static Assembly CompileAndLoad(Compilation compilation)
{
using (MemoryStream dllStream = new MemoryStream())
using (MemoryStream pdbStream = new MemoryStream())
{
EmitResult result = compilation.Emit(dllStream, pdbStream);
if (!result.Success)
{
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
string failuresException = "Failed to compile code generation project : \r\n";
foreach (Diagnostic diagnostic in failures)
{
failuresException += $"{diagnostic.Id} : {diagnostic.GetMessage()}\r\n";
}
throw new Exception(failuresException);
}
else
{
dllStream.Seek(0, SeekOrigin.Begin);
return AppDomain.CurrentDomain.Load(dllStream.ToArray(), pdbStream.ToArray());
}
}
}
Then I can load the assembly in current domain, get it's types and invoke methods.
The problem is that I need to allow the programmer to put breakpoints if the current configuration of the loaded solution is debug.
I need to run some code in current Visual Studio Host from an extension and allow it to be debugged in the current Visual Studio instance.
Seems like this is actually impossible.
The current visual studio instance cannot debug itself.
I've tried creating a Console Application with roslyn, attaching it to the debugger and then run the generation code from it. But the VisualStudioWorkspace is only available inside VisualStudio (Not serializable and not avalaible througt DTE com interface). So the only solution left was using MBBuildWorkspace. Since it does not have that same behavior as Visual studio workspace, I've abandoned the project.
Here's my code for further references :
Process vsProcess = Process.GetCurrentProcess();
string solutionPath = CurrentWorkspace.CurrentSolution.FilePath;
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText($#"
using System;
using System.Threading.Tasks;
namespace CodeGenApplication
{{
public class Program
{{
public static void Main(string[] args)
{{
Console.ReadLine();
int vsProcessId = Int32.Parse(args[0]);
CodeGenApp.Test(""{solutionPath.Replace(#"\", #"\\")}"", ""{projectName}"", ""{_codeGenProjectName}"");
Console.ReadLine();
}}
}}
}}");
string assemblyName = Path.GetRandomFileName();
Project codeGenProject = CurrentWorkspace.CurrentSolution.Projects.Where(x => x.Name == _codeGenProjectName).FirstOrDefault();
List<MetadataReference> references = codeGenProject.MetadataReferences.ToList();
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(OutputKind.ConsoleApplication));
// Emit assembly to streams.
EmitResult result = compilation.Emit("CodeGenApplication.exe", "CodeGenApplication.pdb");
if (!result.Success)
{
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
}
else
{
Process codeGenProcess = new Process();
codeGenProcess.StartInfo.FileName = "CodeGenApplication.exe";
codeGenProcess.StartInfo.Arguments = vsProcess.Id.ToString();
codeGenProcess.StartInfo.UseShellExecute = false;
codeGenProcess.StartInfo.CreateNoWindow = true;
codeGenProcess.StartInfo.LoadUserProfile = true;
codeGenProcess.StartInfo.RedirectStandardError = true;
codeGenProcess.StartInfo.RedirectStandardInput = true;
codeGenProcess.StartInfo.RedirectStandardOutput = false;
codeGenProcess.Start();
foreach (EnvDTE.Process dteProcess in _dte.Debugger.LocalProcesses)
{
if (dteProcess.ProcessID == codeGenProcess.Id)
{
dteProcess.Attach();
}
}
codeGenProcess.StandardInput.WriteLine("Start");
}
You have to attach the debugger to the currently running visual studio host. To do that you need to:
Get hold of the DTE object,
find the (current) process in DTE.Debugger.LocalProcesses
Attach the debugger - example
run method from the compiled assembly
You'd probably want a separate command/button for this, don't just switch on the current build configuration. That's how everybody else does it (e.g. test runners and even Visual Studio). Also, load the compiled Assembly in a new AppDomain, otherwise it'll stick around for ever.
There's also the more "creative" solution of injecting a System.Diagnostics.Debugger.Launch() call to the method you're about to run (modify the correct Compilation.SyntaxTrees before calling Emit) - but seriously, don't do this.
I'm looking for a way to run code by executing the following steps:
Receiving a list of NuGet packages (a list of tuples ("package name", "package version", "path to main class").
Retrieving them in a local directory (cf code sample #1)
Loading them in my program at run-time
Running the main classes by introspection (cf code sample #2)
By now I am struggling with the third step. I can't find out how to load my package at run-time.
My main question are:
How can I find out in which folders were stored the retrieved packages?
How can I load the content of those directories into my program?
Code Sample #1:
private static void getPackageByNameAndVersion(string packageID, string version)
{
IPackageRepository repo =
PackageRepositoryFactory.Default
.CreateRepository("https://packages.nuget.org/api/v2");
string path = "C:/tmp_repo";
PackageManager packageManager = new PackageManager(repo, path);
Console.WriteLine("before dl pkg");
packageManager.InstallPackage(packageID, SemanticVersion.Parse(version));
}
Code sample #2:
private static void loadByAssemblyNameAndTypeName(string assemblyName, string typeName)
{
AppDomain isolationAppDomain = AppDomain.CreateDomain("tmp");
object a = isolationAppDomain.CreateInstanceAndUnwrap(assemblyName, typeName);
Type x = a.GetType();
MethodInfo m = x.GetMethod("Main");
m.Invoke(a, new object[] { });
}
Grab a cup of coffee :)
Downloading the nuget package?
Nuget.Core (nuget package) is a good choice, and here is a snippet of code that I have that should be able to download a nuget package by id and version
var repo = PackageRepositoryFactory.Default
.CreateRepository("https://packages.nuget.org/api/v2");
string path = "c:\\temp";
var packageManager = new PackageManager(repo, path);
packageManager.PackageInstalled += PackageManager_PackageInstalled;
var package = repo.FindPackage("packageName", SemanticVersion.Parse("1.0.0"));
if (package != null)
{
packageManager.InstallPackage(package, false, true);
}
Notice that I plugged an event handler to the PackageInstalled event of the PackageManager class.
How do we load an assembly in an isolated app domain?
Since reflection API does not provide a way to load an assembly in a specific domain, We will create a proxy class that act as a loader in our isolated domain:
public class TypeProxy : MarshalByRefObject
{
public Type LoadFromAssembly(string assemblyPath, string typeName)
{
try
{
var asm = Assembly.LoadFile(assemblyPath);
return asm.GetType(typeName);
}
catch (Exception) { return null; }
}
}
And now, is how to put it all together?
Here comes the complex part:
private static void PackageManager_PackageInstalled(object sender,
PackageOperationEventArgs e)
{
var files = e.FileSystem.GetFiles(e.InstallPath, "*.dll", true);
foreach (var file in files)
{
try
{
AppDomain domain = AppDomain.CreateDomain("tmp");
Type typeProxyType = typeof(TypeProxy);
var typeProxyInstance = (TypeProxy)domain.CreateInstanceAndUnwrap(
typeProxyType.Assembly.FullName,
typeProxyType.FullName);
var type = typeProxyInstance.LoadFromAssembly(file, "<KnownTypeName>");
object instance =
domain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
}
catch (Exception ex)
{
Console.WriteLine("failed to load {0}", file);
Console.WriteLine(ex.ToString());
}
}
}
Notice that this method is the event handler that gets executed after downloading the nuget package
Also
Note that you will need to replace <KnownTypeName> with the expected type name coming from the assembly (or maybe run a discovery of all public types in the assembly)
Worth noting that I haven't executed this code myself and cannot guarantee that it will work out of the box, and still might need some tweaking. but Hopefully it is the concept that allows you to solve the problem.
Don't do that! You are probably trying to load NuGet content at a customers computer to save some space on distribution of your software. Isn't it that?
The common recommended approach is to download the NuGet content as the second step of an automated build (after downloading the source code), build the software and run the automated tests with the NuGet content you have downloaded. And then distribute the build with the NuGet content you have tested as the complex whole unit.