I’m writing a Visual Studio Extension, and I want to get the Type of a variable by its name and location.
For instance:
There’s a C# code:
class Program
{
static void Main(string[] args)
{
var flagMick = true;
Console.WriteLine(""Hello, World!"");
}
}
I am able to communicate with the ExpressionEvaluator and the DTE, I am able to read data off the snapshot and understand its location. I have the specific location but:
I don't want to "relearn" c# parsing, that's what Roslyn is supposed to be for.
So I want my extension to recognize the type of “FlagMick” , given its name and location on the screen.
How can I do that with Roslyn?
Use the following code:
var doc = point.Snapshot.GetOpenDocumentInCurrentContextWithChanges();
var model = doc.GetSemanticModelAsync().Result;
var symbol = SymbolFinder.FindSymbolAtPosition(model, point, doc.Project.Solution.Workspace);
You will need a reference to Microsoft.CodeAnalysis.EditorFeatures.Text.dll, which you can get by adding the appropriate NuGet package.
Related
I am trying to directly invoke IronPython's built-in modules from C#. It looks like I'm missing some important initialization, that I can't find anywhere in the code.
Here's what I do:
namespace py.consoleio
{
using IronPython.Runtime;
using Microsoft.Scripting.Hosting;
using Microsoft.Scripting.Hosting.Providers;
using Microsoft.Scripting.Runtime;
public static class consoleio
{
public static string name;
static void Main()
{
var setup = new ScriptRuntimeSetup();
setup.LanguageSetups.Add(
IronPython.Hosting.Python.CreateLanguageSetup(null));
var dlrRuntime = new ScriptRuntime(setup);
var scriptDomainManager = HostingHelpers.GetDomainManager(dlrRuntime);
var pythonContext = new PythonContext(scriptDomainManager, null);
var context = new CodeContext(new PythonDictionary(), new ModuleContext(new PythonDictionary(), DefaultContext.DefaultPythonContext));
name = IronPython.Modules.Builtin.input(context, "What is your name?\n");
IronPython.Modules.Builtin.print(context, "Hi, %s.", consoleio.name);
System.GC.KeepAlive(pythonContext);
}
}
}
That properly outputs "What is your name?", but then crashes trying to decode input: unknown encoding: cp437.
Now I've already found, that encodings are initialized in Src/StdLib/Lib/encodings/init.py
I can't find how it gets to loading this module in a normal IronPython run (e.g. a console host), so I can't reproduce it in C# program.
My goal here is to invoke IronPython functions without dynamic dispatch.
UPD. Now I also tried to do this:
var engine = Python.CreateEngine();
this.ScriptDomainManager = HostingHelpers.GetDomainManager(engine.Runtime);
to the same result
Figured that one out: encodings module is implemented in Python in IronPython (core modules are in C#). It always worked with IronPythonConsole project, because it implicitly adds IronPython source for standard libraries to Python path. I just had to explicitly specify path like this:
var options = new Dictionary<string, object> { ["SearchPaths"] = path };
var engine = Python.CreateEngine(options);
I have the following c# code within a 2012 SSIS package Script Task using 4.5 Framework:
[Microsoft.sqlServer.Dts.Tasks.ScripTask.SSISScriptTaskEntryPointAttribute]
Public partial class ScriptMain
{
static ScriptMain()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomai_AssemblyResolveForDLL);
}
Static System.Reflection.Assembly CurrentDomain_AssemblyResolveForDLL(object sender, ResolveEventArgs args)
{
If (args.Name.Contains("NameofMydll"))
{
string path = #"c:\Temp\";
return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(path, "NameofMydll.dll"));
}
}
return null;
}
The purpose of this code is to load a .dll file used in a SSIS Script task during run time. The way the code works is just like what is described here. With the path hard coded, it works fine.
The problem I have is I need to dynamically set the value of the path so I can promote the code from server to server with no hard coded values. Since the function is static, I didn't know how to do it.
The line of code here:
MyVariable = (string)Dts.Variables.["MyVariable"].value;
...receives the value with MyVariable declared as a global public string variable (this works fine as long as its not within the code above). But using "MyVariable" in place of the #"c:\Temp\" is where I get the Cannot access non static field...error.
Changing the declaration to public static string allows it to compile, but throws an error at run time.
Exception has been thrown by the target of invocation.
The value is assigned through a function just before the line of code below is called:
AppDomain.CurentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolveForDLL)
The call to the function is the first line of code in Main(), the line of code above is the 2nd.
All I need to do is assign the value of the path from a variable. Any assistance will be greatly appreciated.
Change to MyVariable = (string)Dts.Variables.["MyVariable"].value;
or access via it's positionMyVariable = (string)Dts.Variables.[0].value;
I have these requirements coming from client every week for some new logic or verification. For which I have to code new logic (basically some if-else and loops) and launch a new build for him. I want to avoid it by simply coding my logic in visual studio then writing a utility to export it to XML or something and send it to client via e-mail. He just have to place this file in some appropriate folder and the application will behave considering this logic.
Please suggest some solutions. My platform is C# Asp.Net.
Thanks
Using .NET 4.6 and the NuGetPackage Microsoft.CodeAnalysis.Scripting you could implement a scripting engine to run your c# code residing in a textfile without building an assembly.
Install NuGet Package:
Install-Package Microsoft.CodeAnalysis.Scripting.CSharp
Implement TestClass with some basic C#-Code-Content:
class Program
{
static void Main(string[] args)
{
TestScript();
}
private static async void TestScript()
{
// Code snippet: a class with one string-property.
string codeContent = #" using System;
public class ScriptedClass
{
public string HelloWorld { get; set; }
public ScriptedClass()
{
HelloWorld = ""Hello Roslyn!"";
}
}
new ScriptedClass().HelloWorld";
// Instanciate CSharpScriptEngine
var engine = new CSharpScriptEngine();
// Execute code and return string property (HelloWorld)
var scriptingState = await engine.ExecuteAsync(codeContent);
// Print return value from CSharpScript
Console.WriteLine("Returned from CSharpScript: {0}", scriptingState.ReturnValue);
Console.WriteLine("Press any key to continue.");
Console.ReadKey();
}
}
Implement a ScriptingEngine:
internal sealed class CSharpScriptEngine
{
public async Task<ScriptState<object>> ExecuteAsync(string codeContent)
{
// Add references from calling assembly
ScriptOptions options = ScriptOptions.Default.AddReferences(Assembly.GetExecutingAssembly());
// Run codeContent with given options
return await CSharpScript.RunAsync(codeContent, options);
}
}
Read ScriptCode from textfile:
So basically you could read some csharpcode from a textfile of your choice and run them on the fly:
private static async void TestScript()
{
// Read in script file
string codeContent = File.ReadAllText(#"C:\Temp\CSharpScriptTest.cs");
var engine = new CSharpScriptEngine();
// Run script
var scriptingState = await engine.ExecuteAsync(codeContent);
Console.WriteLine("Returned from CSharpScript: {0}", scriptingState.ReturnValue);
Console.WriteLine("Press any key to continue.");
Console.ReadKey();
}
In case you are wondering how all of this works under the hood, Roslyn will create a so called submission from your script code. A submission is an in memory assembly containing the types generated around your script code, which can be identified among the assemblies in the current AppDomain by a ℛ prefix in the name.
The precise implementation details are not important here (though, for example, scriptcs heavily relies on understanding in detail how Roslyn works to provide its extra features), but it's important to know that submissions can be chained together. When they are chained, variables, methods or classes defined in an earlier submission are available to use in subsequent submissions, creating a feature of a C# REPL (read-evaluate-print loop).
C# and Visual Basic - Use Roslyn to Write a Live Code Analyzer for Your API
Hope it helps
I want to be able to read the VS build configurations programmatically. That is because I want to create my own builder.
How do I do that? Does anyone have code example?
What i mean is that if I have Debug, Development, Release I want them to be listed in a list box in a Form application. I have tried using the "EnvDTE.dll" class but I am not sure it is what I am looking for. If anyone has a concrete example or link to an example I would be more than grateful.
You can use the msbuild API. In Visual Studio 2015, there is a class called Microsoft.Build.Construction.SolutionFile in the Microsoft.Build.dll that ships with VS2015 that you can use to load a solution.
In VS2013 there is no such thing, but you can do the following:
(reference Microsoft.Build.dll, Microsoft.Build.Engine.dll, Microsoft.Build.Framework.dll)
class Program
{
static void Main(string[] args)
{
string nameOfSolutionForThisProject = #"MySolutionFile.sln";
string wrapperContent = SolutionWrapperProject.Generate(nameOfSolutionForThisProject, toolsVersionOverride: null, projectBuildEventContext: null);
byte[] rawWrapperContent = Encoding.Unicode.GetBytes(wrapperContent.ToCharArray());
using (MemoryStream memStream = new MemoryStream(rawWrapperContent))
using (XmlTextReader xmlReader = new XmlTextReader(memStream))
{
Project proj = ProjectCollection.GlobalProjectCollection.LoadProject(xmlReader);
foreach (var p in proj.ConditionedProperties)
{
Console.WriteLine(p.Key);
Console.WriteLine(string.Join(", ", p.Value));
}
}
Console.ReadLine();
}
}
ConditionedProperties contains a list of platforms and configurations contained in the solution. You can use this to populate your forms.
I need to write a windows app that reads an actual C# .cs file and then tries to identify what procedures on that source file do NOT have a parameter named "int userID" (it's kind of a Code Checker).
Is there something out there that already reads C# files into some sort of objects that have a property named "procedureName" and another property named "parameterList" ...that can be queried ??
If not, what would be the best way to accomplish that?
I would recommend using NRefactory which should be able to provide the solution.
The following is an example which will find all the Methods in a file with the Name of bob.
CSharpParser parser = new CSharpParser();
SyntaxTree syntaxTree = parser.Parse(#"namespace Test
{
public class TestClass
{
public void Method(string bob)
{
}
}
}");
var result = syntaxTree.Descendants.OfType<MethodDeclaration>().Where(x => x.Descendants.OfType<ParameterDeclaration>().Any(y => y.NameToken.Name == "bob"));
if (result.Any())
{
Console.WriteLine("We Win");
}