5

I understand that the following snippets can be used to extract a VS solution info when used in a plug-in.

EnvDTE.Solution solution = CodeRush.ApplicationObject.Solution;
EnvDTE.Projects projects = solution.Projects;

Q: I would like to build a console application and access these file details. My aim is to create a console application (that can be run without VS) to generate a report based on the design issues I find in the input .sln file. What functions do I use for this?

akjoshi
  • 15,374
  • 13
  • 103
  • 121
Shaun
  • 199
  • 1
  • 10
  • 1
    I would like to give the location of a solution and access all its project files as a console application and not as a plugin (class library) – Shaun May 30 '11 at 16:14
  • 1
    This is rather a complex task, because DXCore is not designed to be used outside of the Visual Studio environment. – Alex Skorkin May 30 '11 at 16:44
  • Thanks Alex. But I can use most functions in a console application as well. I am able to parse multiple files in sequence, but I wanted to input a solution file and then work with all the files within it. Can you give an idea how complex the task can be?? Thanks – Shaun May 31 '11 at 08:21
  • 2
    Outside IDE, DXCore can parse files (or an entire solution), so you can enumerate solution elements, get their characteristics (e.g. code metrics), and generate code. However, you can't use DXCore APIs to change files, and no DXCore services will be available (accessible through the CodeRush.XXX methods). On the other hand, DXCore services can be emulated, but it is necessary to completely rewrite them. I'm going to create a blog post on my site on how to use DXCore outside of Visual Studio. This will require writing a lot of code, which I will share as well if everything goes as expected. – Alex Skorkin May 31 '11 at 11:12
  • @Alex. It would be of great help to use DXCore to be used outside VS also. But how I can at least parse an entire solution. I tried the following, but in vain: `CSharp30Parser SolnParser = new CSharp30Parser(); fileNode = SolnParser.ParseFile(solnPath) as SolutionElement;` Can you suggest changes? Thanks – Shaun May 31 '11 at 11:29
  • @Shaun, Changes are very huge and this is the most grand-scale part. There are too many things that need to be done manually, for example: read every project's settings such as a language, files list, assembly references list, etc. – Alex Skorkin May 31 '11 at 12:01
  • @Alex. Thanks. Your earlier comment was "Outside IDE, DXCore can parse files (or an entire solution)" Can you give a small snippet to illustrate parsing of an entire solution? – Shaun Jun 01 '11 at 11:57

1 Answers1

9

The original (and updated) post is located here.

Actually, DXCore is not designed to be used outside of Visual Studio, but there are always workarounds... In this article I'm going to show you how to use the DXCore Framework inside the regular C# Console Application to parse an entire solution and work with the abstract parsed tree. The solution should be passed-in as an argument to the program as a full complete path to the *.sln file. If there's no argument used, the hard-coded path to the test program is used, so the program will parse itself and print information about the solution, such as a list of all types used and the number of members inside of each class.

Let's create a new C# Console Application, call it TestDXCoreConsoleApp and save it inside the "C:\Project" folder:

Creating a new Console App

Then, we should change the Target Framework version of the new project to Framework 4.0, so it's not a "Target Framework 4.0 Client Profile", because some required assembly references don't support this version of the Target Framework:

Settings Target Framework

Now, let add required assembly references. Here's the list of what we need:

  1. DXCore assemblies:
  • DevExpress.CodeRush.Common
  • DevExpress.CodeRush.Core
  • DevExpress.CodeRush.StructuralParser
  • DevExpress.CodeRush.VSCore
  • DevExpress.DXCore.AssemblyResolver
  • DevExpress.DXCore.Parser

These assemblies canbe found inside your DevExpress IDE Tools installation folder. For example, the path may look like this:

C:\Program Files\DevExpress 2011.1\IDETools\System\DXCore\BIN

  1. Now, three additional assemblies for different program language support:
  • DX_CPPLanguage
  • DX_CSharpLanguage
  • DX_VBLanguage

With these assemblies we are able to parse CSharp, Visual Basic and C++ projects. They can be found here:

C:\Program Files (x86)\DevExpress 2011.1\IDETools\System\DXCore\BIN\SYSTEM

  1. .NET Framework assemblies:
  • Microsoft.Build.BuildEngine.dll
  1. And, finally, a couple of Visual Studio assemblies:
  • EnvDTE
  • VsLangProj

These two can be found in the "PublicAssemblies" folder:

C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies\

Now, the DXCore support code. This code is required to load a solution, its projects and initialize DXCore parsers. I've added two folders:

  1. The Helpers folder contains the following classes:
  • LanguageHelper.cs - detects the language of projects (e.g. CSharp, Visual Basic or C++).
  • ParserHelper.cs - initializes DXCore parsers, and a few important DXCore services - the Source Model service and the Language service which are used to parse source code.
  • SolutionParser.cs - a helper class, which takes the path to the solution that you are going to parse. Calling the GetParsedSolution method will return the SolutionElement, which holds the abstract source tree of the entire solution.
  1. The Loaders folder contains the Visual Studio project and solution loaders for different Visual Studio versions. They are used to parse *.XXproj and *.sln files. There are versions for VS2002, VS2003 and VS2005. There are no dedicated loaders for VS2008 and VS2010, because those loaders for the old VS versions are perfectly fine to reading and loading newer Visual Studio project and solution format files (e.g. 2008, 2010).

Here's the final structure of the TestDXCoreConsoleApp:

