VS for Mac extension - null Editor in ActiveWindow - c#

I'm trying to get into developing an extension for Visual Studio for Mac. I'm using this tutorial. Everything had been going well until I tried to run my extension. In my case "Insert date" in Edit submenu is disabled. While debugging I've noticed that IdeApp.Workbench.ActiveDocument.Editor is null despite I have an open document. Here's my code
using System;
using MonoDevelop.Components.Commands;
using MonoDevelop.Ide;
namespace ExampleIDEExtension
{
public class InsertDateHandler : CommandHandler
{
protected override void Run()
{
var editor = IdeApp.Workbench.ActiveDocument.Editor;
var currentTime = DateTime.Now.ToString();
editor.InsertAtCaret(currentTime);
}
protected override void Update(CommandInfo info)
{
info.Enabled = IdeApp.Workbench.ActiveDocument.Editor != null;
}
}
}
I have no idea why Editor is null despite having an open document.

The editor is null, as Monodevelop is using Microsoft.VisualStudio.Text.Editor and it mentions that the API has been obsolete in the below link.
https://github.com/mono/monodevelop/blob/50fbe0a7e65c5439e3313c6b50e7ef927f5f1fe9/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs
Anyways, to answer your question, this is what I had to do to achieve the insert date handler demo addin
protected override void Run()
{
var textBuffer = IdeApp.Workbench.ActiveDocument.GetContent<ITextBuffer>();
var date = DateTime.Now.ToString();
var textView = IdeApp.Workbench.ActiveDocument.GetContent<ITextView>();
var caretPosition = textView.Caret.Position;
textBuffer.Insert(caretPosition.BufferPosition.Position,date);
}
protected override void Update(CommandInfo info)
{
var textBuffer = IdeApp.Workbench.ActiveDocument.GetContent<ITextBuffer>();
if (textBuffer != null && textBuffer.AsTextContainer() is SourceTextContainer container)
{
var document = container.GetTextBuffer();
if (document != null)
{
info.Enabled = true;
}
}
}

Just to embellish vin's answer for others still struggling with creating extensions on Visual Studio for Mac it needs to look like this in InsertDateHandler.cs:
using System;
using MonoDevelop.Components.Commands;
using MonoDevelop.Ide;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.CodeAnalysis.Text;
namespace DateInserter
{
class InsertDateHandler : CommandHandler
{
protected override void Run()
{
var textBuffer = IdeApp.Workbench.ActiveDocument.GetContent<ITextBuffer>();
var date = DateTime.Now.ToString();
var textView = IdeApp.Workbench.ActiveDocument.GetContent<ITextView>();
var caretPosition = textView.Caret.Position;
textBuffer.Insert(caretPosition.BufferPosition.Position, date);
}
protected override void Update(CommandInfo info)
{
var textBuffer = IdeApp.Workbench.ActiveDocument.GetContent<ITextBuffer>();
if (textBuffer != null && textBuffer.AsTextContainer() is SourceTextContainer container)
{
var document = container.GetTextBuffer();
if (document != null)
{
info.Enabled = true;
}
}
}
}
}
The next piece of the puzzle that seems to be missing everywhere is just exactly how to package up the extension so you can both install it yourself in your version if Visual studio for mac and how to share the extension for others.
Here is how I do it:
Open Terminal on the Mac, navigate to the .dll you just created when you build the extension for example:
/volumes/ssd1/Myapps/VSCodeextensions/dateinserter/dateinserter/bin/debug/net472
Then create a .mpack file by doing this:-
% mono /Applications/"Visual Studio.app"/Contents/Resources/lib/monodevelop/bin/vstool.exe setup pack DateInserter.dll

Related

“Invalid window handle” error when using FileOpenPicker from C# .net framwork 4.7.2 with Microsoft.Windows.SDK.Contracts without UWP

