Determine if CodeElement belongs to project - c#

I'm trying to develop a plugin for Visual Studio 2015. I have command that gets added to the context menu when a project is right clicked and I can get the project that was right clicked. Now what I am trying to do is determine if the project contains a class that implements a certain interface. So my first step is to get the classes in the project. So I did something like this:
protected IEnumerable<EnvDTE.CodeClass> GetClasses(EnvDTE.CodeElements elements,
EnvDTE.Project project)
{
foreach (EnvDTE.CodeElement element in elements)
{
System.Diagnostics.Debug.WriteLine(element.InfoLocation);
var cls = element as EnvDTE.CodeClass;
if (cls != null)
{
yield return cls;
}
var ns = element as EnvDTE.CodeNamespace;
if (ns != null)
{
foreach (var childCls in GetClasses(ns.Members, project))
{
yield return childCls;
}
}
}
}
So this will go through and pull out classes. The problem is that it will go through everything that is referenced include BCL classes. I thought using InfoLocation might help, but everything returns vsCMInfoLocationExternal (presumably because in the context where the plugin is running, they are external). I've tried things like element.ProjectItem and element.ProjectItem.ContainingProject and element.Parent with the hope of comparing that to the parent project, but those all throw System.Runtime.InteropServices.COMException. So is there a way, given that I know the project, to determine if a particular CodeElement is part of the project, or just referenced by the project?
EDIT: The best I've been able to come up with so far is to do this, first get the default namespace for the project:
var defaultNS = project.Properties.Item("DefaultNamespace").Value.ToString();
And then I can do this:
if (ns != null && ns.Name == defaultNS)
So now I won't go drilling down into System, which is good. The only problem would be if a project had multiple namespaces that I wanted to search. I can't figure out if there is a way to get a list of namespaces that are defined in the project.
Edit: The suggested dupe deals with Type so isn't entirely relevant.

