I am building a REPL interface with Blazor webassembly.
I have build a textarea where you can write code. The code compiles successfully. The problem is that the textarea is empty. Normally, as a C# developer, you would see an entry point (the Main method) by which the program starts.
How do I fix that?
Try dotnet is an good example of what I want to achieve. In my case the textarea is empty, but I can program in there. Not sure what the entry point is in my case.
I am really stuck here since I cannot find much about Roslyn scripting with Blazor.
Please help me out.
My code to compile input:
private ScriptState _scriptState = null;
private StringWriter _sw;
private string _output;
public ChallengeCompiler()
{
_sw = new StringWriter();
Console.SetOut(_sw);
Console.SetError(_sw);
}
public string CompileCode(string input)
{
string[] references =
{
"System",
"System.Collections",
"System.Collections.Generic",
"System.Collections.Concurrent",
"System.Console",
"System.Diagnostics.Debug",
"System.Diagnostics.Process",
"System.Diagnostics.StackTrace",
"System.Globalization",
"System.IO",
"System.Reflection",
"System.Runtime",
"System.Runtime.InteropServices",
"System.Text",
"System.Text.Encoding",
"System.Text.RegularExpressions",
"System.Threading",
"System.Threading.Tasks",
"System.Threading.Tasks.Parallel",
"System.Threading.Thread",
"System.ValueTuple",
};
try
{
if (_scriptState == null)
{
_scriptState = CSharpScript.RunAsync(input, ScriptOptions.Default.WithImports(references)).Result;
}
else
{
_scriptState = _scriptState.ContinueWithAsync(input).Result;
}
if (_scriptState.ReturnValue != null && !string.IsNullOrEmpty(_scriptState.ReturnValue.ToString()))
{
_output = _scriptState.ReturnValue.ToString();
}
else
{
_output = _sw.ToString();
}
}
catch (CompilationErrorException e)
{
_output = "-----------------------------------\n";
_output += string.Join(Environment.NewLine, e.Diagnostics);
}
return _output;
}
Related
So I've got a basic help command which takes the summery and remarks and it works great now. But it looks chunky when every command is together and every other line the text is darker, is there a way to fix both these issues. Here's the code:
public class HelpHandler : ModuleBase<SocketCommandContext>
{
private readonly CommandService _service;
public HelpHandler(CommandService service)
{
Global.ConsoleLog("Started HelpHandler", ConsoleColor.Blue);
_service = service;
}
[Command("help")]
public async Task HelpAsync()
{
var builder = new EmbedBuilder()
{
Color = new Color(114, 137, 218),
Description = "These are the commands you can use"
};
foreach (var module in _service.Modules)
{
string description = null;
foreach (var cmd in module.Commands)
{
var result = await cmd.CheckPreconditionsAsync(Context);
if (result.IsSuccess)
description += $"{Global.Preflix}{cmd.Aliases.First()}\n";
}
if (!string.IsNullOrWhiteSpace(description))
{
builder.AddField(x =>
{
x.Name = module.Name;
x.Value = description;
x.IsInline = false;
});
}
}
await ReplyAsync("", false, builder.Build());
}
}
So to be more specific, is there a way to make it so that I can have different cards full of x amount of commands each and they you can scroll threw them with emoji reactions such as backwards and forwards? And is there a way to remove the horrible way it goes darker every other line of text for no apparent reason?
Thanks!
I am working on an ETL process that is part of a larger system.
This larger system includes a logging system using a REST API.
The ETL section is running under SSIS, developed in Visual Studio 2015 and deployed on SQL Server 2016.
The ETL is covered with integration tests including tests of logs being generated.
The REST API cannot be guaranteed to be running during these integration tests, and even if it is, the asynchronous nature makes testing log generation... awkward.
We could use a script component to handle the logging, but we have 30+ packages requiring logging (each a distinct operation, based on a data-point to be calculated from one database into the next, so that a team can work concurrently without having TFS merge butcher XML definitions as much as possible), so maintenance becomes a headache.
In order to get around this, I have written a custom component that will bundle up all the errors across a package execution (separated into Fatal, Error, Warning, Info, and Detail levels), add a source, and fire JSON off to the REST API. In the event that the REST API is not specified, the system will log locally instead (which for the integration tests, means that we have a synchronous and local log source to check).
The ComponentType is ComponentType.DestinationAdapter.
The component has two custom properties, one for the variable name of the logging url (defaults to $Project::LoggingURL), one for the source of the log (defaults to System::PackageName).
The component validates the custom properties to not be blank.
The component has a single connection, defaulting to a master database, used as a fallback.
The component validates that the connection is set.
The component has multiple (five) inputs and no outputs.
Each input is marked as having side-effects.
Each attached input is validated as having a single input column of type DT_WSTR.
Unattached inputs are fine (a package that cannot log any fatal errors will leave that input unattached).
If any Fatal or Error messages are detected, the component fails the package in the Post-Execute step (in order to detect as many issues as possible in a run, rather than only the first).
The build targets 32-bit, and .NET Framework 4.
On Post-Build, the dll is copied to the DTS/PipelineComponents folder, and the assembly is deregistered then reregistered in the GAC.
When executing a package through Visual Studio (right-click on the package, 'Execute Package'), the component behaves exactly as expected.
When the package is deployed to the local SQL Server 2016 instance on my machine and the integration tests are run, the validation claims that the outputs leading into my component are not used and should be removed, and the component does nothing (as if it was never there). There are no messages about the component whatsoever.
I would very much like to have the component run in SQL Server, otherwise it is completely useless.
This is the code (there is an associated UI, but the Design-Time behaviour is as expected):
[DtsPipelineComponent(
DisplayName = "Custom Logging Component",
ComponentType = ComponentType.DestinationAdapter,
IconResource = "DestinationIcon",
CurrentVersion = 1,
UITypeName = "ETLCustomDataFlowComponents.CustomLoggingComponentUI,ETLCustomDataFlowComponents,Version=1.0.0.0,Culture=neutral,PublicKeyToken=051a7fa35dda5a9f"
)]
public class HermesCustomLoggingComponent : PipelineComponent
{
public const string _SOURCE_PROPERTY = "Source Name";
public const string _LOG_PROPERTY = "Log URL";
public const string _MASTER_CONN_PROPERTY = "Master Connection";
public override void ProvideComponentProperties()
{
base.ProvideComponentProperties();
base.RemoveAllInputsOutputsAndCustomProperties();
var loggingPath = ComponentMetaData.CustomPropertyCollection.New();
loggingPath.Description = "The url to send json log messages to";
loggingPath.Name = _LOG_PROPERTY;
loggingPath.Value = string.Empty;
loggingPath.ExpressionType = DTSCustomPropertyExpressionType.CPET_NOTIFY;
var source = ComponentMetaData.CustomPropertyCollection.New();
source.Description = "The source to which the log is to be attributed";
source.Name = _SOURCE_PROPERTY;
source.Value = string.Empty;
var masterConn = ComponentMetaData.RuntimeConnectionCollection.New();
masterConn.Name = _MASTER_CONN_PROPERTY;
masterConn.Description = "The connection to log.Log as a backup when centralised logging fails";
foreach (var level in new[] { "Fatal", "Error", "Warning", "Info", "Debug" })
{
var input = ComponentMetaData.InputCollection.New();
input.Name = level;
input.HasSideEffects = true;
}
}
public override DTSValidationStatus Validate()
{
bool broken = false;
bool cancel;
foreach (IDTSInput100 input in ComponentMetaData.InputCollection)
{
if (input.IsAttached)
{
if (input.InputColumnCollection.Count != 1)
{
ComponentMetaData.FireError(0, ComponentMetaData.Name, $"{input.Name} should have only a message input", "", 0, out cancel);
broken = true;
}
else
{
if (input.InputColumnCollection[0].DataType != DataType.DT_WSTR)
{
ComponentMetaData.FireError(0, ComponentMetaData.Name, $"Input to {input.Name} is not of type DT_WSTR", "", 0, out cancel);
broken = true;
}
}
}
else
{
input.InputColumnCollection.RemoveAll();
}
}
if (ComponentMetaData.CustomPropertyCollection[_SOURCE_PROPERTY].Value == string.Empty)
{
ComponentMetaData.FireError(0, ComponentMetaData.Name, $"{_SOURCE_PROPERTY} parameter has not been set", "", 0, out cancel);
broken = true;
}
if (ComponentMetaData.CustomPropertyCollection[_LOG_PROPERTY].Value == string.Empty)
{
ComponentMetaData.FireError(0, ComponentMetaData.Name, $"{_LOG_PROPERTY} parameter has not been set", "", 0, out cancel);
broken = true;
}
if (ComponentMetaData.RuntimeConnectionCollection[_MASTER_CONN_PROPERTY].ConnectionManager == null)
{
ComponentMetaData.FireError(0, ComponentMetaData.Name, $"{_MASTER_CONN_PROPERTY} has not been set", "", 0, out cancel);
broken = true;
}
if (broken)
{
return DTSValidationStatus.VS_ISBROKEN;
}
return base.Validate();
}
private readonly List<Dictionary<string, string>> _logMessages = new List<Dictionary<string, string>>();
private readonly Dictionary<int, IDTSInput100> _inputs = new Dictionary<int, IDTSInput100>();
private readonly Dictionary<string, int> _messageCounts = new Dictionary<string, int>();
private string _source = string.Empty;
private string _loggingPath = string.Empty;
private SqlConnection sqlConnection;
public override void AcquireConnections(object transaction)
{
if (ComponentMetaData.RuntimeConnectionCollection[_MASTER_CONN_PROPERTY].ConnectionManager != null)
{
ConnectionManager cm = DtsConvert.GetWrapper(ComponentMetaData.RuntimeConnectionCollection[_MASTER_CONN_PROPERTY].ConnectionManager);
ConnectionManagerAdoNet cmAdoNet = cm.InnerObject as ConnectionManagerAdoNet;
if (cmAdoNet == null) throw new Exception($"Connection Manager {cm.Name} is not ADO.NET");
sqlConnection = cmAdoNet.AcquireConnection(transaction) as SqlConnection;
if ((sqlConnection != null) && (sqlConnection.State != ConnectionState.Open)) sqlConnection.Open();
}
}
public override void ReleaseConnections()
{
if ((sqlConnection != null) && (sqlConnection.State != ConnectionState.Closed)) sqlConnection.Close();
}
public override void PreExecute()
{
var sourceVar = ComponentMetaData.CustomPropertyCollection[_SOURCE_PROPERTY].Value;
if (sourceVar != string.Empty)
{
IDTSVariables100 variables;
VariableDispenser.LockForRead(sourceVar);
VariableDispenser.GetVariables(out variables);
_source = variables[sourceVar].Value.ToString();
}
var loggingVar = ComponentMetaData.CustomPropertyCollection[_LOG_PROPERTY].Value;
if (loggingVar != string.Empty)
{
IDTSVariables100 variables;
VariableDispenser.LockForRead(loggingVar);
VariableDispenser.GetVariables(out variables);
_loggingPath = variables[loggingVar].Value.ToString();
}
foreach (IDTSInput100 input in ComponentMetaData.InputCollection)
{
_inputs[input.ID] = input;
_messageCounts[input.Name] = 0;
}
}
public override void ProcessInput(int inputID, PipelineBuffer buffer)
{
while (buffer.NextRow())
{
string message = buffer[0].ToString();
_messageCounts[_inputs[inputID].Name] += 1;
_logMessages.Add(new Dictionary<string, string>
{
{"Level", _inputs[inputID].Name},
{"InstanceId", Environment.MachineName},
{"Source", _source},
{"Message", message}
});
}
}
public override void PostExecute()
{
if (string.IsNullOrWhiteSpace(_loggingPath))
{
List<string> logMessagesList = new List<string>();
foreach (var logMessage in _logMessages)
{
logMessagesList.Add(
$"('{logMessage["Level"].Substring(0, 1)}', '{logMessage["Source"]}', '{logMessage["Message"]}')");
}
if ((sqlConnection != null) && (sqlConnection.State == ConnectionState.Open))
{
var command = sqlConnection.CreateCommand();
command.CommandText =
$"INSERT INTO log.Log ([Level], [Source], [Message]) VALUES {string.Join(", ", logMessagesList)}";
command.ExecuteNonQuery();
}
}
else
{
List<string> logMessagesList = new List<string>();
foreach (var logMessage in _logMessages)
{
List<string> logJsonList = new List<string>();
foreach (var logElement in logMessage)
{
logJsonList.Add($"\"{logElement.Key}\":\"{logElement.Value}\"");
}
var logString = string.Join(", ", logJsonList);
if (!logMessagesList.Contains(logString))
{
logMessagesList.Add(logString);
}
}
string logJson = "[{" + string.Join("}, {", logMessagesList) + "}]";
var request = (HttpWebRequest)WebRequest.Create(_loggingPath + "api/log");
request.Method = "POST";
request.ContentType = "application/json";
request.ContentLength = logJson.Length;
using (var requestWriter = new StreamWriter(request.GetRequestStream(), System.Text.Encoding.ASCII))
{
requestWriter.Write(logJson);
}
}
foreach (var level in new[] { "Fatal", "Error" })
{
if (_messageCounts[level] > 0)
{
bool cancel;
ComponentMetaData.FireError(0, _source, "Package has logged an exception, and cannot continue", "",
0,
out cancel);
}
}
}
public override void PerformUpgrade(int pipelineVersion)
{
ComponentMetaData.Version = 1;
}
}
I'm attempting to write a simple keylogger that will check typed words against a blacklist and fire a screenshot when a word is triggered. This is because we have a new PREVENT agenda that we have to use in UK schools to capture any possible extremist views.
I've been looking at the Keylogger API from https://github.com/fabriciorissetto/KeystrokeAPI
I'm using the following code as a test but i'm trying to add the characters to a string so i can then fire a comparison with a word list when the user presses the spacebar. The trouble i'm having is that i cannot convert character into a string. Is it possible do this so i can append it to another string a whilst waiting for a spacebar key?
class Program
{
static void Main(string[] args)
{
using (var api = new KeystrokeAPI())
{
api.CreateKeyboardHook((character) => { Console.Write(character); });
Application.Run();
}
}
}
This is what i have so far, the error i get is on the if statement converting character to a string.
static void Main(string[] args)
{
string line = "";
using (var api = new KeystrokeAPI())
{
api.CreateKeyboardHook((character) => {
line += character.ToString();
if (character.ToString() = "space")
{
Console.Write("Spacebar Hit");
}
Console.Write(character.KeyCode);
});
Application.Run();
}
}
Edit.
I rewrote this.
Captures both spaces and enter commands
static void Main(string[] args)
{
string line = string.Empty;
using (var api = new KeystrokeAPI())
{
api.CreateKeyboardHook((character) => {
if (character.KeyCode.ToString() == "Space" || character.KeyCode.ToString() == "Return")
{
if(BannedWordsCheck(line))
{
Console.WriteLine("Banned Word Typed: " + line);
}
line = string.Empty;
}
else
{
line += character.KeyCode.ToString();
}
});
Application.Run();
}
}
static bool BannedWordsCheck(string word)
{
if(word.ToLower().Contains("terror"))
{
return true;
}
else
{
return false;
}
}
The error you are receiving in your code is due to the following line
if (character.ToString() = "space")
You are attempting to assign the string literal "space" to character.ToString(), I also have this error in my comment which I can't edit anymore.
Here's a snippet that will check the key code against an enum instead of a string, it will then call the HandleComparison method if Space was pressed, and then clear out the StringBuilder
The only issue I found here is that pressing Shift will prefix the string with <shift>, so some additional logic will have to be applied for action keys, but this is a base to get you started with a working code sample.
I hope this helps.
class Program
{
private static StringBuilder builder;
static void Main(string[] args)
{
using (var api = new KeystrokeAPI())
{
builder = new StringBuilder();
api.CreateKeyboardHook(HandleKeyPress);
Application.Run();
}
}
private static void HandleKeyPress(KeyPressed obj)
{
// To be more reliable, lets use the KeyCode enum instead
if (obj.KeyCode == KeyCode.Space)
{
// Spacebar was pressed, let's check the word and flush the StringBuilder
HandleComparison(builder.ToString());
builder.Clear();
return;
}
// Space wasn't pressed, let's add the word to the StringBuilder
builder.Append(obj);
}
// Handle comparison logic here, I.E check word if exists on blacklist
private static void HandleComparison(string compareString)
{
Console.WriteLine(compareString);
}
}
Could you use the StringBuilder as a buffer?
something like this
var buffer = new StringBuilder();
using (var api = new KeystrokeAPI())
{
api.CreateKeyboardHook((character) => {
if (character.ToString() == " ")
{
//check the word
CallSomeMethodToCheckWord(buffer.ToString());
//reset the buffer
buffer = new StringBuilder();
}
else
{
//ToString returns special characters in it, so you could append here and parse later, or parse here.
buffer.Append(character.ToString());
}
});
Application.Run();
}
I've been following MSDN's Hello World guide to developing Visual Studio extensions (this article specifically deals with creating one as a Visual Studio toolbar command).
I am trying to list all projects contained in the current/active solution.
In the auto generated code for the Command template.
I have tried EnvDTE's Solution's Projects property, but it shows zero projects.
There is a ActiveSolutionProjects property as well, but it also shows an empty array.
How is this achieved ?
P.S.: I tried both DTE and DTE2 interfaces since it is confusing understanding which version to use, from the docs. I get a null service for DTE2, so I am going with DTE.
My Solution Explorer looks like:
Update: Bert Huijben, from gitter/extendvs, suggested the following, found at the VSSDK Extensibility Samples - but this too does not work (returns 0 elements, both within the constructor and within the callback function):
private Hashtable GetLoadedControllableProjectsEnum()
{
Hashtable mapHierarchies = new Hashtable();
IVsSolution sol = (IVsSolution)this.ServiceProvider.GetService(typeof(SVsSolution));
Guid rguidEnumOnlyThisType = new Guid();
IEnumHierarchies ppenum = null;
ErrorHandler.ThrowOnFailure(sol.GetProjectEnum((uint)__VSENUMPROJFLAGS.EPF_LOADEDINSOLUTION, ref rguidEnumOnlyThisType, out ppenum));
IVsHierarchy[] rgelt = new IVsHierarchy[1];
uint pceltFetched = 0;
while (ppenum.Next(1, rgelt, out pceltFetched) == VSConstants.S_OK &&
pceltFetched == 1)
{
IVsSccProject2 sccProject2 = rgelt[0] as IVsSccProject2;
if (sccProject2 != null)
{
mapHierarchies[rgelt[0]] = true;
}
}
return mapHierarchies;
}
To get the EnvDTE.Project objects:
static private void FindProjectsIn(EnvDTE.ProjectItem item, List<EnvDTE.Project> results)
{
if (item.Object is EnvDTE.Project)
{
var proj = (EnvDTE.Project)item.Object;
if (new Guid(proj.Kind) != Utilities.ProjectTypeGuids.Folder)
{
results.Add((EnvDTE.Project)item.Object);
}
else
{
foreach (EnvDTE.ProjectItem innerItem in proj.ProjectItems)
{
FindProjectsIn(innerItem, results);
}
}
}
if (item.ProjectItems != null)
{
foreach (EnvDTE.ProjectItem innerItem in item.ProjectItems)
{
FindProjectsIn(innerItem, results);
}
}
}
static private void FindProjectsIn(EnvDTE.UIHierarchyItem item, List<EnvDTE.Project> results)
{
if (item.Object is EnvDTE.Project)
{
var proj = (EnvDTE.Project)item.Object;
if (new Guid(proj.Kind) != Utilities.ProjectTypeGuids.Folder)
{
results.Add((EnvDTE.Project)item.Object);
}
else
{
foreach (EnvDTE.ProjectItem innerItem in proj.ProjectItems)
{
FindProjectsIn(innerItem, results);
}
}
}
foreach (EnvDTE.UIHierarchyItem innerItem in item.UIHierarchyItems)
{
FindProjectsIn(innerItem, results);
}
}
static internal IEnumerable<EnvDTE.Project> GetEnvDTEProjectsInSolution()
{
List<EnvDTE.Project> ret = new List<EnvDTE.Project>();
EnvDTE80.DTE2 dte = (EnvDTE80.DTE2)ServiceProvider.GlobalProvider.GetService(typeof(EnvDTE.DTE));
EnvDTE.UIHierarchy hierarchy = dte.ToolWindows.SolutionExplorer;
foreach (EnvDTE.UIHierarchyItem innerItem in hierarchy.UIHierarchyItems)
{
FindProjectsIn(innerItem, ret);
}
return ret;
}
Notably recursion is necessary to dig into solution folders.
If you just want the file paths you can do this w/o using DTE:
static internal string[] GetProjectFilesInSolution()
{
IVsSolution sol = ServiceProvider.GlobalProvider.GetService(typeof(SVsSolution)) as IVsSolution;
uint numProjects;
ErrorHandler.ThrowOnFailure(sol.GetProjectFilesInSolution((uint)__VSGETPROJFILESFLAGS.GPFF_SKIPUNLOADEDPROJECTS, 0, null, out numProjects));
string[] projects = new string[numProjects];
ErrorHandler.ThrowOnFailure(sol.GetProjectFilesInSolution((uint)__VSGETPROJFILESFLAGS.GPFF_SKIPUNLOADEDPROJECTS, numProjects, projects, out numProjects));
//GetProjectFilesInSolution also returns solution folders, so we want to do some filtering
//things that don't exist on disk certainly can't be project files
return projects.Where(p => !string.IsNullOrEmpty(p) && System.IO.File.Exists(p)).ToArray();
}
Unfortunately could not find any working solution here, decided to post my own solution:
/// <summary>
/// Queries for all projects in solution, recursively (without recursion)
/// </summary>
/// <param name="sln">Solution</param>
/// <returns>List of projects</returns>
static List<Project> GetProjects(Solution sln)
{
List<Project> list = new List<Project>();
list.AddRange(sln.Projects.Cast<Project>());
for (int i = 0; i < list.Count; i++)
// OfType will ignore null's.
list.AddRange(list[i].ProjectItems.Cast<ProjectItem>().Select(x => x.SubProject).OfType<Project>());
return list;
}
And if you don't know what references / namespaces to add, you can pick up project with source code from here:
https://github.com/tapika/cppscriptcore/blob/2a73f45474c8b2179774fd4715b8d8e80080f3ae/Tools/vsStart/Program.cs#L478
And check namespaces / references.
Works for me:
Add a field in your package for dte.
Get the DTE service.
Reference the Solution.
using EnvDTE;
using EnvDTE80;
In your constructor:
dte = this.ServiceProvider.GetService(typeof(EnvDTE.DTE)) as EnvDTE80.DTE2;
In your command handler:
Integer count = ((EnvDTE.SolutionClass)dte.Solution).Projects.Count;
I get the correct count from this.
Screenshot (requested)
Code
//------------------------------------------------------------------------------
// <copyright file="Command1.cs" company="Company">
// Copyright (c) Company. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
using System;
using System.ComponentModel.Design;
using System.Globalization;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using EnvDTE;
using EnvDTE80;
namespace SolExpExt
{
internal sealed class Command1
{
public const int CommandId = 0x0100;
public static readonly Guid CommandSet = new Guid("beff5a1a-dff5-4f6a-95c8-fd7ea7411a7b");
private DTE2 dte;
private readonly Package package;
private IVsSolution sol;
private Command1(Package package)
{
if (package == null)
{
throw new ArgumentNullException("package");
}
this.package = package;
OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
if (commandService != null)
{
var menuCommandID = new CommandID(CommandSet, CommandId);
var menuItem = new MenuCommand(this.MenuItemCallback, menuCommandID);
commandService.AddCommand(menuItem);
}
dte = this.ServiceProvider.GetService(typeof(EnvDTE.DTE)) as EnvDTE80.DTE2;
}
public static Command1 Instance
{
get;
private set;
}
private IServiceProvider ServiceProvider
{
get
{
return this.package;
}
}
public static void Initialize(Package package)
{
Instance = new Command1(package);
}
private void MenuItemCallback(object sender, EventArgs e)
{
string message = $"There are {dte.Solution.Projects.Count} projects in this solution.";
string title = "Command1";
VsShellUtilities.ShowMessageBox(
this.ServiceProvider,
message,
title,
OLEMSGICON.OLEMSGICON_INFO,
OLEMSGBUTTON.OLEMSGBUTTON_OK,
OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
}
}
}
The following, shamelessly taken from AutoFindReplace, works using VS2015 Community:
using EnvDTE;
.
.
protected override void Initialize()
{
base.Initialize();
IServiceContainer serviceContainer = this as IServiceContainer;
dte = serviceContainer.GetService(typeof(SDTE)) as DTE;
var solutionEvents = dte.Events.SolutionEvents;
solutionEvents.Opened += OnSolutionOpened;
var i = dte.Solution.Projects.Count; // Happy days !
}
All the code lines above pre-exist in the solution within VSPackage.cs except for "var i = dte.Solution.Projects.Count;" which I added locally to VSPackage.cs just after line 44. I then open the solution, hit F5, and within the Experimental Instance I opened JoePublic.Sln and hey presto the count was '2' correctly - Bingo ! Happy days !
Does anyone know how to get a CodeDomProvider in the new Microsoft.VisualStudio.TextTemplating.VSHost.BaseCodeGeneratorWithSite from the Visual Studio 2010 SDK? I used to get access to it just by in mere inheritance of the class Microsoft.CustomTool.BaseCodeGeneratorWithSite, but now with this new class it is not there. I see a GlobalServiceProvider and a SiteServiceProvider but I can't find any example on how to use them.
Microsoft.VisualStudio.TextTemplating.VSHost.BaseCodeGeneratorWithSite:
http://msdn.microsoft.com/en-us/library/bb932625.aspx
I was to do this:
public class Generator : Microsoft.VisualStudio.TextTemplating.VSHost.BaseCodeGeneratorWithSite {
public override string GetDefaultExtension() {
// GetDefaultExtension IS ALSO NOT ACCESSIBLE...
return this.InputFilePath.Substring(this.InputFilePath.LastIndexOf(".")) + ".designer" + base.GetDefaultExtension();
}
// This method is being called every time the attached xml is saved.
protected override byte[] GenerateCode(string inputFileName, string inputFileContent) {
try {
// Try to generate the wrapper file.
return GenerateSourceCode(inputFileName);
} catch (Exception ex) {
// In case of a faliure - print the exception
// as a comment in the source code.
return GenerateExceptionCode(ex);
}
}
public byte[] GenerateSourceCode(string inputFileName) {
Dictionary<string, CodeCompileUnit> oCodeUnits;
// THIS IS WHERE CodeProvider IS NOT ACCESSIBLE
CodeDomProvider oCodeDomProvider = this.CodeProvider;
string[] aCode = new MyCustomAPI.GenerateCode(inputFileName, ref oCodeDomProvider);
return Encoding.ASCII.GetBytes(String.Join(#"
", aCode));
}
private byte[] GenerateExceptionCode(Exception ex) {
CodeCompileUnit oCode = new CodeCompileUnit();
CodeNamespace oNamespace = new CodeNamespace("System");
oNamespace.Comments.Add(new CodeCommentStatement(MyCustomAPI.Print(ex)));
oCode.Namespaces.Add(oNamespace);
string sCode = null;
using (StringWriter oSW = new StringWriter()) {
using (IndentedTextWriter oITW = new IndentedTextWriter(oSW)) {
this.CodeProvider.GenerateCodeFromCompileUnit(oCode, oITW, null);
sCode = oSW.ToString();
}
}
return Encoding.ASCII.GetBytes(sCode );
}
}
Thanks for your help!
You can access the CodeDomProvider via the SiteServiceProvider by asking for the SVSMDCodeDomProvider service.
Something along the lines of:
IVSMDCodeDomProvider provider = SiteServiceProvider.
GetService(typeof(SVSMDCodeDomProvider)) as IVSMDCodeDomProvider;
if (provider != null)
{
codeDomProvider = provider.CodeDomProvider as CodeDomProvider;
}
The SiteServiceProvider is the limited scope service provider exposed by the site of a SingleFileGenerator, whereas the GlobalServiceProvider is VS' main service provider that you can ask for any globally-scoped interface.
Hope this helps.
Gareth