I am trying to use Microsoft.Windows.SDK.Contracts to access the Windows10 API from .net framework WFP application.
I want to use the FileOpenPicker() to select the image for OCR processing by Windows.Media.Ocr. But I met the 'Invalid window handle' error when using the picker
I found a post which met the similar a link issue with C++/WinRT. One of the answer point out " The program will crash because the File­Open­Picker looks for a Core­Window on the current thread to serve as the owner of the dialog. But we are a Win32 desktop app without a Core­Window." I think the root cause is the same. But I don't know how to fix from the my code based on .net framework side.
public async void Load()
{
var picker = new FileOpenPicker()
{
SuggestedStartLocation = PickerLocationId.PicturesLibrary,
FileTypeFilter = { ".jpg", ".jpeg", ".png", ".bmp" },
};
var file = await picker.PickSingleFileAsync();
if (file != null)
{
}
else
{
}
}
Error message: System.Exception: 'Invalid window handle.(Exception from HRESULT:0x80070578)'
Create a file with:
using System;
using System.Runtime.InteropServices;
namespace <standardnamespace>
{
[ComImport]
[Guid("3E68D4BD-7135-4D10-8018-9FB6D9F33FA1")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IInitializeWithWindow
{
void Initialize(IntPtr hwnd);
}
}
change your code to:
public async void Load()
{
var picker = new FileOpenPicker()
{
SuggestedStartLocation = PickerLocationId.PicturesLibrary,
FileTypeFilter = { ".jpg", ".jpeg", ".png", ".bmp" },
};
((IInitializeWithWindow)(object)picker).Initialize(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle);
var file = await picker.PickSingleFileAsync();
if (file != null)
{
}
else
{
}
}

Unity Add Default Namespace to Script Template?

I just found Unity's script template for C# scripts. To get the script name you write #SCRIPTNAME# so it looks like this:
using UnityEngine;
using System.Collections;
public class #SCRIPTNAME# : MonoBehaviour
{
void Start ()
{
}
void Update ()
{
}
}
Then it would create the script with the right name, but is there something like #FOLDERNAME# so that I can put it in the right namespace directly when creating the script?
There is no built-in template variables like #FOLDERNAME#.
According to this post, there are only 3 magic variables.
"#NAME#"
"#SCRIPTNAME#"
"#SCRIPTNAME_LOWER#"
But you can always hook into the creation process of a script and append the namespace yourself using AssetModificationProcessor.
Here is an example that adds some custom data to the created script.
//Assets/Editor/KeywordReplace.cs
using UnityEngine;
using UnityEditor;
using System.Collections;
public class KeywordReplace : UnityEditor.AssetModificationProcessor
{
public static void OnWillCreateAsset ( string path )
{
path = path.Replace( ".meta", "" );
int index = path.LastIndexOf( "." );
string file = path.Substring( index );
if ( file != ".cs" && file != ".js" && file != ".boo" ) return;
index = Application.dataPath.LastIndexOf( "Assets" );
path = Application.dataPath.Substring( 0, index ) + path;
file = System.IO.File.ReadAllText( path );
file = file.Replace( "#CREATIONDATE#", System.DateTime.Now + "" );
file = file.Replace( "#PROJECTNAME#", PlayerSettings.productName );
file = file.Replace( "#SMARTDEVELOPERS#", PlayerSettings.companyName );
System.IO.File.WriteAllText( path, file );
AssetDatabase.Refresh();
}
}
I know it's and old question but in newer versions of Unity you can define a root namespace to be used in the project.
You can define the namespace in Edit > Project Settings > Editor > Root Namespace
Doing this will add the defined namespace on newly created scripts.
Using zwcloud's answer and some other resources I was able to generate a namespace on my script files:
First step, navigate:
Unity's default templates can be found under your Unity installation's directory in Editor\Data\Resources\ScriptTemplates for Windows and /Contents/Resources/ScriptTemplates for OSX.
And open the file 81-C# Script-NewBehaviourScript.cs.txt
And make the following change:
namespace #NAMESPACE# {
At the top and
}
At the bottom. Indent the rest so that the whitespace is as desired. Don't save this just yet. If you wish, you can make other changes to the template, such as removing the default comments, making Update() and Start() private, or even removing them entirely.
Again, do not save this file yet or Unity will throw an error on the next step. If you saved, just hit ctrl-Z to undo and then resave, then ctrl-Y to re-apply the changes.
Now create a new script inside an Editor folder inside your Unity Assets directory and call it AddNameSpace. Replace the contents with this:
using UnityEngine;
using UnityEditor;
public class AddNameSpace : UnityEditor.AssetModificationProcessor {
public static void OnWillCreateAsset(string path) {
path = path.Replace(".meta", "");
int index = path.LastIndexOf(".");
if(index < 0) return;
string file = path.Substring(index);
if(file != ".cs" && file != ".js" && file != ".boo") return;
index = Application.dataPath.LastIndexOf("Assets");
path = Application.dataPath.Substring(0, index) + path;
file = System.IO.File.ReadAllText(path);
string lastPart = path.Substring(path.IndexOf("Assets"));
string _namespace = lastPart.Substring(0, lastPart.LastIndexOf('/'));
_namespace = _namespace.Replace('/', '.');
file = file.Replace("#NAMESPACE#", _namespace);
System.IO.File.WriteAllText(path, file);
AssetDatabase.Refresh();
}
}
Save this script as well as saving the changes to 81-C# Script-NewBehaviourScript.cs.txt
And you're done! You can test it by creating a new C# script inside any series of folders inside Assets and it will generate the new namespace definition we created.
I'm well aware that this question has been answered by the awesome people (zwcloud, Darco18 and Alexey).
However, since my namespace organization follows the folder structure in the project, I've put together in a jiffy some minor modification to the code, and I'm sharing it here in case someone needs it and has the same organizational methodology which I'm following.
Please keep in mind that you need to set the root namespace in your project settings in the C# project generation section.
EDIT: I've adjusted the code a bit work with placement in the root folders of Scripts, Editor, etc..
public class NamespaceResolver : UnityEditor.AssetModificationProcessor
{
public static void OnWillCreateAsset(string metaFilePath)
{
var fileName = Path.GetFileNameWithoutExtension(metaFilePath);
if (!fileName.EndsWith(".cs"))
return;
var actualFile = $"{Path.GetDirectoryName(metaFilePath)}\\{fileName}";
var segmentedPath = $"{Path.GetDirectoryName(metaFilePath)}".Split(new[] { '\\' }, StringSplitOptions.None);
var generatedNamespace = "";
var finalNamespace = "";
// In case of placing the class at the root of a folder such as (Editor, Scripts, etc...)
if (segmentedPath.Length <= 2)
finalNamespace = EditorSettings.projectGenerationRootNamespace;
else
{
// Skipping the Assets folder and a single subfolder (i.e. Scripts, Editor, Plugins, etc...)
for (var i = 2; i < segmentedPath.Length; i++)
{
generatedNamespace +=
i == segmentedPath.Length - 1
? segmentedPath[i]
: segmentedPath[i] + "."; // Don't add '.' at the end of the namespace
}
finalNamespace = EditorSettings.projectGenerationRootNamespace + "." + generatedNamespace;
}
var content = File.ReadAllText(actualFile);
var newContent = content.Replace("#NAMESPACE#", finalNamespace);
if (content != newContent)
{
File.WriteAllText(actualFile, newContent);
AssetDatabase.Refresh();
}
}
}
OK, so this question was already answered by two wonderful people, zwcloud and Draco18s, and their solution works, I'm just showing another version of the same code that, I hope, will be a little more clear in terms of what exactly happening.
Quick notes:
Yes, in this method we are getting not the actual file path,
but the path of its meta file as a parameter
No, you can not use AssetModificationProcessor without UnityEditor prefix, it is deprecated
OnWillCreateAsset method is not shown via Ctrl+Shift+M, 'override' typing or base class metadata
_
using UnityEditor;
using System.IO;
public class ScriptTemplateKeywordReplacer : UnityEditor.AssetModificationProcessor
{
//If there would be more than one keyword to replace, add a Dictionary
public static void OnWillCreateAsset(string metaFilePath)
{
string fileName = Path.GetFileNameWithoutExtension(metaFilePath);
if (!fileName.EndsWith(".cs"))
return;
string actualFilePath = $"{Path.GetDirectoryName(metaFilePath)}{Path.DirectorySeparatorChar}{fileName}";
string content = File.ReadAllText(actualFilePath);
string newcontent = content.Replace("#PROJECTNAME#", PlayerSettings.productName);
if (content != newcontent)
{
File.WriteAllText(actualFilePath, newcontent);
AssetDatabase.Refresh();
}
}
}
And this is the contents of my file c:\Program Files\Unity\Editor\Data\Resources\ScriptTemplates\81-C# Script-NewBehaviourScript.cs.txt
using UnityEngine;
namespace #PROJECTNAME#
{
public class #SCRIPTNAME# : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
}
Here is my solution, in my case Unity added the root namespace right before OnWillCreateAsset(), so I had to replace the root namespace in the script with the one I want.
Here is the code
public class AddNameSpace : UnityEditor.AssetModificationProcessor
{
public static void OnWillCreateAsset(string path)
{
var rootNamespace =
string.IsNullOrWhiteSpace(EditorSettings.projectGenerationRootNamespace) ?
string.Empty : $"{EditorSettings.projectGenerationRootNamespace}";
path = path.Replace(".meta", "");
int index = path.LastIndexOf(".");
if (index < 0)
return;
string file = path.Substring(index);
if (file != ".cs" && file != ".js" && file != ".boo")
return;
string formattedNamespace = GetNamespace(path, rootNamespace);
file = System.IO.File.ReadAllText(path);
if (file.Contains(formattedNamespace))
return;
file = file.Replace(rootNamespace, formattedNamespace);
System.IO.File.WriteAllText(path, file);
AssetDatabase.Refresh();
}
private static string GetNamespace(string filePath, string rootNamespace)
{
filePath = filePath.Replace("Assets/Scripts/", "/")
.Replace('/', '.')
.Replace(' '.ToString(), string.Empty);
var splitPath = filePath.Split('.');
string pathWithoutFileName = string.Empty;
for (int i = 1; i < splitPath.Length - 2; i++)
{
if (i == splitPath.Length - 3)
{
pathWithoutFileName += splitPath[i];
}
else
{
pathWithoutFileName += splitPath[i] + '.';
}
}
return $"{rootNamespace}.{pathWithoutFileName}";
}
}

