-1

I have a created a dll with a Cmdlet command (see Get_DemoNames.cs). From this cmdlet I call a method UpdateXml(), so far everything is working. But UpdateXml() also creates files if they don't exist. When I call UpdateXml() in a class file like this:

var parser = new Parser();
parser.UpdateXml();

And I run the project it goes to the correct directories.

But if I load the import the dll and run the command DemoNames in a seperate test project like this:

PM> Import-Module C:\projects\EF.XML\EF.XML.dll
PM> DemoNames

The program goes to a wrong directory resulting in the following error:

Get-DemoNames : Access to the path 'C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\beheer_extern\config' is denied. At line:1 char:10 + DemoNames <<<< + CategoryInfo : NotSpecified: (:) [Get-DemoNames], UnauthorizedAccessException + FullyQualifiedErrorId : System.UnauthorizedAccessException,EF.XML.Get_DemoNames

I searched on the net for this error and found out that some other people were able to solve it by adding this line to the constructor:

public Parser()
{
    AppDomain.CurrentDomain.SetData("APPBASE", Environment.CurrentDirectory);
}

This gives me another wrong path:

Get-DemoNames : Access to the path 'C:\Windows\system32\beheer_extern\config' is denied. At line:1 char:10 + DemoNames <<<< + CategoryInfo : NotSpecified: (:) [Get-DemoNames], UnauthorizedAccessException + FullyQualifiedErrorId : System.UnauthorizedAccessException,EF.XML.Get_DemoNames

Get_DemoNames.cs

namespace EF.XML
{
using System;
using System.Linq;
using System.Management.Automation;

[Cmdlet(VerbsCommon.Get, "DemoNames")]
public class Get_DemoNames : PSCmdlet
{

    [Parameter(Position = 0, Mandatory = false)]
    public string prefix;

    protected override void ProcessRecord()
    {

        var names = new[] { "Chris", "Charlie", "Isaac", "Simon" };

        if (string.IsNullOrEmpty(prefix))
        {
            WriteObject(names, true);
        }
        else
        {
            var prefixed_names = names.Select(n => prefix + n);

            WriteObject(prefixed_names, true);
        }

        System.Diagnostics.Debug.Write("hello");

        var parser = new Parser();
        parser.UpdateXml();

    }

  }
}

Parser.cs

 public class Parser
{
    public void UpdateXml()
    {
                var directoryInfo = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory); // www directory

                var path = Path.Combine(directoryInfo.FullName, @"beheer_extern\config");

                //Creates the beheer_extern\config directory if it doesn't exist, otherwise nothing happens.
                Directory.CreateDirectory(path);

                var instellingenFile = Path.Combine(path, "instellingen.xml");
                var instellingenFileDb = Path.Combine(path, "instellingenDb.xml");

                //Create instellingen.xml if not already existing
                if (!File.Exists(instellingenFile))
                {
                    using (var writer = XmlWriter.Create(instellingenFile, _writerSettings))
                    {
                        var xDoc = new XDocument(
                            new XElement("database", string.Empty, new XAttribute("version", 4)));
                        xDoc.WriteTo(writer);
                    }
                }
      }
}

How can I get the right directory of the project (www directory)?

Sybren
  • 1,071
  • 3
  • 17
  • 51
  • Don't save stuff under a subdirectory of the executable. You should be saving data under AppData. –  Oct 21 '15 at 14:58
  • @Will This solution is supposed to update existing projects which need to have the xml files in `//www/beheer_extern/config`. I agree it's better practice to save in AppData, but is what I want possible? – Sybren Oct 21 '15 at 15:06
  • are you running this in powershell within visual studio? –  Oct 21 '15 at 15:08
  • @Will I load the dll with the `Cmdlet` class and the Parser class in the VS `Package Manager Console` – Sybren Oct 21 '15 at 16:29

2 Answers2

0

Okay, so you're trying to access a project, loaded within Visual Studio, from within the Package Manager Console.

Know that the executable is Visual Studio, and so AppDomain.CurrentDomain.BaseDirectory is going to be the Visual Studio install directory. It absolutely will not be the directory of the current project.

