Project Names in Visual Studio Solution sometimes are empty - c#

In visual studio extension I would like to get all projects and their names:
var service = (DTE) Package.GetGlobalService(typeof (SDTE));
var projects = service.Solution.Projects;
foreach (Project project in projects)
//....
This approach work nice and neat for exception of one little problem:
project variable returns really exact number of project. But project's full name might be EMPTY if it located in the solution's folder. (I mean the structure of solution when projects are united in the solution's folder)
How to get these project properly?

Here is a solution.
This guy is the Lord of Rings as minimum:
http://www.wwwlicious.com/2011/03/29/envdte-getting-all-projects-html/
Relevant information from the blog, in case it goes away (like so many other answers on Stack Overflow):
DTE2.Solution.Projects will only give you the top level list of items under the Solution so any projects nested within Solution Folders are missed. In order to get at these pesky little worms, you need to burrow into the ProjectItem.SubProject property accessed from the ProjectItems collection on the the Project. Just to cap the whole lot off, if you have nested solution folders, then you need some recursion.
using System.Collections.Generic;
using EnvDTE;
using EnvDTE80;
public static class SolutionProjects
{
public static DTE2 GetActiveIDE()
{
// Get an instance of currently running Visual Studio IDE.
DTE2 dte2 = Package.GetGlobalService(typeof(DTE)) as DTE2;
return dte2;
}
public static IList<Project> Projects()
{
Projects projects = GetActiveIDE().Solution.Projects;
List<Project> list = new List<Project>();
var item = projects.GetEnumerator();
while (item.MoveNext())
{
var project = item.Current as Project;
if (project == null)
{
continue;
}
if (project.Kind == ProjectKinds.vsProjectKindSolutionFolder)
{
list.AddRange(GetSolutionFolderProjects(project));
}
else
{
list.Add(project);
}
}
return list;
}
private static IEnumerable<Project> GetSolutionFolderProjects(Project solutionFolder)
{
List<Project> list = new List<Project>();
for (var i = 1; i <= solutionFolder.ProjectItems.Count; i++)
{
var subProject = solutionFolder.ProjectItems.Item(i).SubProject;
if (subProject == null)
{
continue;
}
// If this is another solution folder, do a recursive call, otherwise add
if (subProject.Kind == ProjectKinds.vsProjectKindSolutionFolder)
{
list.AddRange(GetSolutionFolderProjects(subProject));
}
else
{
list.Add(subProject);
}
}
return list;
}
}

Related

Cannot get SyntaxTree from Compilation object

I'm a beginner of roslyn, so I tried to start learning it by making a very simple console application, which is introduced in the famous tutorial site. (https://riptutorial.com/roslyn/example/16545/introspective-analysis-of-an-analyzer-in-csharp), and it didn't work well.
The Cosole Application I made is of .NET Framework (target Framework version is 4.7.2), and not of .NET Core nor .NET standard.
I added the NuGet package Microsoft.CodeAnalysis, and Microsoft.CodeAnalysis.Workspaces.MSBuild, then wrote a simple code as I show below.
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
using System;
using System.Linq;
namespace SimpleRoslynConsole
{
class Program
{
static void Main(string[] args)
{
// Declaring a variable with the current project file path.
// *** You have to change this path to fit your development environment.
const string projectPath =
#"C:\Users\[MyName]\Source\Repos\RoslynTrialConsole01\RoslynTrialConsole01.csproj";
var workspace = MSBuildWorkspace.Create();
var project = workspace.OpenProjectAsync(projectPath).Result;
// [**1]Getting the compilation.
var compilation = project.GetCompilationAsync().Result;
// [**2]As this is a simple single file program, the first syntax tree will be the current file.
var syntaxTree = compilation.SyntaxTrees.FirstOrDefault();
if (syntaxTree != null)
{
var rootSyntaxNode = syntaxTree.GetRootAsync().Result;
var firstLocalVariablesDeclaration = rootSyntaxNode.DescendantNodesAndSelf()
.OfType<LocalDeclarationStatementSyntax>().First();
var firstVariable = firstLocalVariablesDeclaration.Declaration.Variables.First();
var variableInitializer = firstVariable.Initializer.Value.GetFirstToken().ValueText;
Console.WriteLine(variableInitializer);
}
else
{
Console.WriteLine("Could not get SyntaxTrees from this projects.");
}
Console.WriteLine("Hit any key.");
Console.ReadKey();
}
}
}
My problem is that, SyntaxTrees property of Compilation object returns null in [**2]mark. Naturally, following FirstOrDefault method returns null.
I've tried several other code. I found I could get SyntaxTree from CSharp code text, by using CSharpSyntaxTree.ParseText method. But I couldn't get any from source code, by the sequence of
var workspace = MSBuildWorkspace.Create();
var project = workspace.OpenProjectAsync(projectPath).Result;
var compilation = project.GetCompilationAsync().Result;
What I'd like to know is if I miss something to get Syntax information from source code by using above process.
I'll appreciate someone give me a good advice.
I think the issue is that .net framework projects have their source files paths within their .csproj. And opening project works right away.
For .net core project you have no such information and, maybe, this is why Workspace instance doesn't know what to load and so loads nothing.
At least specifying .cs files as added documents does the trick. Try to apply this:
static class ProjectExtensions
{
public static Project AddDocuments(this Project project, IEnumerable<string> files)
{
foreach (string file in files)
{
project = project.AddDocument(file, File.ReadAllText(file)).Project;
}
return project;
}
private static IEnumerable<string> GetAllSourceFiles(string directoryPath)
{
var res = Directory.GetFiles(directoryPath, "*.cs", SearchOption.AllDirectories);
return res;
}
public static Project WithAllSourceFiles(this Project project)
{
string projectDirectory = Directory.GetParent(project.FilePath).FullName;
var files = GetAllSourceFiles(projectDirectory);
var newProject = project.AddDocuments(files);
return newProject;
}
}
Method WithAllsourceFiles will return you the project, compilation of which will in its turn have all syntax trees you would expect of it, as you would have in Visual Studio
MsBuildWorkspace won't work correctly unless you have all the same redirects in your app's app.config file that msbuild.exe.config has in it. Without the redirects, it's probably failing to load the msbuild libraries. You need to find the msbuild.exe.config file that is on your system and copy the <assemblyBinding> elements related to Microsoft.Build assemblies into your app.config. Make sure you place them under the correct elements configuration/runtime.
I searched various sample programs on the net and found the most reliable and safest method. The solution is to create a static method which returns SyntaxTrees in designated File as follow.
private static Compilation CreateTestCompilation()
{
var found = false;
var di = new DirectoryInfo(Environment.CurrentDirectory);
var fi = di.GetFiles().Where((crt) => { return crt.Name.Equals("program.cs", StringComparison.CurrentCultureIgnoreCase); }).FirstOrDefault();
while ((fi == null) || (di.Parent == null))
{
di = new DirectoryInfo(di.Parent.FullName);
fi = di.GetFiles().Where((crt) => { return crt.Name.Equals("program.cs", StringComparison.CurrentCultureIgnoreCase); }).FirstOrDefault();
if (fi != null)
{
found = true;
break;
}
}
if (!found)
{
return null;
}
var targetPath = di.FullName + #"\Program.cs";
var targetText = File.ReadAllText(targetPath);
var targetTree =
CSharpSyntaxTree.ParseText(targetText)
.WithFilePath(targetPath);
var target2Path = di.FullName + #"\TypeInferenceRewriter.cs";
var target2Text = File.ReadAllText(target2Path);
var target2Tree =
CSharpSyntaxTree.ParseText(target2Text)
.WithFilePath(target2Path);
SyntaxTree[] sourceTrees = { programTree, target2Tree };
MetadataReference mscorlib =
MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
MetadataReference codeAnalysis =
MetadataReference.CreateFromFile(typeof(SyntaxTree).Assembly.Location);
MetadataReference csharpCodeAnalysis =
MetadataReference.CreateFromFile(typeof(CSharpSyntaxTree).Assembly.Location);
MetadataReference[] references = { mscorlib, codeAnalysis, csharpCodeAnalysis };
return CSharpCompilation.Create("TransformationCS",
sourceTrees,
references,
new CSharpCompilationOptions(
OutputKind.ConsoleApplication));
}
And the caller program will be like this.
static void Main(string[] args)
{
var test = CreateTestCompilation();
if (test == null)
{
return;
}
foreach (SyntaxTree sourceTree in test.SyntaxTrees)
{
Console.WriteLine(souceTree.ToFullString());
}
}
Of course, many improvements are needed to put it to practical use.

How to set linker options for VC project using EnvDTE

I'm using EnvDTE to modify the linker and compiler settings/options of a VC project in a Visual Studio Add-in. But I can't seem to find where I can access these options from the DTE instance. What I have so far is
// I successfully can open the solution and get the project I'd like to
// modify the build options of (compiler and linker options)
foreach (EnvDTE.Project p in VS2015Instance.Solution.Projects)
{
if(p.UniqueName.Contains(projectName))
{
// At this point I have a reference to my VC project.
// Here I'd like to set some linker option before building the
// project.
VS2015Instance.ExecuteCommand("Build.BuildSolution");
}
}
So, where can I get/set these options?
I ended up using Microsoft.VisualStudio.VCProjectEngine in conjunction with EnvDTE to do what I wanted to do:
VCLinkerTool linker;
foreach (EnvDTE.Project p in VS2015Instance.Solution.Projects)
{
if (p.UniqueName.Contains(project.Name))
{
var prj = (VCProject)p.Object;
var cfgs = (IVCCollection)prj.Configurations;
foreach (VCConfiguration cfg in cfgs)
{
if (cfg.ConfigurationName.Contains("Debug"))
{
var tools = (IVCCollection)cfg.Tools;
foreach (var tool in tools)
{
if (tool is VCLinkerTool)
{
linker = (VCLinkerTool)tool;
// now I can use linker to set its options.
break;
}
}
break;
}
}
break;
}
}

Visual Studio 2015 build drop location

Our company recently updated TFS to 2015 update 1. After that context menu item named Drop folder disappeared from completed builds. I found nothing about it and how to bring it back. When I click Open on completed build, VS opens web version of TFS where I forced to click through the menus and copy drop folder path manually. So I decided to write a simple extension that will add this item to the menu.
Some googling brought me to this page. But it seems that the example code is quite old and not working in VS2015:
IVsTeamFoundationBuild vsTfBuild = (IVsTeamFoundationBuild)GetService(typeof(IVsTeamFoundationBuild));
IBuildDetail[] builds = vsTfBuild.BuildExplorer.CompletedView.SelectedBuilds;
Property SelectedBuilds is always empty. I suppose that it relates to old window from VS2010. It returns items that are instance of IBuildDetail interface.
So I found this piece of code here:
var teamExplorer = (ITeamExplorer)ServiceProvider.GetService(typeof(ITeamExplorer));
var page = teamExplorer.CurrentPage;
var buildsPageExt = (IBuildsPageExt)page.GetExtensibilityService(typeof(IBuildsPageExt));
var build = buildsPageExt.SelectedBuilds[0];
Here build is the instance of IBuildModel interface. It lacks DropLocation property.
Is there any way to found drop location of selected build? Or maybe latest build?
You can use IBuildDedetail.DropLocation in .NET client libraries for Visual Studio Team Services (and TFS). Basic code for your reference:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.Build.Client;
using Microsoft.TeamFoundation.Client;
namespace BuildAPI
{
class Program
{
static void Main(string[] args)
{
string project = "http://xxx.xxx.xxx.xxx";
TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(new Uri(project));
IBuildServer ibs = tpc.GetService<IBuildServer>();
var builds = ibs.QueryBuilds("TeamProjectName");
foreach (IBuildDetail ibd in builds)
{
Console.WriteLine(ibd.DropLocation);
Console.ReadLine();
}
}
}
}
So, after digging through TFS API, I ended up with this workaround.
private void MenuItemCallback(object sender, EventArgs e)
{
var context = (ITeamFoundationContextManager)ServiceProvider.GetService(typeof(ITeamFoundationContextManager));
IBuildServer buildServer = context.CurrentContext.TeamProjectCollection.GetService<IBuildServer>();
var teamExplorer = (ITeamExplorer)ServiceProvider.GetService(typeof(ITeamExplorer));
var buildsPageExt = (IBuildsPageExt)teamExplorer.CurrentPage.GetExtensibilityService(typeof(IBuildsPageExt));
var menuCommand = (MenuCommand)sender;
if (menuCommand.CommandID.Guid == CommandSetCompleted)
{
foreach (var buildDetail in buildsPageExt.SelectedBuilds)
Process.Start("explorer.exe", GetBuild(buildServer, buildDetail).DropLocation);
}
if (menuCommand.CommandID.Guid == CommandSetFavorite)
{
var definitions = buildsPageExt.SelectedFavoriteDefinitions.Concat(buildsPageExt.SelectedXamlDefinitions).ToArray();
foreach (var build in GetLatestSuccessfulBuild(buildServer, definitions))
Process.Start("explorer.exe", build.DropLocation);
}
}
private IBuildDetail GetBuild(IBuildServer buildServer, IBuildModel buildModel)
{
Uri buildUri = new Uri(buildModel.GetType().GetProperty("UriToOpen").GetValue(buildModel).ToString());
return buildServer.GetBuild(buildUri);
}
private IBuildDetail[] GetLatestSuccessfulBuild(IBuildServer buildServer, IDefinitionModel[] buildDefinitions)
{
var spec = buildServer.CreateBuildDetailSpec(buildDefinitions.Select(bd => bd.Uri));
spec.MaxBuildsPerDefinition = 1;
spec.QueryOrder = BuildQueryOrder.FinishTimeDescending;
spec.Status = BuildStatus.Succeeded;
var builds = buildServer.QueryBuilds(spec);
return builds.Builds;
}

ENVDTE - Add new project to existing solution and locate it in a specific folder

I wrote a Visual Studio Wizard Template using c# on visual studio 2012.
I followed the MSDN steps: I created a VS template, then I created a Class Library project with a class which implements the IWizard interface, I configured the .vstemplate file and etc...
In my Class Library project I copy an existing solution from some directory in my computer, I add the new generated project to that solution, and run it.
I'm doing this like:
public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
{
solutionDir = replacementsDictionary["$solutiondirectory$"];
destProjectDir = replacementsDictionary["$destinationdirectory$"];
projName = replacementsDictionary["$specifiedsolutionname$"];
EmulationDir = #"MY_PATH\TestSln";
DirectoryCopy(EmulationDir, solutionDir);
dte = (DTE2)automationObject;
}
public void RunFinished()
{
Solution2 solution;
Project p;
solution = (Solution2)dte.Solution;
solution.Open(solutionDir + "\\TestSln.sln");
p = solution.AddFromFile(destProjectDir + "\\" + projName + ".vcxproj");
}
but I have to add the new project to a specific sub-folder of the solution: the above code adds the new project to the solution straightly, and I'ld like to add it to the solutionDir\apps.
Do you know about any way to do thus?
thanks!!
You can accomplish this using SolutionFolder interface:
Project project = getSolutionSubFolder(solution, "SubFolderName");
if (project != null)
{
SolutionFolder folder = (SolutionFolder)project.Object;
folder.AddFromFile("yourProjectFilePath");
}
Where getSolutionSubFolder method looks like this:
private static Project getSolutionSubFolder(Solution2 solution, string subfolder)
{
return
solution
.Projects
.Cast<Project>()
.FirstOrDefault(
p => string.Equals(p.Name, subfolder, StringComparison.Ordinal));
}

How do you get the current project directory from C# code when creating a custom MSBuild task?

Instead of running an external program with its path hardcoded, I would like to get the current Project Dir. I'm calling an external program using a process in the custom task.
How would I do that? AppDomain.CurrentDomain.BaseDirectory just gives me the location of VS 2008.
using System;
using System.IO;
// This will get the current WORKING directory (i.e. \bin\Debug)
string workingDirectory = Environment.CurrentDirectory;
// or: Directory.GetCurrentDirectory() gives the same result
// This will get the current PROJECT bin directory (ie ../bin/)
string projectDirectory = Directory.GetParent(workingDirectory).Parent.FullName;
// This will get the current PROJECT directory
string projectDirectory = Directory.GetParent(workingDirectory).Parent.Parent.FullName;
You can try one of this two methods.
string startupPath = System.IO.Directory.GetCurrentDirectory();
string startupPath = Environment.CurrentDirectory;
Tell me, which one seems to you better
If a project is running on an IIS express, the Environment.CurrentDirectory could point to where IIS Express is located ( the default path would be C:\Program Files (x86)\IIS Express ), not to where your project resides.
This is probably the most suitable directory path for various kinds of projects.
AppDomain.CurrentDomain.BaseDirectory
This is the MSDN definition.
Gets the base directory that the assembly resolver uses to probe for assemblies.
The proper1 way to get the root folder of a C# project is to leverage the [CallerFilePath] attribute to obtain the full path name of a source file, and then subtract the filename plus extension from it, leaving you with the path to the project.
Here is how to actually do it:
In the root folder of your project, add file ProjectSourcePath.cs with the following content:
internal static class ProjectSourcePath
{
private const string myRelativePath = nameof(ProjectSourcePath) + ".cs";
private static string? lazyValue;
public static string Value => lazyValue ??= calculatePath();
private static string calculatePath()
{
string pathName = GetSourceFilePathName();
Assert( pathName.EndsWith( myRelativePath, StringComparison.Ordinal ) );
return pathName.Substring( 0, pathName.Length - myRelativePath.Length );
}
}
The string? requires a pretty late version of C# with #nullable enable; if you don't have it, then just remove the ?.
The Assert() function is my own; you can replace it with your own, or omit it, if you like living your life dangerously.
The function GetSourceFilePathName() is defined as follows:
using System.Runtime.CompilerServices
public static string GetSourceFilePathName( [CallerFilePath] string? callerFilePath = null ) //
=> callerFilePath ?? "";
Once you have the above, you can use it as follows:
string projectSourcePath = ProjectSourcePath.Value;
1 'proper' as in: fool-proof; sure-fire; without presumptions; not being held together by shoestrings; not bound to work for some projects but fail for others; not likely to horribly break without a warning when you change unrelated things; etc.
This will also give you the project directory by navigating two levels up from the current executing directory (this won't return the project directory for every build, but this is the most common).
System.IO.Path.GetFullPath(#"..\..\")
Of course you would want to contain this inside some sort of validation/error handling logic.
If you want ot know what is the directory where your solution is located, you need to do this:
var parent = Directory.GetParent(Directory.GetCurrentDirectory()).Parent;
if (parent != null)
{
var directoryInfo = parent.Parent;
string startDirectory = null;
if (directoryInfo != null)
{
startDirectory = directoryInfo.FullName;
}
if (startDirectory != null)
{ /*Do whatever you want "startDirectory" variable*/}
}
If you let only with GetCurrrentDirectory() method, you get the build folder no matter if you are debugging or releasing. I hope this help! If you forget about validations it would be like this:
var startDirectory = Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.FullName;
Based on Gucu112's answer, but for .NET Core Console/Window application, it should be:
string projectDir =
Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"..\..\.."));
I'm using this in a xUnit project for a .NET Core Window Application.
If you really want to ensure you get the source project directory, no matter what the bin output path is set to:
Add a pre-build event command line (Visual Studio: Project properties -> Build Events):
echo $(MSBuildProjectDirectory) > $(MSBuildProjectDirectory)\Resources\ProjectDirectory.txt
Add the ProjectDirectory.txt file to the Resources.resx of the project (If it doesn't exist yet, right click project -> Add new item -> Resources file)
Access from code with Resources.ProjectDirectory.
This solution works well for me, on Develop and also on TEST and PROD servers with ASP.NET MVC5 via C#:
var projectDir = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
If you need project directory in project configuration file use:
$(ProjectDir)
I was looking for this too. I've got a project that runs HWC, and I'd like to keep the web site out of the app tree, but I don't want to keep it in the debug (or release) directory. FWIW, the accepted solution (and this one as well) only identifies the directory the executable is running in.
To find that directory, I've been using
string startupPath = System.IO.Path.GetFullPath(".\\").
using System;
using System.IO;
// Get the current directory and make it a DirectoryInfo object.
// Do not use Environment.CurrentDirectory, vistual studio
// and visual studio code will return different result:
// Visual studio will return #"projectDir\bin\Release\netcoreapp2.0\", yet
// vs code will return #"projectDir\"
var currentDirectory = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory);
// On windows, the current directory is the compiled binary sits,
// so string like #"bin\Release\netcoreapp2.0\" will follow the project directory.
// Hense, the project directory is the great grand-father of the current directory.
string projectDirectory = currentDirectory.Parent.Parent.Parent.FullName;
I had a similar situation, and after fruitless Googles, I declared a public string, which mods a string value of the debug / release path to get the project path. A benefit of using this method is that since it uses the currect project's directory, it matters not if you are working from a debug directory or a release directory:
public string DirProject()
{
string DirDebug = System.IO.Directory.GetCurrentDirectory();
string DirProject = DirDebug;
for (int counter_slash = 0; counter_slash < 4; counter_slash++)
{
DirProject = DirProject.Substring(0, DirProject.LastIndexOf(#"\"));
}
return DirProject;
}
You would then be able to call it whenever you want, using only one line:
string MyProjectDir = DirProject();
This should work in most cases.
Another way to do this
string startupPath = System.IO.Directory.GetParent(#"./").FullName;
If you want to get path to bin folder
string startupPath = System.IO.Directory.GetParent(#"../").FullName;
Maybe there are better way =)
Yet another imperfect solution (but perhaps a little closer to perfect than some of the others):
protected static string GetSolutionFSPath() {
return System.IO.Directory.GetParent(System.IO.Directory.GetCurrentDirectory()).Parent.Parent.FullName;
}
protected static string GetProjectFSPath() {
return String.Format("{0}\\{1}", GetSolutionFSPath(), System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
}
This version will return the current projects' folder even if the current project is not the Startup Project for the solution.
The first flaw with this is that I've skipped all error checking. That can be fixed easy enough but should only be a problem if you're storing your project in the root directory for the drive or using a junction in your path (and that junction is a descendant of the solution folder) so this scenario is unlikely. I'm not entirely sure that Visual Studio could handle either of these setups anyway.
Another (more likely) problem that you may run into is that the project name must match the folder name for the project for it to be found.
Another problem you may have is that the project must be inside the solution folder. This usually isn't a problem but if you've used the Add Existing Project to Solution option to add the project to the solution then this may not be the way your solution is organized.
Lastly, if you're application will be modifying the working directory, you should store this value before you do that because this value is determined relative to the current working directory.
Of course, this all also means that you must not alter the default values for your projects' Build->Output path or Debug->Working directory options in the project properties dialog.
Try this, its simple
HttpContext.Current.Server.MapPath("~/FolderName/");
string projPath = Path.GetFullPath(#"..\..\..\");
Console.WriteLine(projPath);
This consistently works well for me. Give it a go.
After I had finally finished polishing my first answer regarding the us of public strings to derive an answer, it dawned on me that you could probably read a value from the registry to get your desired result. As it turns out, that route was even shorter:
First, you must include the Microsoft.Win32 namespace so you can work with the registry:
using Microsoft.Win32; // required for reading and / or writing the registry
Here is the main code:
RegistryKey Projects_Key = Registry.CurrentUser.OpenSubKey(#"SOFTWARE\Microsoft\VisualStudio\9.0", false);
string DirProject = (string)Projects_Key.GetValue(#"DefaultNewProjectLocation");
A note on this answer:
I am using Visual Studio 2008 Professional Edition. If you are using another version, (i.e. 2003, 2005, 2010; etc.), then you mayt have to modify the 'version' part of the SubKey string (i.e. 8.0, 7.0; etc.).
If you use one of my answers, and if it is not too much to ask, then I would like to know which of my methods you used and why. Good luck.
dm
Use this to get the Project directory (worked for me):
string projectPath =
Directory.GetParent(Directory.GetCurrentDirectory()).Parent.FullName;
I have used following solution to get the job done:
string projectDir =
Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"..\.."));
Try:
var pathRegex = new Regex(#"\\bin(\\x86|\\x64)?\\(Debug|Release)$", RegexOptions.Compiled);
var directory = pathRegex.Replace(Directory.GetCurrentDirectory(), String.Empty);
This is solution different from the others does also take into account possible x86 or x64 build.
(Because 22 answers are not enough... here's one more....)
Mike Nakis posted a great answer, to which I added a few enhancements. This is just a slightly spiffed up version of his very nice code.
As Mike pointed out, this class file must be in the root of the project.
I did not run into any problems with the below, but perhaps there are nuances I'm not aware of. YMMV.
using System.IO;
using System.Runtime.CompilerServices;
namespace Whatever
{
internal static class ProjectPathInfo
{
public static string CSharpClassFileName = nameof(ProjectPathInfo) + ".cs";
public static string CSharpClassPath;
public static string ProjectPath;
public static string SolutionPath;
static ProjectPathInfo() {
CSharpClassPath = GetSourceFilePathName();
ProjectPath = Directory.GetParent(CSharpClassPath)!.FullName;
SolutionPath = Directory.GetParent(ProjectPath)!.FullName;
}
private static string GetSourceFilePathName( [CallerFilePath] string? callerFilePath = null ) => callerFilePath ?? "";
}
}
Ok, 2021, a bit late to the party... but very annoyed by all possibilities I found in many projects:
bin/Debug
bin/x86/Debug
bin/Debug/net5.0-windows
...
Come on... I just need a one-liner (or almost) to address some files in test units; I need to use it on all past, current, (maybe future) projects.
So, if the project name is the same of relative folder which it lies in:
use the assembly name to pick project root folder name;
go back until that name is found.
Code sample:
string appName = Assembly.GetExecutingAssembly().GetName().Name;
var dir = new DirectoryInfo(Environment.CurrentDirectory);
while (dir.Name != appName) {
dir = Directory.GetParent(dir.FullName);
}
return dir.FullName;
The best solution
string PjFolder1 =
Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory).
Parent.Parent.FullName;
Other solution
string pjFolder2 = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)));
Test it, AppDomain.CurrentDomain.BaseDirectory worked for me on past project, now I get debug folder .... the selected GOOD answer just NOT WORK!.
//Project DEBUG folder, but STILL PROJECT FOLDER
string pjDebugFolder = AppDomain.CurrentDomain.BaseDirectory;
//Visual studio folder, NOT PROJECT FOLDER
//This solutions just not work
string vsFolder = Directory.GetCurrentDirectory();
string vsFolder2 = Environment.CurrentDirectory;
string vsFolder3 = Path.GetFullPath(".\\");
//Current PROJECT FOLDER
string ProjectFolder =
//Get Debug Folder object from BaseDirectory ( the same with end slash)
Directory.GetParent(pjDebugFolder).
Parent.//Bin Folder object
Parent. //Project Folder object
FullName;//Project Folder complete path
This works on VS2017 w/ SDK Core MSBuild configurations.
You need to NuGet in the EnvDTE / EnvDTE80 packages.
Do not use COM or interop. anything.... garbage!!
internal class Program {
private static readonly DTE2 _dte2;
// Static Constructor
static Program() {
_dte2 = (DTE2)Marshal.GetActiveObject("VisualStudio.DTE.15.0");
}
private static void FindProjectsIn(ProjectItem item, List<Project> results) {
if (item.Object is Project) {
var proj = (Project) item.Object;
if (new Guid(proj.Kind) != new Guid(Constants.vsProjectItemKindPhysicalFolder))
results.Add((Project) item.Object);
else
foreach (ProjectItem innerItem in proj.ProjectItems)
FindProjectsIn(innerItem, results);
}
if (item.ProjectItems != null)
foreach (ProjectItem innerItem in item.ProjectItems)
FindProjectsIn(innerItem, results);
}
private static void FindProjectsIn(UIHierarchyItem item, List<Project> results) {
if (item.Object is Project) {
var proj = (Project) item.Object;
if (new Guid(proj.Kind) != new Guid(Constants.vsProjectItemKindPhysicalFolder))
results.Add((Project) item.Object);
else
foreach (ProjectItem innerItem in proj.ProjectItems)
FindProjectsIn(innerItem, results);
}
foreach (UIHierarchyItem innerItem in item.UIHierarchyItems)
FindProjectsIn(innerItem, results);
}
private static IEnumerable<Project> GetEnvDTEProjectsInSolution() {
var ret = new List<Project>();
var hierarchy = _dte2.ToolWindows.SolutionExplorer;
foreach (UIHierarchyItem innerItem in hierarchy.UIHierarchyItems)
FindProjectsIn(innerItem, ret);
return ret;
}
private static void Main() {
var projects = GetEnvDTEProjectsInSolution();
var solutiondir = Path.GetDirectoryName(_dte2.Solution.FullName);
// TODO
...
var project = projects.FirstOrDefault(p => p.Name == <current project>);
Console.WriteLine(project.FullName);
}
}
I didn't see a solution by using string.Join and string.Split + SkipLast 4 elements, so here it is.
string projectDir =
string.Join('/', AppDomain.CurrentDomain.BaseDirectory
.Split(new char[] { '/' })
.SkipLast(4));
/* /home/freephoenix888/Programming/csharpProject/bin/Debug/net7.0/csharpProject */
Console.WriteLine(Environment.CurrentDirectory);
/* /home/freephoenix888/Programming/csharpProject/ */
Console.WriteLine(Directory.GetParent(Environment.CurrentDirectory).Parent.Parent.Parent.FullName);
Try:
{
OpenFileDialog fd = new OpenFileDialog();
fd.Multiselect = false;
fd.Filter = "Image files (*.bmp, *.jpg)|*.bmp;*.jpg|All files (*.*)|*.*";
if (fd.ShowDialog() == true)
{
if (fd.CheckFileExists)
{
var fileNameToSave = GetTimestamp(DateTime.Now) + Path.GetExtension(fd.FileName);
var pathRegex = new Regex(#"\\bin(\\x86|\\x64)?\\(Debug|Release)$", RegexOptions.Compiled);
var directory = pathRegex.Replace(Directory.GetCurrentDirectory(), String.Empty);
var imagePath = Path.Combine(directory + #"\Uploads\" + fileNameToSave);
File.Copy(fd.FileName, imagePath);
}
}
}
catch (Exception ex)
{
throw ex;
}
this is the code for uploading image into wpf upload directory
Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.Parent.Parent.FullName
Will give you the project directory.

Categories

Resources