Dynamically Load and Activate a dataset (vuforia and unity)

I want to make a system that I can use to download various targets dynamically from my website without using "Cloud" system.
I also want to save the dataset to .xml and .dat formats which I want to activate from my saving device.
There are a lot of methods and pages to doing that with vuforia and unity, but unfortunately when I test it I receive an error for all of them.
It seems that i have made a mistake in my code or a vuforia class was changed.
For instance please look this link:
https://developer.vuforia.com/library/articles/Solution/Unity-Load-DataSet-from-SD-Card
I got Error: Using Vuforia;
I placed the .xml and .dat files in "Application.persistentDataPath + "/" + "Building1.xml"
i used this Script "DataSetLoadBehavior" that attached "AR Camera and placed my code in it. I got an Error:
NullReferenceException: Object reference not set to an instance of an
object DataSetLoadBehaviour.OnInitialized () (at Assets/Qualcomm
Augmented Reality/Scripts/DataSetLoadBehaviour.cs:49)
DataSetLoadBehaviour.Start () (at Assets/Qualcomm Augmented
Reality/Scripts/DataSetLoadBehaviour.cs:80)
My code is this:
unity 4.2 pro and vuforia 2.8.9 or 3.0.9
/*==============================================================================
Copyright (c) 2010-2014 Qualcomm Connected Experiences, Inc.
All Rights Reserved.
Confidential and Proprietary - Qualcomm Connected Experiences, Inc.
==============================================================================*/
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// This behaviour allows to automatically load and activate one or more DataSet on startup
/// </summary>
public class DataSetLoadBehaviour : DataSetLoadAbstractBehaviour
{
[HideInInspector, SerializeField]
public List<string> mDataSetsToActivate2 = new List<string>();
[SerializeField, HideInInspector]
public List<string> mDataSetsToLoad2 = new List<string>();
protected DataSetLoadBehaviour()
{
}
private void OnDestroy()
{
QCARAbstractBehaviour behaviour = (QCARAbstractBehaviour) UnityEngine.Object.FindObjectOfType(typeof(QCARAbstractBehaviour));
if (behaviour != null)
{
}
}
public void OnInitialized()
{
if (QCARRuntimeUtilities.IsQCAREnabled())
{
foreach (string str in this.mDataSetsToLoad2)
{
if (!DataSet.Exists(str, QCARUnity.StorageType.STORAGE_ABSOLUTE))
{
Debug.LogError("Data set " + str + " does not exist.");
}
else
{
ImageTracker tracker = TrackerManager.Instance.GetTracker<ImageTracker>();
DataSet dataSet = tracker.CreateDataSet();
if (!dataSet.Load(str))
{
Debug.LogError("Failed to load data set " + str + ".");
}
else if (this.mDataSetsToActivate2.Contains(str))
{
tracker.ActivateDataSet(dataSet);
}
}
}
}
}
public void OnTrackablesUpdated()
{
}
private void Start()
{
QCARAbstractBehaviour behaviour = (QCARAbstractBehaviour) UnityEngine.Object.FindObjectOfType(typeof(QCARAbstractBehaviour));
if (behaviour != null)
{
mDataSetsToLoad2.Add(Application.persistentDataPath + "/" + "Building1.xml");
OnInitialized();
}
}
public override void AddOSSpecificExternalDatasetSearchDirs()
{
#if UNITY_ANDROID
if (Application.platform == RuntimePlatform.Android)
{
// Get the external storage directory
AndroidJavaClass jclassEnvironment = new AndroidJavaClass("android.os.Environment");
AndroidJavaObject jobjFile = jclassEnvironment.CallStatic<AndroidJavaObject>("getExternalStorageDirectory");
string externalStorageDirectory = jobjFile.Call<string>("getAbsolutePath");
// Get the package name
AndroidJavaObject jobjActivity = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic<AndroidJavaObject>("currentActivity");
string packageName = jobjActivity.Call<string>("getPackageName");
// Add some best practice search directories
//
// Assumes just Vufroria datasets extracted to the files directory
AddExternalDatasetSearchDir(externalStorageDirectory + "/Android/data/" + packageName + "/files/");
// Assume entire StreamingAssets dir is extracted here and our datasets are in the "QCAR" directory
AddExternalDatasetSearchDir(externalStorageDirectory + "/Android/data/" + packageName + "/files/QCAR/");
}
#endif //UNITY_ANDROID
}
void Update()
{
}
}
Yeah, Vuforia has has changed a lot.
You will now have to include Vuforia as a header in order for it to work
using Vuforia;
Hope this works.
If it says Vuforia hasn't been found it's probably because you haven't imported the Unitypackage for Vuforia. You can follow these instructions.
Also, I believe you haven't followed the steps to Migrating your Unity Project. The new Vuforia doesn't support ImageTracker anymore, hence you will have to change all instances of ImageTracker to ObjectTracker