This may suit your needs or it may not but this is what I used to parse code elements and figure out if the definition is in the solution or if it comes in via a reference. There is no way to know if the reference is 3rd party vs BCL however. Some code removed for brevity since this is inside an API and hard to fully break out. You could add a trick once you have the types full name and know its a reference where you reflect all dlls that are signed with the Microsoft key for the type name, if you find one its bcl, otherwise it probably is not.
public static string CodeElementAsTypeFullName(EnvDTE80.CodeElement2 element)
{
if (element == null)
throw new ArgumentNullException(nameof(element));
if (element.Kind == vsCMElement.vsCMElementClass
|| element.Kind == vsCMElement.vsCMElementEnum
|| element.Kind == vsCMElement.vsCMElementStruct)
return element.FullName;
else
return ((dynamic)element).Type.AsFullName;
}
protected void FlattenElement(EnvDTE80.CodeElement2 element)
{
try
{
string varType = CodeElementAsTypeFullName(element);
switch (element.Kind)
{
case vsCMElement.vsCMElementVariable:
case vsCMElement.vsCMElementProperty:
{
EnvDTE80.CodeElement2 defined = null;
///this is API, basically a collection of all the files in the solution with all class/enum/stuct defs parsed out into collections.
foreach (SquishFile file in this.solutionFiles)
{
//next line goes through each solution file one by one to figure out if the file defines the class/enum/struct definition.
defined = file.ContainsCodeElement(varType);
if (defined != null)
break;
}
if (defined != null)
{
if (defined.Kind == vsCMElement.vsCMElementClass
|| defined.Kind == vsCMElement.vsCMElementStruct
|| defined.Kind == vsCMElement.vsCMElementEnum)
//THE ITEM IS DEFINED LOCALLY!
}else
//the item is a reference
}
}
}
public class SquishFile
{
public ConcurrentBag<CodeClass> ClassDefinitions = new ConcurrentBag<CodeClass>();
public ConcurrentBag<CodeEnum> EnumDefinitions = new ConcurrentBag<CodeEnum>();
public ConcurrentBag<CodeStruct> StructDefinitions = new ConcurrentBag<CodeStruct>();
protected ProjectItem _projectItem;
public ProjectItem ProjectItem { get { return _projectItem; } }
public SquishFile(ProjectItem projectItem)
{
if (projectItem.FileCodeModel == null)
throw new Exception("Cannot make a squish file out of a project item with no FileCodeModel!");
_projectItem = projectItem;
foreach (EnvDTE80.CodeElement2 ele in projectItem.FileCodeModel.CodeElements)
Discovery(ele);
}
public EnvDTE80.CodeElement2 ContainsCodeElement(string fullName)
{
foreach(EnvDTE80.CodeElement2 ele in ClassDefinitions)
if (ele.FullName.Equals(fullName))
return ele;
foreach (EnvDTE80.CodeElement2 ele in EnumDefinitions)
if (ele.FullName.Equals(fullName))
return ele;
foreach (EnvDTE80.CodeElement2 ele in StructDefinitions)
if (ele.FullName.Equals(fullName))
return ele;
return null;
}
protected void Discovery(EnvDTE80.CodeElement2 element)
{
if (element.IsCodeType && element.Kind == vsCMElement.vsCMElementClass)
this.ClassDefinitions.Add(element as EnvDTE80.CodeClass2);
else if (element.IsCodeType && element.Kind == vsCMElement.vsCMElementEnum)
this.EnumDefinitions.Add(element as EnvDTE.CodeEnum);
else if (element.IsCodeType && element.Kind == vsCMElement.vsCMElementStruct)
this.StructDefinitions.Add(element as EnvDTE80.CodeStruct2);
foreach (EnvDTE80.CodeElement2 ele in element.Children)
Discovery(ele);
}
}

Related

What parameter i have to assign inside my variable so i can start it from my Main?

im new to C# and Tia Openness and have an problem. I dont know what parameter goes inside my ImportSingleTextList();.Its an example from Siemens but there is never mentioned how to call it inisde the main. That is my code.
private static void ImportSingleTextList(HmiTarget hmitarget)
{
TextListComposition textListsComposition = hmitarget.TextLists;
IList<TextList> importedTextLists = textListsComposition.Import(new FileInfo(#"D:\SamplesImport\myTextList.xml"), ImportOptions.Override);
}
I guess you have to look into your HmiTarget exactly. Is it a class, then you should instantiate a first instance of it; what constructor does this class have - with or without parameters? Click on HmiTarget and see what input it expects.
I guess you class has some kind of enumerable hmitarget.TextLists that you have to fill or get too.
Presumably you have a Project instance. You have to drill down from Project->Device->DeviceItem(->DeviceItem) until you find a DeviceItem that can provide a SoftwareContainer service. It may be that all such DeviceItems reside at the first level below Device; I haven't checked. Anyway, here's a method I wrote that searches the first and second DeviceItem levels:
public static HmiTarget GetHmiTarget(Device hmiDevice)
{
//search first level of DeviceItems
foreach (DeviceItem di in hmiDevice.DeviceItems)
{
SoftwareContainer container =
di.GetService<SoftwareContainer>();
if (container != null)
{
HmiTarget hmi = container.Software as HmiTarget;
if (hmi != null)
return hmi;
}
//search second level of DeviceItems
foreach (DeviceItem devItem in di.DeviceItems)
{
SoftwareContainer subContainer = devItem.GetService<SoftwareContainer>();
if(subContainer != null)
{
HmiTarget hmi = subContainer.Software as HmiTarget;
if (hmi != null)
return hmi;
}
}
}
return null; //nothing was found at the first or second levels
}
to get the Device, you can use PROJECT.Devices.Find(NAME) where PROJECT is your TIA portal project instance, and NAME is the string name of your HMI device.

Find subsolution items

I am trying to find subsolution and add items into that solution programmatically. But code that i use is not searching for subfolders.
I use code on github: See code here please
Method that i search for solution is:
public static IEnumerable<EnvDTE.Project> GetAllProjects(EnvDTE.DTE dte)
{
List<EnvDTE.Project> projectList = new List<EnvDTE.Project>();
var folders = dte.Solution.Projects.Cast<EnvDTE.Project>().Where(p=>p.Kind == EnvDTE80.ProjectKinds.vsProjectKindSolutionFolder);
foreach (EnvDTE.Project folder in folders)
{
if (folder.ProjectItems == null) continue;
foreach (EnvDTE.ProjectItem item in folder.ProjectItems)
{
if (item.Object is EnvDTE.Project)
projectList.Add(item.Object as EnvDTE.Project);
}
}
var projects = dte.Solution.Projects.Cast<EnvDTE.Project>().Where(p=>p.Kind != EnvDTE80.ProjectKinds.vsProjectKindSolutionFolder);
if (projects.Count() > 0)
projectList.AddRange(projects);
return projectList;
}
But this is searching only main solution and sub solution. But solution that i want to find has a longer path like "MainFolder\SubFolder1\SubFolder2\SubFolder3\SubSolution"

Resharper - Go To Implementation listing reference twice

In one of my solutions, when I right click a symbol and choose "Go To Implementation" for an object defined in one of the other solution projects, it lists the reference twice and forces me to choose one.
Based on the icons, it appears that one of the items in the list represents the project, and the other represents a dll. It doesn't matter which one I click - it goes to the same source file.
I only have the library reference once in this particular project - it is referencing the project.
What would cause this to happen? Some sort of circular reference issue perhaps?
As far as I can tell, this can also happen if you have a solution with several projects, where a certain project is referenced as project and also as pure file by two other projects in the solution.
Another advice that I can give if something is broken with ReSharper, is to clear the cache.
I had this problem and I just fixed it.
First, try do a Clean Solution and then a Build.
In my case, one rogue Project in my solution was compiled using an older version of the .NET framework than the other Projects, so when Resharper added a reference to my other Projects for me, it must have added it as a dll reference instead of as a Project reference.
My fix was
Upgrade old Project to the same version of .NET framework as the other Projects
Remove references to other Projects from that old Project
Add references to the other Projects again (as Project references this time)
Clean solution
Build solution
Done.
I've found a couple different cases that cause this problem, and got so annoyed that I wrote a little console app to scan my solution and find the problems for me. Here it is for anyone who might find this useful. To run it pass it the path to your solution folder and it will print out the issues on the console. It's very "quick and dirty" but it found the issues for me.
class Program
{
static void Main(string[] args)
{
if (args != null && args.Any())
{
foreach (var s in args)
{
Console.WriteLine("Checking " + s);
DirectoryInfo dir = new DirectoryInfo(s);
var files = dir.GetFiles("*.csproj", SearchOption.AllDirectories);
var projects = files.Select(x => new Project(x)).ToList();
var grouped = projects.GroupBy(x => x.TargetFrameworkVersion);
if(grouped.Count()>1)
{
Console.WriteLine("Solution contains multiple versions of Target Frameworks, this may cause duplicate assemblies in R# cache");
foreach (var group in grouped)
{
Console.WriteLine(group.Key);
foreach (var project in group)
{
Console.WriteLine(project.AssemblyName);
}
}
}
//loop through for debugging
foreach (var project in projects)
{
foreach (var reference in project.References)
{
foreach (var checkProject in projects)
{
if (checkProject.AssemblyName == reference)
{
Console.WriteLine("Reference in" + project.FileName + " referencing " +
reference+" that should be a ProjectReference, this may cause duplicate entries in R# Cache");
}
}
}
}
}
Console.WriteLine("Complete");
Console.ReadLine();
}
else
{
Console.WriteLine("You must provide a path to scan for csproj files");
}
}
}
public class Project
{
public string FileName { get; set; }
public string AssemblyName { get; set; }
public string ProjectGuid { get; set; }
public string TargetFrameworkVersion { get; set; }
public IList<string> References { get; set; }
private FileInfo _file;
private XmlDocument _document;
private XmlNamespaceManager _namespaceManager;
public Project(FileInfo file)
{
_file = file;
FileName = _file.FullName;
_document = new XmlDocument();
_document.Load(_file.FullName);
_namespaceManager = new XmlNamespaceManager(_document.NameTable);
_namespaceManager.AddNamespace("msbld", "http://schemas.microsoft.com/developer/msbuild/2003");
var projectGuidNode = _document.SelectSingleNode("//msbld:ProjectGuid", _namespaceManager);
ProjectGuid = projectGuidNode.InnerText;
var assemblyNameNode = _document.SelectSingleNode("//msbld:AssemblyName", _namespaceManager);
AssemblyName = assemblyNameNode.InnerText;
var targetFrameworkNode = _document.SelectSingleNode("//msbld:TargetFrameworkVersion", _namespaceManager);
TargetFrameworkVersion = targetFrameworkNode.InnerText;
References = new List<string>();
var referenceNodes = _document.SelectNodes("//msbld:Reference", _namespaceManager);
foreach (var node in referenceNodes)
{
var element = (XmlElement) node;
//file references
if (element.HasChildNodes)
{
foreach (var child in element.ChildNodes)
{
var childElement = (XmlElement)child;
if (childElement.Name == "HintPath")
{
var value = childElement.InnerText;
value = value.Substring(value.LastIndexOf("\\") + 1);
value = value.Replace(".dll", "");
References.Add(value);
}
}
}
//gac references
else
{
foreach (var attr in element.Attributes)
{
var attribute = (XmlAttribute)attr;
if (attribute.Name == "Include")
{
var value = attribute.Value;
string reference = value;
if (value.Contains(','))
{
reference = value.Substring(0, value.IndexOf(','));
}
References.Add(reference);
break;
}
}
}
}
}
}

C# Get form names of project A from Project B

I have two Projects in one solution, project A and project B (using VS2010 Ultimate and C# windows application).
Project B acts as a user management application for project A.
In project B i have a form that contains a chekcedlistbox control that will list all project A forms names and texts (this form will let system administrator to grant users the forms that are allowed to view/edit based on their security groups)
this is my code:
private void GetFormNames()
{
foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (Type t in a.GetTypes())
{
if (t.BaseType == typeof(Form))
{
var emptyCtor = t.GetConstructor(Type.EmptyTypes);
if (emptyCtor != null)
{
var f = (Form)emptyCtor.Invoke(new object[] { });
string FormText = f.Text;
string FormName = f.Name;
checkedListBox1.Items.Add("" + FormText + "//" + FormName + "");
}
}
}
}
}
the result i am getting is the form names of my current project (B) and Empty lines(//) and Select Window//MdiWindowDialog, PrintPreview.
I'm going to assume you've referenced ProjectA correctly and all the forms you're interested in actually have a public parameterless constructor. The problem is likely caused by ProjectA not being loaded yet, you can fix this in multiple ways. Probably the most direct is to use the static Assembly.Load (as long as the files are in the same directory, if not it gets more complicated).
try
{
Assembly projectA = Assembly.Load("ProjectA"); // replace with actual ProjectA name
// despite all Microsoft's dire warnings about loading from a simple name,
// you should be fine here as long as you don't have multiple versions of ProjectA
// floating around
foreach (Type t in projectA.GetTypes())
{
if (t.BaseType == typeof(Form))
{
var emptyCtor = t.GetConstructor(Type.EmptyTypes);
if (emptyCtor != null)
{
var f = (Form)emptyCtor.Invoke(new object[] { });
// t.FullName will help distinguish the unwanted entries and
// possibly later ignore them
string formItem = t.FullName + " // " + f.Text + " // " + f.Name;
checkedListBox1.Items.Add(formItem);
}
}
}
}
catch(Exception err)
{
// log exception
}
Another (probably cleaner solution) would be to have all the forms you're interested in inherit from a single base form. You could then load the assembly from that known Type and check that each enumerated Type inherits from it before adding it to your list. This is a more extensive change, however, and touches ProjectA.
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly a in assemblies)
{
Type[] types = a.GetTypes();
foreach (Type t in types)
{
if (t.BaseType == typeof(Form))
{
//Do Your works
}
}
}
Try This Code:
private void GetFormNames()
{
Type[] AllTypesInProjects = Assembly.GetExecutingAssembly().GetTypes();
for (int i = 0; i < AllTypesInProjects.Length; i++)
{
if (AllTypesInProjects[i].BaseType == typeof(Form))
{ /* Convert Type to Object */
Form f = (Form)Activator.CreateInstance(AllTypesInProjects[i]);
string FormText = f.Text;
listBox1.Items.Add(FormText);
}
}
}

What is the replacement of Controller.ReadFromRequest in ASP.NET MVC?

I am attempting to update a project from ASP.NET MVC Preview 3 to Preview 5 and it seems that Controller.ReadFromRequest(string key) has been removed from the Controller class. Does anyone know of any alternatives to retrieving information based on an identifier from a form?
Looks like they've added controller.UpdateModel to address this issue, signature is:
UpdateModel(object model, string[] keys)
I haven't upgraded my app personally, so I'm not sure of the actual usage. I'll be interested to find out about this myself, as I'm using controller.ReadFromRequest as well.
Not sure where it went. You could roll your own extension though:
public static class MyBindingExtensions
{
public static T ReadFromRequest < T > (this Controller controller, string key)
{
// Setup
HttpContextBase context = controller.ControllerContext.HttpContext;
object val = null;
T result = default(T);
// Gaurd
if (context == null)
return result; // no point checking request
// Bind value (check form then query string)
if (context.Request.Form[key] != null)
val = context.Request.Form[key];
if (val == null)
{
if (context.Request.QueryString[key] != null)
val = context.Request.QueryString[key];
}
// Cast value
if (val != null)
result = (t)val;
return result;
}
}
could you redo that link in something like tinyurl.com?
I need this info too but can get that mega-link to work.

Categories

Resources