Expand environment variable for My Documents - c#

I know I can read environment variables like this:
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
However, it would be really helpful to me if I could do something like this:
Environment.ExpandEnvironmentVariables(#"%MyDocuments%\Foo");
Is there an environement variable that equals SpecialFolder.MyDocuments?
I also tried to do something like this, but this doesn't lead to the expected result:
Environment.ExpandEnvironmentVariables(#"%USERPROFILE%\My Documents\Foo");
This way I ended up with something like #"C:\Users\<MyUser>\My Documents\Foo" but I what I need is #"\\someservername\users$\<MyUser>\My Documents\Foo".
EDIT: My Goal is NOT to hardcode either environment variable nor the part after that.
Any other suggestions?

No there is no environment variable for the MyDocuments special folder (the same is true for most members of the SpecialFolder enumeration).
Check out this snippet, it might be exactly what you are searching for.
It allows you to do something like that:
string fullPath = SpecialFolder.ExpandVariables(#"%MyDocuments%\Foo");
Note: SpecialFolder.ExpandVariables is a static method of a helper class introduced in the above snippet.

Is there an environment variable that equals SpecialFolder.MyDocuments?
Short answer: No.
Long answer:
Still no. You can type "set" into a Command Prompt to see all you current environment variables. I couldn't find any for my documents folder on my profile (tried on WinXP and Win7).
Also, expanding "%USERPROFILE%\My Documents" would be incorrect since the user's documents folder could be anywhere else (e.g., on my home PC I always change mine to D:\Documents).
If you really need to use environment variables, one solution might be to set the variable yourself:
// this environment variable is created for the current process only
string documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Environment.SetEnvironmentVariable("MYDOCUMENTS", documents);
Another solution might be to use a "fake" environment variable in the path and expand it yourself, something like:
string path = "%MYDOCUMENTS%\\Foo"; // read from config
// expand real env. vars
string expandedPath1 = Environment.ExpandEnvironmentVariables(path);
// expand our "fake" env. var
string documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string expandedPath2 = path.Replace("%MYDOCUMENTS%", documents);

What exactly are you trying to do? Is there any reason why you can't just use Path.Combine?
string docs = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string foo = Path.Combine(docs, "Foo");

I'm not sure if there is a good way to do this but instead of trying to do environment expansion to get the path why not use the Path.Combine API instead?
Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
"Foo");

You can expand environment variables using then Environment.GetEnvironmentVariable method. Given your comment, I would suggest breaking your path up into 2 separate config settings to make expanding it easier:
string variablePath = "%appdata%".Trim('%'); //read from some config setting
string appdataPath = Environment.GetEnvironmentVariable(variablePath);
string subdir = "foo"; //some other config setting
string myDir = Path.Combine(appdataPath, subdir);

No it does not exist. The easiest way to check is to run "set" from command line and see yourself.
Start-> run -> cmd
set
set |findstr /i documents

Related

Revit API: Use String as Button Assembly Name with Current Path

I'm not entirely sure at all why this is happening...
So I have a ExternalCommand and an application for making a ribbon tab and button. These two programs are in the same solution and under the same namespace, which allows me to have fewer files to deal with. When I create a button for my command, I want to put in the current path of the application that is currently running. I do this with Directory.GetCurrentDirectory() + \AddInsAll\Ribbon17.dll (where AddInsAll is the folder and Ribbon17 is the dll, obviously). I use # when necessary to avoid escape sequences. This string contains the exact assembly name needed, but Revit tells me "Assembly does not exist." If I replace this String variable with the hard coded C:\ProgramData\Autodesk\Revit\Addins\2017\AddInsAll\Ribbon17.dll it works. I want it obviously more robust than that. My code will be below, thanks in advance.
FYI: I have a TaskDialog showing when it first runs, and the fullPath that it returns is exacly the same as the hard coded path. I have to do a replace (Program Files to ProgramData) due to some weird bug with the get directory. Also, I add "\AddInsAll\Ribbon17.dll" to the end of the string because the CurrentDirectory goes only to Addins\2017. Finally, if you think the problem is due to the #'s, I have already tried putting it and taking it off of variables and none of the attempts work. But if you think of them is the problem, I welcome the advice. Thanks.
public class RibApp : IExternalApplication
{
public Result OnStartup(Autodesk.Revit.UI.UIControlledApplication application)
{
// Create a custom ribbon tab
String tabName = "Add-Ins";
String fakeFullPath = #Directory.GetCurrentDirectory() + #"\AddInsAll\Ribbon17.dll";
String fullPath = fakeFullPath.Replace(#"\Program Files\", #"\ProgramData\");
TaskDialog.Show("Hi", #fullPath);
application.CreateRibbonTab(tabName);
//Create buttons and panel
// Create two push buttons
PushButtonData CommandButton = new PushButtonData("Command17", "Command",
#fullPath, "Ribbon17.Command");
I suggest you skip the # and replace each backslash \ by a forward slash /.
KISS!
Better still, use an approach similar to the CreateRibbonTab implementation in the HoloLens Escape Path Waypoint JSON Exporter.

Environment is not being set in windows using c#. Where am I going wrong?

string path = System.Environment.GetEnvironmentVariable("Path");
Console.WriteLine(path);
if (!path.Contains("C:\ccstg"))
{
if (!path.EndsWith(";"))
path = path + ';';
var v=#"C:\ccstg;";
path = path + v;
Environment.SetEnvironmentVariable("Path",path);
Console.WriteLine(path);
Console.WriteLine("Path Set");
Console.ReadKey();
}
I am trying to set path environment variable using c#, I am able to get "Path" but while setting it is not being set. It doesn't show any error either. I have tried running it as administrator also , no help.
Does anybody what am I missing here ?
Firstly, you need to be a little more careful with your string literals, the code you posted won't compile because "\c" is not a valid string literal escape sequence. To fix:
string newPathComponent = #"C:\ccstg";
if (!path.Contains(newPathComponent))
{
if (!path.EndsWith(";"))
path = path + ';';
path = path + newPathComponent;
Environment.SetEnvironmentVariable("Path", path);
Now, this code works and sets the path for the duration of the process. If you want to set the path permanently, you need to use Environment.SetEnvironmentVariable Method (String, String, EnvironmentVariableTarget), for instance:
var target = EnvironmentVariableTarget.User; // Or EnvironmentVariableTarget.Machine
Environment.SetEnvironmentVariable("Path", path, target);
More here.
However, if you do that, you have to be careful to add your path component only to the path associated with that EnvironmentVariableTarget. That's because the %PATH% environment variable is actually combined from several sources. If you aren't careful, you may copy the combined path into just the EnvironmentVariableTarget.Machine or EnvironmentVariableTarget.User source -- which you do not want to do.
Thus:
static void AddToEnvironmentPath(string pathComponent, EnvironmentVariableTarget target)
{
string targetPath = System.Environment.GetEnvironmentVariable("Path", target) ?? string.Empty;
if (!string.IsNullOrEmpty(targetPath) && !targetPath.EndsWith(";"))
targetPath = targetPath + ';';
targetPath = targetPath + pathComponent;
Environment.SetEnvironmentVariable("Path", targetPath, target);
}
Finally, if you are running inside the Visual Studio Hosting Process for debugging, I have observed that if you use Environment.SetEnvironmentVariable("Path",path, EnvironmentVariableTarget.User), changes to the permanent environment will not be picked up until you exit and restart visual studio. Something weird to do with the visual studio hosting process, I reckon. To handle this odd scenario you might want to do both:
AddToEnvironmentPath(#"C:\ccstg", EnvironmentVariableTarget.User)
AddToEnvironmentPath(#"C:\ccstg", EnvironmentVariableTarget.Process)
SetEnvironmentVariable sets the variable for the current process. Your process has its own environment. When you set an environment variable in your program, it only affects your program's environment.
If you want to affect the user's environment, that is, make the change so that it can be seen outside your program, then you have to call this overload. For example:
Environment.SetEnvironmentVariable("Path", path, EnvironmentVariableTarget.User);
See EnvironmentVariableTarget enumeration for more details.

How to get Directory while running unit test

Hi when running my unit test I'm wanting to get the directory my project is running in to retrieve a file.
Say I have a Test project named MyProject. Test I run:
AppDomain.CurrentDomain.SetupInformation.ApplicationBase
and I receive "C:\\Source\\MyProject.Test\\bin\\Debug".
This is close to what I'm after. I don't want the bin\\Debug part.
Anyone know how instead I could get "C:\\Source\\MyProject.Test\\"?
I would do it differently.
I suggest making that file part of the solution/project. Then right-click -> Properties -> Copy To Output = Copy Always.
That file will then be copied to whatever your output directory is (e.g. C:\Source\MyProject.Test\bin\Debug).
Edit: Copy To Output = Copy if Newer is the better option
Usually you retrieve your solution directory (or project directory, depending on your solution structure) like this:
string solution_dir = Path.GetDirectoryName( Path.GetDirectoryName(
TestContext.CurrentContext.TestDirectory ) );
This will give you the parent directory of the "TestResults" folder created by testing projects.
Directory.GetParent(Directory.GetCurrentDirectory()).Parent.FullName;
This will give you the directory you need....
as
AppDomain.CurrentDomain.SetupInformation.ApplicationBase
gives nothing but
Directory.GetCurrentDirectory().
Have alook at this link
http://msdn.microsoft.com/en-us/library/system.appdomain.currentdomain.aspx
Further to #abhilash's comment.
This works in my EXE's, DLL's and when tested from a different UnitTest project in both Debug or Release modes:
var dirName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location.Replace("bin\\Debug", string.Empty));
/// <summary>
/// Testing various directory sources in a Unit Test project
/// </summary>
/// <remarks>
/// I want to mimic the web app's App_Data folder in a Unit Test project:
/// A) Using Copy to Output Directory on each data file
/// D) Without having to set Copy to Output Directory on each data file
/// </remarks>
[TestMethod]
public void UT_PathsExist()
{
// Gets bin\Release or bin\Debug depending on mode
string baseA = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
Console.WriteLine(string.Format("Dir A:{0}", baseA));
Assert.IsTrue(System.IO.Directory.Exists(baseA));
// Gets bin\Release or bin\Debug depending on mode
string baseB = AppDomain.CurrentDomain.BaseDirectory;
Console.WriteLine(string.Format("Dir B:{0}", baseB));
Assert.IsTrue(System.IO.Directory.Exists(baseB));
// Returns empty string (or exception if you use .ToString()
string baseC = (string)AppDomain.CurrentDomain.GetData("DataDirectory");
Console.WriteLine(string.Format("Dir C:{0}", baseC));
Assert.IsFalse(System.IO.Directory.Exists(baseC));
// Move up two levels
string baseD = System.IO.Directory.GetParent(baseA).Parent.FullName;
Console.WriteLine(string.Format("Dir D:{0}", baseD));
Assert.IsTrue(System.IO.Directory.Exists(baseD));
// You need to set the Copy to Output Directory on each data file
var appPathA = System.IO.Path.Combine(baseA, "App_Data");
Console.WriteLine(string.Format("Dir A/App_Data:{0}", appPathA));
// C:/solution/UnitTestProject/bin/Debug/App_Data
Assert.IsTrue(System.IO.Directory.Exists(appPathA));
// You can work with data files in the project directory's App_Data folder (or any other test data folder)
var appPathD = System.IO.Path.Combine(baseD, "App_Data");
Console.WriteLine(string.Format("Dir D/App_Data:{0}", appPathD));
// C:/solution/UnitTestProject/App_Data
Assert.IsTrue(System.IO.Directory.Exists(appPathD));
}
I normally do it like that, and then I just add "..\..\" to the path to get up to the directory I want.
So what you could do is this:
var path = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + #"..\..\";
For NUnit this is what I do:
// Get the executing directory of the tests
string dir = NUnit.Framework.TestContext.CurrentContext.TestDirectory;
// Infer the project directory from there...2 levels up (depending on project type - for asp.net omit the latter Parent for a single level up)
dir = System.IO.Directory.GetParent(dir).Parent.FullName;
If required you can from there navigate back down to other directories if required:
dir = Path.Combine(dir, "MySubDir");
According to https://github.com/nunit/nunit/issues/742#issuecomment-121964506
For NUnit3 , System.Environment.CurrentDirector is never changed, so it shall be the path of solution.
Eg:
string szProjectPath = System.Environment.CurrentDirectory + #"\where\your\project\is";
I prefer fixed location rather than GetParent().
One drawback of GetParent is when build is changed from AnyCPU to x86, default path would be changed from bin\Debug to bin\x86\Debug.
Need to get another parent, and it's pain in the neck.
Also, you may still access to you test assemblies at TestContext.CurrentContext.TestDirectory and get output from TestContext.CurrentContext.WorkDirectory
Edit:
Note: There are many changes in NUnit3. I will suggest reading through the documentation about "Breaking changes"
The best solution I found was to put the file as an embedded resource on the test project and get it from my unit test. With this solution I don´t need to care about file paths.
I'm not sure if this helps, but this looks to be briefly touched on in the following question.
Visual Studio Solution Path environment variable
In general you may use this, regardless if running a test or console app or web app:
// returns the absolute path of assembly, file://C:/.../MyAssembly.dll
var codeBase = Assembly.GetExecutingAssembly().CodeBase;
// returns the absolute path of assembly, i.e: C:\...\MyAssembly.dll
var location = Assembly.GetExecutingAssembly().Location;
If you are running NUnit, then:
// return the absolute path of directory, i.e. C:\...\
var testDirectory = TestContext.CurrentContext.TestDirectory;
My approach relies on getting the location of the unit testing assembly and then traversing upwards. In the following snippet the variable folderProjectLevel will give you the path to the Unit test project.
string pathAssembly = System.Reflection.Assembly.GetExecutingAssembly().Location;
string folderAssembly = System.IO.Path.GetDirectoryName(pathAssembly);
if (folderAssembly.EndsWith("\\") == false) {
folderAssembly = folderAssembly + "\\";
}
string folderProjectLevel = System.IO.Path.GetFullPath(folderAssembly + "..\\..\\");
You can do it like this:
using System.IO;
Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, #"..\..\"));
use StackTrace
internal static class Extensions
{
public static string GetSourceDirectoryName(this Type type)
{
StackTrace stackTrace = new StackTrace(true);
foreach (var frame in stackTrace.GetFrames())
{
if (frame.GetMethod() is { } method && method.DeclaringType == type)
{
return Path.GetDirectoryName(frame.GetFileName());
}
}
throw new Exception($"未找到{type.Name}源文件目录");
}
}

Notepad Path in VS2008

In my application, I have defined the following:
public static readonly string NOTEPAD = "%windir%\\notepad.exe";
I can type in the text value of NOTEPAD into the Run command on my Win7 machine, and Notepad will open.
However, from within my Visual Studio C# project, the Write Line routine will fire every time:
if (!File.Exists(NOTEPAD)) {
Console.WriteLine("File Not Found: " + NOTEPAD);
}
Does Visual Studio not understand %windir%?
Instead of expanding the variable manually as suggested by the other answers so far, you can have the Environment class do this for you just like the Run command does:
if (!File.Exists(Environment.ExpandEnvironmentVariables(NOTEPAD))) {
Console.WriteLine("File Not Found: " + NOTEPAD);
}
See http://msdn.microsoft.com/en-us/library/system.environment.expandenvironmentvariables.aspx
When looking on my windows XP box, the location of notepad is:
%SystemRoot%\system32\notepad.exe
Not:
%windir%\notepad.exe
You also need to make sure that these environment variables are resolved correctly - use Environment.GetEnvironmentVariable and Path.Combine to build up the correct path:
string root = Environment.GetEnvironmentVariable("SystemRoot");
string path = Path.Combine(root, "system32", "notepad.exe");
Just have a closer Look at the Class Environment. The Environment Variable is SystemRoot, so you can use
Environment.GetEnvironmentVariable("windir") (or something like that)
http://msdn.microsoft.com/en-us/library/system.environment.getenvironmentvariable.aspx
The console "Resolves" the %windir% environment variable to the correct path. You need to use the above function to do the same within your application.
Use Environment.GetEnvironmentVariable("windir");
So you could declare it like this:
public static readonly string NOTEPAD = Environment.GetEnvironmentVariable("windir") + "\\notepad.exe";

In Windows Vista and 7, I can't access the %DEFAULTUSERPROFILE% system variable - it shows as not found

If I try to access this system variable from the Run... dialog, Windows tells me the directory doesn't exist. Some system variables, like %SYSTEMROOT% and %USERPROFILE%, do work. Consequently, if I try to use a supposedly nonexistent variable like %DEFAULTUSERPROFILE% or %PROFILESFOLDER% in C#, I get nothing in return. Is there something special I need to do to get access to these variables?
Have you tried %ALLUSERSPROFILE%?
I need to point to
C:\Users\Default\AppData.
Are you sure? Be aware that this folder is used to populate the inital AppData directory for each new user added to the system.
If you want the actual shared application data directory in .NET, it's this:
String commonAppData = Environment.GetFolderPath(Environment.SpecialFolders.CommonApplicationData)
My suggestion is to retreive that value directly from the registry - in case you can't expand it:
public static string GetDefaultUserProfilePath() {
string path = System.Environment.GetEnvironmentVariable("DEFAULTUSERPROFILE") ?? string.Empty;
if (path.Length == 0) {
using (Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList")) {
path = (string)key.GetValue("Default", string.Empty);
}
}
return path;
}
You mention C# - you can't use environment variables inside C# path strings, you need to replace them using System.Environment.
System.Environment.GetEnvironmentalVariable("USERPROFILE");
I haven't seen %DefaultUserProfile% before - should it point to the first username that was installed?
Call SHGetFolderLocation with CSIDL_PROFILE and -1 as the token parameter

Categories

Resources