How do I use the Bing Search API in Windows Phone?

I'm trying to use the Bing Search API to find images as backgrounds to the tiles inside of my app. I've included the BingSearchContainer.cs in my Project but I can't make it work with the sample code provided here.
Any guidelines for how to use the Bing Search API inside of my Windows Phone 8 app would be appriciated!
Thanks for any answer.
I expect that you already have a AccountKey so I wont tell you have to get one.
Implementation
First of all, add the BingSearchContainer.cs to your project
Implement the sample C# code found in the Bing API Quick Start & Code
Thereafter, right-click References and choose Manage NuGet Packages... and search for, and install, Microsoft.Data.Services.Client.WindowsP.
Modify the sample code so that it work with Windows Phone:
using Bing;
using System;
using System.Data.Services.Client;
using System.Linq;
using System.Net;
namespace StackOverflow.Samples.BingSearch
{
public class Finder
{
public void FindImageUrlsFor(string searchQuery)
{
// Create a Bing container.
string rootUri = "https://api.datamarket.azure.com/Bing/Search";
var bingContainer = new Bing.BingSearchContainer(new Uri(rootUri));
bingContainer.UseDefaultCredentials = false;
// Replace this value with your account key.
var accountKey = "YourAccountKey";
// Configure bingContainer to use your credentials.
bingContainer.Credentials = new NetworkCredential(accountKey, accountKey);
// Build the query.
var imageQuery = bingContainer.Image(query, null, null, null, null, null, null);
imageQuery.BeginExecute(_onImageQueryComplete, imageQuery);
}
// Handle the query callback.
private void _onImageQueryComplete(IAsyncResult imageResults)
{
// Get the original query from the imageResults.
DataServiceQuery<Bing.ImageResult> query =
imageResults.AsyncState as DataServiceQuery<Bing.ImageResult>;
var resultList = new List<string>();
foreach (var result in query.EndExecute(imageResults))
resultList.Add(result.MediaUrl);
FindImageCompleted(this, resultList);
}
public event FindImageUrlsForEventHandler FindImageUrlsForCompleted;
public delegate void FindImageUrlsForEventHandler(object sender, List<string> result);
}
}
Example
And now, let's use the code I provided you with:
using Bing;
using System;
using System.Data.Services.Client;
using System.Linq;
using System.Net;
namespace StackOverflow.Samples.BingSearch
{
public class MyPage
{
private void Button_Click_1(object sender, RoutedEventArgs e)
{
var finder = new Finder();
finder.FindImageUrlsForCompleted += finder_FindImageUrlsForCompleted;
finder.FindImageUrlsFor("candy");
}
void finder_FindImageUrlsForCompleted(object sender, List<string> result)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
foreach (var s in result)
MyTextBox.Text += s + "\n";
});
}
}
}