DXCore ConsoleApp structure

The TestDXCoreConsoleApp with the full source is here (267,457 bytes, C#, VS2010), so you may review the code and use it as you'd like. Here's the Main function of the Program class:

static void Main(string[] args)
{
  string SolutionPath;
  if (args != null && args.Length > 0)
    SolutionPath = args[0];
  else
    SolutionPath = @"c:\Projects\TestDXCoreConsoleApp\TestDXCoreConsoleApp.sln";

  try
  {
    ParserHelper.RegisterParserServices();

    Console.Write("Parsing solution... ");

    SolutionParser solutionParser = new SolutionParser(SolutionPath);
    SolutionElement solution = solutionParser.GetParsedSolution();
    if (solution == null)
      return;

    Console.WriteLine("Done.");

    foreach (ProjectElement project in solution.AllProjects)
      foreach (SourceFile file in project.AllFiles)
        foreach (TypeDeclaration type in file.AllTypes)
        {
          Console.Write(type.FullName);
          Console.WriteLine(", members: " + ((ITypeElement)type).Members.Count);
        }
  }
  catch (Exception ex)
  {
    Console.WriteLine(ex.Message);
  }
  finally
  {
    ParserHelper.UnRegisterParserServices();
  }

  Console.ReadLine();
}

If you put the sources into the "C:\Projects" folder and run the program without any arguments specified, you should see the following result:

DXCore Console App result

Press the Enter key to close the window. Bear in mind, that the parsing process may take some time, so you might need to wait a few seconds, until the entire solution is parsed.

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Alex Skorkin
  • 4,264
  • 3
  • 25
  • 47
  • Thanks Alex for the workaround. I could work with it and get expected outputs. It works fine with VS2008 with .NET 3.5 with DXCore 10.2.6. You had indicated the use of .NET 4, does it make any difference?? Thanks again. – Shaun Jun 07 '11 at 12:04
  • @Alex. I had seen a bounty +50 on your reply. I am quite new to StackOverflow, how do I use it? – Shaun Jun 07 '11 at 12:08
  • @Shaun I'm not totally sure what a bounty is - I was just testing this thing. There's some info about 'bounty' in the StackOverflow FAQ. – Alex Skorkin Jun 07 '11 at 18:54
  • @Alex: As I have been working with the packages sent by you. it is really good. There is one issue, my solution hierarchy has the structure: [Solution heirarchy](http://i.imgur.com/r9stG.jpg) The there is a conflict in the project names, the implementation as a Dictionary is throwing an exception and not parsing the entire solution. How do I tweak it? Thanks – Shaun Jun 17 '11 at 14:31
  • @Shaun, could you please provide a call stack and tell me what exception is being thrown? Thanks. – Alex Skorkin Jun 17 '11 at 15:03
  • @Alex, I see the following exception: Exception caught: Item has already been added. Key in dictionary: 'Automate' Key being added: 'Automate'. – Shaun Jun 20 '11 at 08:21
  • @Alex: Thank you for a wonderful blog. I was trying to use classInstance.GetBaseTypes(); in main() program but this did not work. EXCEPTION: ParserServices.ProfilingService: ProfilingService must be registered before calling. Thanks. – Vinod Menezes Jun 21 '11 at 08:47
  • @Shaun, The sample program doesn't use dictionaries to store any keys/values. It seems that there's some issue inside the DXCore Framework. If you could provide us a call stack, or a project to for reproduction, we will try to fix the issue. – Alex Skorkin Jun 22 '11 at 18:34
  • 1
    @Vinod, To fix the exception with the ParserServices.ProfilingService, open up the ParserHelper.cs and put these lines of code into the RegisterParserServices() method: IProfilingService lProfiling = (IProfilingService)CreateParserService(typeof(ProfilingServices)); ParserServices.RegisterProfilingService(lProfiling); And don't forget to put this line of code into the UnRegisterParserServices() method: ParserServices.UnregisterProfilingService(); – Alex Skorkin Jun 22 '11 at 18:34
  • @Alex: The following output if from System.Environment.StackTrace(): at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo) at System.Environment.get_StackTrace() at TestDXCoreConsoleApp.Program.Main(String[] args) in F:\Internship\implementation\Code plugins\TestDXCoreConsoleApp\solnProgram.cs:line 77 at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args) at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() – Shaun Jun 23 '11 at 14:27
  • and this.. everything did not fit in one comment: at System.Threading.ThreadHelper.ThreadStart_Context(Object state) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart() – Shaun Jun 23 '11 at 14:28
  • @Shaun, I apologize, but the stack is not very descriptive. There are no DevExpress assemblies present in the stack, so it is difficult to see where the exception is being thrown inside DXCore. If you can send a small test project to the DevExpress Support Services at support[@]devexpress[dot]com, we will take a look at the issue and fix it. – Alex Skorkin Jun 28 '11 at 19:59
  • @Alex, I have emailed it to the support. Hope it reaches you. – Shaun Jun 30 '11 at 12:57
  • @Shaun, got it. The bug is reproduced. You are welcome to track its status at http://www.devexpress.com/issue=B186988. Once the bug is fixed, you can request a daily build, containing the fix, from the Support Team. – Alex Skorkin Jun 30 '11 at 18:30
  • @Shaun, the bug is fixed. You are welcome to request a daily build with the fix from the Support Team. Thanks for the feedback! – Alex Skorkin Jul 08 '11 at 09:33