In order to get the project directory for the currently loaded solution, you need to interact with the running instance of Visual Studio via automation. Typically that's done by writing an extension or via the Visual Studio core automation, AKA the EnvDTE com object. This is complex. Want to do this? You'll probably have to grab a book on the subject and read.

Luckily, the PMC does provide a cmdlet that will simplify this greatly for you--get-project. It returns the DTE representation of the project, which you can then use this to get the project file's full filename, from which you can get the directory name.

Those are the pieces and parts you need. As for calling the cmdlet from your code, that's another question.

  • I found a fix by myself you can take a look if you want, see my edited question. – Sybren Oct 22 '15 at 08:13
  • @Sybren odd, but good for you. You shouldn't add your answer to your question. Just add it as an answer down here. After the waiting period, you can select it as correct and close out your question. That's the way it's done around here. Also, if you could add more details about *what* you did and *how* it works, that'd be great. I don't understand how your fix would work, and would like to know. Thanks. –  Oct 22 '15 at 13:12
  • I added the code as an answer, I also dived into EnvDTE and added my 'EnvDTE' solution to the answer. From my `Cmdlet` dll I can get the current solution with the code you see in my answer, I also can get the `Parser.cs` class (as a `ProjectItem`) from the current solution with: `var p = dte2.Solution.FindProjectItem("Parser.cs")` but do you know how I can make an `Parser` instance of `p` and call it's method (`UpdateXml`)? – Sybren Oct 22 '15 at 13:28
  • @Sybren nope. Might be a new question, if you can't work it out. –  Oct 22 '15 at 14:18
  • Okay no problem, I will open a new question if I can't find out how to do it. – Sybren Oct 22 '15 at 14:22
0

FIX

I managed to get it working with the following code

Get_DemoNames.cs

namespace EF.XML
{
using System;
using System.Linq;
using System.Management.Automation;

[Cmdlet(VerbsCommon.Get, "DemoNames")]
public class Get_DemoNames : PSCmdlet
{

[Parameter(Position = 0, Mandatory = false)]
public string prefix;

protected override void ProcessRecord()
{

    var names = new[] { "Chris", "Charlie", "Isaac", "Simon" };

    if (string.IsNullOrEmpty(prefix))
    {
        WriteObject(names, true);
    }
    else
    {
        var prefixed_names = names.Select(n => prefix + n);

        WriteObject(prefixed_names, true);
    }

    //added
    const string networkPath = "Microsoft.PowerShell.Core\\FileSystem::";
    var currentPath = SessionState.Path.CurrentFileSystemLocation.Path;

    var curProjectDir = currentPath.Substring(networkPath.Length); 

    WriteObject(curProjectDir);

    System.Diagnostics.Debug.Write("hello");

    var parser = new Parser {CurrentProjectDirectory = curProjectDir };
    parser.UpdateXml();

    }

  }
}

Parser.cs

public class Parser
{    



public string CurrentProjectDirectory{ get; set; }

public void UpdateXml()
{

    var wwwDirectory = Path.Combine(CurrentProjectDirectory, @"www"); // www directory

    var path = Path.Combine(wwwDirectory, @"beheer_extern\config");

    //Creates the beheer_extern\config directory if it doesn't exist, otherwise nothing happens.
    Directory.CreateDirectory(path);

    var instellingenFile = Path.Combine(path, "instellingen.xml");
    var instellingenFileDb = Path.Combine(path, "instellingenDb.xml");

    //Create instellingen.xml if not already existing
    if (!File.Exists(instellingenFile))
    {
        using (var writer = XmlWriter.Create(instellingenFile, _writerSettings))
        {
            var xDoc = new XDocument(
                new XElement("database", string.Empty, new XAttribute("version", 4)));
            xDoc.WriteTo(writer);
        }
    }
  }
}

I also tried EnvDTE which also works.

Required imports:

using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.Shell;

Code to get the current solution (path):

 DTE2 dte2 = Package.GetGlobalService(typeof(DTE)) as DTE2;

        if (dte2 != null)
        {
            WriteObject(dte2.Solution.FullName);
        }
Sybren
  • 1,071
  • 3
  • 17
  • 51