How do I compile a C# solution with Roslyn?

I have a piece of software that generates code for a C# project based on user actions. I would like to create a GUI to automatically compile the solution so I don't have to load up Visual Studio just to trigger a recompile.
I've been looking for a chance to play with Roslyn a bit and decided to try and use Roslyn instead of msbuild to do this. Unfortunately, I can't seem to find any good resources on using Roslyn in this fashion.
Can anyone point me in the right direction?
You can load the solution by using Roslyn.Services.Workspace.LoadSolution. Once you have done so, you need to go through each of the projects in dependency order, get the Compilation for the project and call Emit on it.
You can get the compilations in dependency order with code like below. (Yes, I know that having to cast to IHaveWorkspaceServices sucks. It'll be better in the next public release, I promise).
using Roslyn.Services;
using Roslyn.Services.Host;
using System;
using System.Collections.Generic;
using System.IO;
class Program
{
static void Main(string[] args)
{
var solution = Solution.Create(SolutionId.CreateNewId()).AddCSharpProject("Foo", "Foo").Solution;
var workspaceServices = (IHaveWorkspaceServices)solution;
var projectDependencyService = workspaceServices.WorkspaceServices.GetService<IProjectDependencyService>();
var assemblies = new List<Stream>();
foreach (var projectId in projectDependencyService.GetDependencyGraph(solution).GetTopologicallySortedProjects())
{
using (var stream = new MemoryStream())
{
solution.GetProject(projectId).GetCompilation().Emit(stream);
assemblies.Add(stream);
}
}
}
}
Note1: LoadSolution still does use msbuild under the covers to parse the .csproj files and determine the files/references/compiler options.
Note2: As Roslyn is not yet language complete, there will likely be projects that don't compile successfully when you attempt this.
I also wanted to compile a full solution on the fly. Building from Kevin Pilch-Bisson's answer and Josh E's comment, I wrote code to compile itself and write it to files.
Software Used
Visual Studio Community 2015 Update 1
Microsoft.CodeAnalysis v1.1.0.0 (Installed using Package Manager Console with command Install-Package Microsoft.CodeAnalysis).
Code
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.MSBuild;
namespace Roslyn.TryItOut
{
class Program
{
static void Main(string[] args)
{
string solutionUrl = "C:\\Dev\\Roslyn.TryItOut\\Roslyn.TryItOut.sln";
string outputDir = "C:\\Dev\\Roslyn.TryItOut\\output";
if (!Directory.Exists(outputDir))
{
Directory.CreateDirectory(outputDir);
}
bool success = CompileSolution(solutionUrl, outputDir);
if (success)
{
Console.WriteLine("Compilation completed successfully.");
Console.WriteLine("Output directory:");
Console.WriteLine(outputDir);
}
else
{
Console.WriteLine("Compilation failed.");
}
Console.WriteLine("Press the any key to exit.");
Console.ReadKey();
}
private static bool CompileSolution(string solutionUrl, string outputDir)
{
bool success = true;
MSBuildWorkspace workspace = MSBuildWorkspace.Create();
Solution solution = workspace.OpenSolutionAsync(solutionUrl).Result;
ProjectDependencyGraph projectGraph = solution.GetProjectDependencyGraph();
Dictionary<string, Stream> assemblies = new Dictionary<string, Stream>();
foreach (ProjectId projectId in projectGraph.GetTopologicallySortedProjects())
{
Compilation projectCompilation = solution.GetProject(projectId).GetCompilationAsync().Result;
if (null != projectCompilation && !string.IsNullOrEmpty(projectCompilation.AssemblyName))
{
using (var stream = new MemoryStream())
{
EmitResult result = projectCompilation.Emit(stream);
if (result.Success)
{
string fileName = string.Format("{0}.dll", projectCompilation.AssemblyName);
using (FileStream file = File.Create(outputDir + '\\' + fileName))
{
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(file);
}
}
else
{
success = false;
}
}
}
else
{
success = false;
}
}
return success;
}
}
}

Categories