CodeFluent vs Interop.MSScriptControl.dll - c#

We had a 32 bits service that we are trying to migrate to 64 bits.
We were using Interop.MSScriptControl.dll to evaluate vb script written by users.
Since there is no 64 bits version of the MSScriptControl. I created a process that was called inside the service. Each time that we need to evaluate users scripts, we call the process. After trying this solution, I found it really slow.
I just discovered the CodeFluentRuntimeClient library that can evaluate vb script as well as JavaScript. However, the way it evaluates the script is complety different from MSScriptControl library.
I created a simple test program to evaluate the old vb script wrote by users.
public class VBScriptEvaluator
{
public static dynamic Evaluate(string key, string script, IDictionary<string, object> parameterValuePair)
{
try
{
using (ScriptEngine engine = new ScriptEngine(ScriptEngine.VBScriptLanguage))
{
ParsedScript parsed = engine.Parse(string.Format(#"Function {0}()
{1}
End Function", key, script));
if (script.Contains("NecUserProfile"))
engine.SetNamedItem("NecUserProfile", #"" + "ADMIN" + #""); //Hardcoded For now
if (parameterValuePair != null && parameterValuePair.Count > 0)
{
foreach (var para in parameterValuePair)
engine.SetNamedItem(para.Key, para.Value);
}
dynamic value = parsed.CallMethod(key);
return (value != null) ? value.ToString() : string.Empty;
}
}
catch (Exception ex)
{
throw;
}
}
}
If I use like this, it's working fine:
static void Main(string[] args)
{
string key = "necGlobalValue";
string script = #"necGlobalValue = ""ADMIN""";
var result = VBScriptEvaluator.Evaluate(key, script, null); //ADMIN
}
Like this it works well too:
static void Main(string[] args)
{
Dictionary<string, object> parameterValuePair = new Dictionary<string, object>();
parameterValuePair.Add("ZINVOICE_MARGIN_0", 141615427.8);
parameterValuePair.Add("ZINVOICE_AMTNOTLIN_0", 187260276.84);
var script = #"If (ZINVOICE_AMTNOTLIN_0) <> 0 Then
SERVER_FLD0000001 = Abs(ZINVOICE_MARGIN_0) / ZINVOICE_AMTNOTLIN_0
else
SERVER_FLD0000001 = 0
End If";
var key = "SERVER_FLD0000001";
var result = VBScriptEvaluator.Evaluate(key, script, parameterValuePair);
}
In the previous library it was detecting automatically the type of the variables that will be evaluated. I can pass integers as string and it will work just fine.
If I replace the value of the dictionary like by using the ScripEngine, it will fail:
Dictionary<string, object> parameterValuePair = new Dictionary<string, object>();
parameterValuePair.Add("ZINVOICE_MARGIN_0", "141615427.8");
parameterValuePair.Add("ZINVOICE_AMTNOTLIN_0", "187260276.84");
Also, If I do this I'm not getting the user ADMIN.
string key = "necGlobalValue";
string script = #"necGlobalValue = ""NecUserProfile""";
var result = VBScriptEvaluator.Evaluate(key, script, null); // output NecUserProfile instead of ADMIN
And BTW I tried to give as much details, that's why the question is that long.

I made it work by passing the parameters to the function instead of using the SetNamedItem function.
public class VBScriptEvaluator
{
public static dynamic Evaluate(string key, string script, IDictionary<string, object> parameterValuePair = null)
{
try
{
using (ScriptEngine engine = new ScriptEngine(ScriptEngine.VBScriptLanguage))
{
List<object> parameters = new List<object>() { "ADMIN" };
string extraParameters = string.Empty;
if (parameterValuePair != null && parameterValuePair.Count > 0)
{
extraParameters = "," + string.Join(",", parameterValuePair.Select(e => e.Key));
foreach (var para in parameterValuePair)
parameters.Add(para.Value);
}
string parsedScript = string.Format(#"Function {0}(NecUserProfile {2})
{1}
End Function", key, script, extraParameters);
ParsedScript parsed = engine.Parse(parsedScript);
dynamic value = parsed.CallMethod(key, parameters.ToArray());
return (value != null) ? value.ToString() : string.Empty;
}
}
catch (Exception ex)
{
throw;
}
}
}
And here's how to use it:
Dictionary<string, object> parameterValuePair = new Dictionary<string, object>()
{
{"Param1", 100.0 },
{"Param2", 10.0}
};
var script = #"If (Param2) <> 0 Then
result = Param1 + Param2
else
result = 1 + 2
End If";
var key = "result";
var result = VBScriptEvaluator.Evaluate(key, script, parameterValuePair); // output 110

Related

Twincat Ads Reactive weird Handle behaviour

I'm working with TwincatAds.Reactive 6.0.190 in .NET 6 WPF Desktop application.
I'm also using MVVM pattern.
My goal is to create a Class that is going to observe for a PLC Variable changes, collect those variables to a dictionary, and later on use those values in the ViewModel.
Here's the method where I'm attaching the notification and action where I'm handling the notification.
public void AttachNotification(IEnumerable<(string key, Type type)> Symbols)
{
_observerValueNotification = Observer.Create<ValueNotification>(val =>
{
// Does handle really start from 2?
var handle = val.Handle;
if (val.UserData is object[] objects)
{
string tag = objects[handle - 2].ToString();
if (!_values.Any(x => x.Key == tag))
_values.Add(new SymbolModel { Key = tag, Value = val.Value });
else
{
var symbol = _values.First(x => x.Key == tag);
symbol.Value = val.Value;
}
}
ValuesChanged?.Invoke(_values);
});
if (_plcWrapper.AdsClient != null)
{
// Get Symbols from SymbolLoader
List<AnySymbolSpecifier> list = new();
List<string> userData = new();
foreach (var (key, type) in Symbols)
{
list.Add(new AnySymbolSpecifier(key, new AnyTypeSpecifier(type)));
userData.Add(key);
}
_subscription2 = _plcWrapper.AdsClient.WhenNotificationEx(list, NotificationSettings.ImmediatelyOnChange, userData.ToArray())
.Subscribe(_observerValueNotification);
}
}
I'm using ValueNotification simply because, I'd like to use this pattern also for complex PLC Variables like Structs.
As You can see, in the WhenNotificationEx method I'm using UserData[] to provide some sort of identification of what Variable has changed when handling the change.
My idea was to use Handle property from ValueNotification as an indexer in UserData[] to identify what variable I'm dealing with, but for some reason Handle starts from 2.
My question is, is it expected behaviour, does the Handle value really always start from 2?
I've decided that relying on the Handle being index in the UserData array is quite unpredictable as Handle is being created by the Twincat Ads server.
Solved the issue by creating own extension method to the WhenNotificationEx. Turned out IDisposableHandleBag has exactly what I was looking for, which is SourceResultHandles property, where AnySymbolSpecifier and ResultHandle are both stored!
Here's created extension method
public static Dictionary<string, uint> Handles { get; private set; } = new();
public static IObservable<ValueNotification> WhenNotificationWithHandle(this IAdsConnection connection, IList<AnySymbolSpecifier> symbols, NotificationSettings settings)
{
IAdsConnection connection2 = connection;
IList<AnySymbolSpecifier> symbols2 = symbols;
NotificationSettings settings2 = settings;
if (connection2 == null)
{
throw new ArgumentNullException("connection");
}
if (symbols2 == null)
{
throw new ArgumentNullException("symbols");
}
if (symbols2.Count == 0)
{
throw new ArgumentOutOfRangeException("symbols", "Symbol list is empty!");
}
IDisposableHandleBag<AnySymbolSpecifier> bag = null;
EventLoopScheduler scheduler = new EventLoopScheduler();
IObservable<int> whenSymbolChangeObserver = connection2.WhenSymbolVersionChanges(scheduler);
IDisposable whenSymbolChanges = null;
Action<EventHandler<AdsNotificationExEventArgs>> addHandler = delegate (EventHandler<AdsNotificationExEventArgs> h)
{
connection2.AdsNotificationEx += h;
bag = ((IAdsHandleCacheProvider)connection2).CreateNotificationExHandleBag(symbols2, relaxSubErrors: false, settings2, null);
bag.CreateHandles();
// Collect Handles
Handles.Clear();
foreach (var item in bag.SourceResultHandles)
Handles.Add(item.source.InstancePath, item.result.Handle);
whenSymbolChanges = whenSymbolChangeObserver.Subscribe((Action<int>)delegate
{
bag.CreateHandles();
Handles.Clear();
foreach (var item in bag.SourceResultHandles)
Handles.Add(item.source.InstancePath, item.result.Handle);
}, (Action<Exception>)delegate
{
TcTraceSource traceAds = AdsModule.TraceAds;
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(101, 1);
defaultInterpolatedStringHandler.AppendLiteral("The AdsServer '");
defaultInterpolatedStringHandler.AppendFormatted(connection2.Address);
defaultInterpolatedStringHandler.AppendLiteral("' doesn't support SymbolVersionChanged Notifications! Handle recreation is not active!");
traceAds.TraceInformation(defaultInterpolatedStringHandler.ToStringAndClear());
});
};
Action<EventHandler<AdsNotificationExEventArgs>> removeHandler = delegate (EventHandler<AdsNotificationExEventArgs> h)
{
if (whenSymbolChanges != null)
{
whenSymbolChanges.Dispose();
}
scheduler.Dispose();
if (bag != null)
{
bag.Dispose();
bag = null;
Handles.Clear();
}
connection2.AdsNotificationEx -= h;
};
return from ev in Observable.FromEventPattern<EventHandler<AdsNotificationExEventArgs>, AdsNotificationExEventArgs>(addHandler, removeHandler)
where bag.Contains(ev.EventArgs.Handle)
select new ValueNotification(ev.EventArgs, ev.EventArgs.Value);
}

Program closes instead of getting info from API

Okay so I'm still quite new to coding and mostly only knows the basics. I have never worked with API. I'm trying to make a program that gets the number of subs from PewDiePie and Cocomelon and compare them.
namespace PewdiepieVsCoco
{
class Program
{
static void Main(string[] args)
{
try
{
var len = args?.Length;
if (len == null || len.Value == 0)
{
PrintStart();
return;
}
var pdpSubCount = args[0];
var pdpSub = GetPDPSubcount(pdpSubCount).Result;
PrintPDPResult(pdpSub);
}
catch (AggregateException agg)
{
foreach (var e in agg.Flatten().InnerExceptions)
Console.WriteLine(e.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
private static async Task<dynamic> GetPDPSubcount(string pdpSubCount)
{
var parameters = new Dictionary<String, String>
{
["key"] = ConfigurationManager.AppSettings["APIKey"],
["channelsId"] = "UC-lHJZR3Gqxm24_Vd_AJ5Yw",
["part"] = "statistics",
["forUsername"] = "PewDiePie",
["fields"] = "items/statistics(subscriberCount)"
};
var baseUrl = "https://www.googleapis.com/youtube/v3/channels";
var fullUrl = MakeUrlWithQuery(baseUrl, parameters);
var pdpSub = await new HttpClient().GetStringAsync(fullUrl);
if (pdpSub != null)
{
//Does the thing
return JsonConvert.DeserializeObject(pdpSubCount);
}
return default(dynamic);
}
private static string MakeUrlWithQuery(string baseUrl,
IEnumerable<KeyValuePair<string, string>> parameters)
{
if (string.IsNullOrEmpty(baseUrl))
throw new ArgumentException(nameof(baseUrl));
if (parameters == null || parameters.Count() == 0)
return baseUrl;
return parameters.Aggregate(baseUrl,
(accumulated, kvp) => string.Format($"{accumulated}{kvp.Value}&"));
}
private static void PrintPDPResult(dynamic pdpSub)
{
Console.WriteLine($"PewDiePie currently have: {pdpSub} subscribers");//insert subs
}
private static void PrintStart()
{
Console.WriteLine("The war is on!");
Thread.Sleep(500);
Console.WriteLine("PewDiePie Vs Cocomelon – Nursery Rhymes");
Thread.Sleep(500);
}
}
}
Here is the code, I have followed what an Indian dude did in a YT video so some things I don't know what do but I have an idea on what's going on.
Your program is likely returning before making any API calls because you aren't executing it with any command line arguments. Remove this block of code, so it continues without returning early.
var len = args?.Length;
if (len == null || len.Value == 0)
{
PrintStart();
return;
}
Also, pdpSubCount should not be a function argument, and should not be passed into DeserializeObject. You should be deserializing the response from the api call
return JsonConvert.DeserializeObject(pdpSub)

Handling Crystal Reports without parameters

I currently run a Windows service that exports Crystal Reports to PDF on schedule. Out of the 66 reports that run, 5 or so have no parameters defined.
Using the Crystal Reports DLL, I understand that the ReportDocument.Export() method requires parameters, as I have been experiencing the "missing parameter values" exception when it hits the ReportDocument.Export() method.
I'm currently doing my set parameters in this method:
private void SetParameters(string rawParameters = null)
{
var crystalParameters = new Dictionary<string, object>();
var parameters = String.IsNullOrEmpty(rawParameters) ? null : HttpUtility.ParseQueryString(rawParameters);
if(parameters != null)
{
foreach (string rawKey in parameters.AllKeys)
{
var value = parameters[rawKey];
// Check for array value (e.g. key[0]=value)
var arrayCheck = Regex.Match(rawKey, #"^(.+)\[[0-9]?\]$");
if (arrayCheck.Success)
{
var key = arrayCheck.Groups[1].Value;
// Existing entry for this key, reconstruct object array with this added
if (crystalParameters.ContainsKey(key))
{
var newParameterArray = new object[((object[])crystalParameters[key]).Count() + 1];
int i = 0;
foreach (object item in (object[])crystalParameters[key])
{
newParameterArray[i++] = item;
}
newParameterArray[i++] = (object)value;
crystalParameters[key] = (object)newParameterArray;
}
// New array value
else
crystalParameters[key] = (object)new object[] { value };
}
// Discrete value
else
crystalParameters[rawKey] = (object)parameters[rawKey];
}
foreach (string parameter in crystalParameters.Keys)
{
try
{
this.reportDocument.SetParameterValue(parameter, crystalParameters[parameter]);
}
catch (Exception ex)
{
// Ignore invalid parameter exceptions, otherwise throw again
if (ex.HResult != -2147352565)
{
throw ex;
}
}
}
}
}
I tested a couple of scenarios, including ReportDocument.SetParameterValue("", ""). However, I resolved the issue when and only when I blanked out this if clause; reserving this method to be called only when the report does have parameters.

How to access invocations through extension methods, methods in static classes and methods with ref/out parameters with Roslyn

I'm working on creating an open source project for creating .NET UML Sequence Diagrams that leverages a javascript library called js-sequence-diagrams. I am not sure Roslyn is the right tool for the job, but I thought I would give it a shot so I have put together some proof of concept code which attempts to get all methods and their invocations and then outputs these invocations in a form that can be interpreted by js-sequence-diagrams.
The code generates some output, but it does not capture everything. I cannot seem to capture invocations via extension methods, invocations of static methods in static classes.
I do see invocations of methods with out parameters, but not in any form that extends the BaseMethodDeclarationSyntax
Here is the code (keep in mind this is proof of concept code and so I did not entirely follow best-practices, but I am not requesting a code review here ... also, I am used to using Tasks so I am messing around with await, but am not entirely sure I am using it properly yet)
https://gist.github.com/SoundLogic/11193841
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Emit;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.FindSymbols;
using System.Collections.Immutable;
namespace Diagrams
{
class Program
{
static void Main(string[] args)
{
string solutionName = "Diagrams";
string solutionExtension = ".sln";
string solutionFileName = solutionName + solutionExtension;
string rootPath = #"C:\Workspace\";
string solutionPath = rootPath + solutionName + #"\" + solutionFileName;
MSBuildWorkspace workspace = MSBuildWorkspace.Create();
DiagramGenerator diagramGenerator = new DiagramGenerator( solutionPath, workspace );
diagramGenerator.ProcessSolution();
#region reference
//TODO: would ReferencedSymbol.Locations be a better way of accessing MethodDeclarationSyntaxes?
//INamedTypeSymbol programClass = compilation.GetTypeByMetadataName("DotNetDiagrams.Program");
//IMethodSymbol barMethod = programClass.GetMembers("Bar").First(s => s.Kind == SymbolKind.Method) as IMethodSymbol;
//IMethodSymbol fooMethod = programClass.GetMembers("Foo").First(s => s.Kind == SymbolKind.Method) as IMethodSymbol;
//ITypeSymbol fooSymbol = fooMethod.ContainingType;
//ITypeSymbol barSymbol = barMethod.ContainingType;
//Debug.Assert(barMethod != null);
//Debug.Assert(fooMethod != null);
//List<ReferencedSymbol> barReferencedSymbols = SymbolFinder.FindReferencesAsync(barMethod, solution).Result.ToList();
//List<ReferencedSymbol> fooReferencedSymbols = SymbolFinder.FindReferencesAsync(fooMethod, solution).Result.ToList();
//Debug.Assert(barReferencedSymbols.First().Locations.Count() == 1);
//Debug.Assert(fooReferencedSymbols.First().Locations.Count() == 0);
#endregion
Console.ReadKey();
}
}
class DiagramGenerator
{
private Solution _solution;
public DiagramGenerator( string solutionPath, MSBuildWorkspace workspace )
{
_solution = workspace.OpenSolutionAsync(solutionPath).Result;
}
public async void ProcessSolution()
{
foreach (Project project in _solution.Projects)
{
Compilation compilation = await project.GetCompilationAsync();
ProcessCompilation(compilation);
}
}
private async void ProcessCompilation(Compilation compilation)
{
var trees = compilation.SyntaxTrees;
foreach (var tree in trees)
{
var root = await tree.GetRootAsync();
var classes = root.DescendantNodes().OfType<ClassDeclarationSyntax>();
foreach (var #class in classes)
{
ProcessClass( #class, compilation, tree, root );
}
}
}
private void ProcessClass(
ClassDeclarationSyntax #class
, Compilation compilation
, SyntaxTree tree
, SyntaxNode root)
{
var methods = #class.DescendantNodes().OfType<MethodDeclarationSyntax>();
foreach (var method in methods)
{
var model = compilation.GetSemanticModel(tree);
// Get MethodSymbol corresponding to method
var methodSymbol = model.GetDeclaredSymbol(method);
// Get all InvocationExpressionSyntax in the above code.
var allInvocations = root.DescendantNodes().OfType<InvocationExpressionSyntax>();
// Use GetSymbolInfo() to find invocations of target method
var matchingInvocations =
allInvocations.Where(i => model.GetSymbolInfo(i).Symbol.Equals(methodSymbol));
ProcessMethod( matchingInvocations, method, #class);
}
var delegates = #class.DescendantNodes().OfType<DelegateDeclarationSyntax>();
foreach (var #delegate in delegates)
{
var model = compilation.GetSemanticModel(tree);
// Get MethodSymbol corresponding to method
var methodSymbol = model.GetDeclaredSymbol(#delegate);
// Get all InvocationExpressionSyntax in the above code.
var allInvocations = tree.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>();
// Use GetSymbolInfo() to find invocations of target method
var matchingInvocations =
allInvocations.Where(i => model.GetSymbolInfo(i).Symbol.Equals(methodSymbol));
ProcessDelegates(matchingInvocations, #delegate, #class);
}
}
private void ProcessMethod(
IEnumerable<InvocationExpressionSyntax> matchingInvocations
, MethodDeclarationSyntax methodDeclarationSyntax
, ClassDeclarationSyntax classDeclarationSyntax )
{
foreach (var invocation in matchingInvocations)
{
MethodDeclarationSyntax actingMethodDeclarationSyntax = null;
if (SyntaxNodeHelper.TryGetParentSyntax(invocation, out actingMethodDeclarationSyntax))
{
var r = methodDeclarationSyntax;
var m = actingMethodDeclarationSyntax;
PrintCallerInfo(
invocation
, classDeclarationSyntax
, m.Identifier.ToFullString()
, r.ReturnType.ToFullString()
, r.Identifier.ToFullString()
, r.ParameterList.ToFullString()
, r.TypeParameterList != null ? r.TypeParameterList.ToFullString() : String.Empty
);
}
}
}
private void ProcessDelegates(
IEnumerable<InvocationExpressionSyntax> matchingInvocations
, DelegateDeclarationSyntax delegateDeclarationSyntax
, ClassDeclarationSyntax classDeclarationSyntax )
{
foreach (var invocation in matchingInvocations)
{
DelegateDeclarationSyntax actingMethodDeclarationSyntax = null;
if (SyntaxNodeHelper.TryGetParentSyntax(invocation, out actingMethodDeclarationSyntax))
{
var r = delegateDeclarationSyntax;
var m = actingMethodDeclarationSyntax;
PrintCallerInfo(
invocation
, classDeclarationSyntax
, m.Identifier.ToFullString()
, r.ReturnType.ToFullString()
, r.Identifier.ToFullString()
, r.ParameterList.ToFullString()
, r.TypeParameterList != null ? r.TypeParameterList.ToFullString() : String.Empty
);
}
}
}
private void PrintCallerInfo(
InvocationExpressionSyntax invocation
, ClassDeclarationSyntax classBeingCalled
, string callingMethodName
, string returnType
, string calledMethodName
, string calledMethodArguments
, string calledMethodTypeParameters = null )
{
ClassDeclarationSyntax parentClassDeclarationSyntax = null;
if (!SyntaxNodeHelper.TryGetParentSyntax(invocation, out parentClassDeclarationSyntax))
{
throw new Exception();
}
calledMethodTypeParameters = calledMethodTypeParameters ?? String.Empty;
var actedUpon = classBeingCalled.Identifier.ValueText;
var actor = parentClassDeclarationSyntax.Identifier.ValueText;
var callInfo = callingMethodName + "=>" + calledMethodName + calledMethodTypeParameters + calledMethodArguments;
var returnCallInfo = returnType;
string info = BuildCallInfo(
actor
, actedUpon
, callInfo
, returnCallInfo);
Console.Write(info);
}
private string BuildCallInfo(string actor, string actedUpon, string callInfo, string returnInfo)
{
const string calls = "->";
const string returns = "-->";
const string descriptionSeparator = ": ";
string callingInfo = actor + calls + actedUpon + descriptionSeparator + callInfo;
string returningInfo = actedUpon + returns + actor + descriptionSeparator + "returns " + returnInfo;
callingInfo = callingInfo.RemoveNewLines(true);
returningInfo = returningInfo.RemoveNewLines(true);
string result = callingInfo + Environment.NewLine;
result += returningInfo + Environment.NewLine;
return result;
}
}
static class SyntaxNodeHelper
{
public static bool TryGetParentSyntax<T>(SyntaxNode syntaxNode, out T result)
where T : SyntaxNode
{
// set defaults
result = null;
if (syntaxNode == null)
{
return false;
}
try
{
syntaxNode = syntaxNode.Parent;
if (syntaxNode == null)
{
return false;
}
if (syntaxNode.GetType() == typeof (T))
{
result = syntaxNode as T;
return true;
}
return TryGetParentSyntax<T>(syntaxNode, out result);
}
catch
{
return false;
}
}
}
public static class StringEx
{
public static string RemoveNewLines(this string stringWithNewLines, bool cleanWhitespace = false)
{
string stringWithoutNewLines = null;
List<char> splitElementList = Environment.NewLine.ToCharArray().ToList();
if (cleanWhitespace)
{
splitElementList.AddRange(" ".ToCharArray().ToList());
}
char[] splitElements = splitElementList.ToArray();
var stringElements = stringWithNewLines.Split(splitElements, StringSplitOptions.RemoveEmptyEntries);
if (stringElements.Any())
{
stringWithoutNewLines = stringElements.Aggregate(stringWithoutNewLines, (current, element) => current + (current == null ? element : " " + element));
}
return stringWithoutNewLines ?? stringWithNewLines;
}
}
}
Any guidance here would be much appreciated!
Using the methodSymbol in the ProcessClass method I took Andy's suggestion and came up with the below (although I imagine there may be an easier way to go about this):
private async Task<List<MethodDeclarationSyntax>> GetMethodSymbolReferences( IMethodSymbol methodSymbol )
{
var references = new List<MethodDeclarationSyntax>();
var referencingSymbols = await SymbolFinder.FindCallersAsync(methodSymbol, _solution);
var referencingSymbolsList = referencingSymbols as IList<SymbolCallerInfo> ?? referencingSymbols.ToList();
if (!referencingSymbolsList.Any(s => s.Locations.Any()))
{
return references;
}
foreach (var referenceSymbol in referencingSymbolsList)
{
foreach (var location in referenceSymbol.Locations)
{
var position = location.SourceSpan.Start;
var root = await location.SourceTree.GetRootAsync();
var nodes = root.FindToken(position).Parent.AncestorsAndSelf().OfType<MethodDeclarationSyntax>();
references.AddRange(nodes);
}
}
return references;
}
and the resulting image generated by plugging the output text into js-sequence-diagrams (I have updated the github gist with the full source for this should anyone find it useful - I excluded method parameters so the diagram was easy digest, but these can optionally be turned back on):
Edit:
I've updated the code (see the github gist) so now calls are shown in the order they were made (based on the span start location of a called method from within the calling method via results from FindCallersAsync):

Get url from firefox 8 not working anymore

I have a windows applicaiton c# catching the url of a running firefox instance.
I have always used "MozillaContentWindow" to get firefox URL but i dont understand why it dont work anymore.
string s = GetUrlFromBrowsersWithIdentifier("MozillaContentWindow", foreGround);
public string GetUrlFromBrowsersWithIdentifier(string identifier, int foreground)
{
try
{
IntPtr ptr = new IntPtr(foreground);
var aeBrowser = AutomationElement.FromHandle(ptr);
return aeBrowser == null ? "" : GetURLfromBrowser(aeBrowser, identifier);
}
catch (Exception ex)
{
return "";
}
}
string GetURLfromBrowser(AutomationElement rootElement, string identifier)
{
try
{
Condition condition1 = new PropertyCondition(AutomationElement.IsContentElementProperty, true);
Condition condition2 = new PropertyCondition(AutomationElement.ClassNameProperty, identifier);
var walker = new TreeWalker(new AndCondition(condition1, condition2));
var elementNode = walker.GetFirstChild(rootElement);
if (elementNode != null)
{
var p = elementNode.GetSupportedPatterns();
if (p.Any(autop => autop.ProgrammaticName.Equals("ValuePatternIdentifiers.Pattern")))
{
var valuePattern = elementNode.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
if (valuePattern != null)
return (valuePattern.Current.Value);
}
}
}
catch
{
return "";
}
return "";
}
Now when it enters "walker.GetFirstChild(rootElement);" it just stops there. I cant figure out why. This only happend on latest version of firefox.
Did they change the name of the value bar containing the url?
Thank you
Try using MozillaWindowContentClass for newer versions.

Categories

Resources