How do I store the $(TargetDir) in a variable - c#

I'm trying to store the Target Directory in a variable, However when I'm doing path.Combine it just ignores my parameters.
this._outputPath = "$(TargetDir)../../"
block.Name = "/Contracts/TestDTO";
var filePath = Path.Combine(this._outputPath, block.Name);
When I try this it ignores the output path probably because its not resolving properly.
As a Note this is being run from a T4 Generator meaning the application current directory is not the same as what I want.
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE"

So It seems path.combine does not like to work with mixed slashes.
I fixed this by removing the / from the second path
this._outputPath = "$(TargetDir)../../"
block.Name = "Contracts/TestDTO";
var filePath = Path.Combine(this._outputPath, block.Name);

Related

C# - Determine file path

I am using the following code to load a custom cursor for my program:
public static Cursor LoadCustomCursor(string path)
{
IntPtr hCurs = LoadCursorFromFile(path);
if (hCurs == IntPtr.Zero) throw new Win32Exception(-2147467259, "Key game file missing. Please try re-installing the game to fix this error.");
Cursor curs = new Cursor(hCurs);
// Note: force the cursor to own the handle so it gets released properly.
FieldInfo fi = typeof(Cursor).GetField("ownHandle", BindingFlags.NonPublic | BindingFlags.Instance);
fi.SetValue(curs, true);
return curs;
}
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern IntPtr LoadCursorFromFile(string path);
And then:
Cursor gameCursor = NativeMethods.LoadCustomCursor(#"C:/Users/User/Documents/Visual Studio 2015/Projects/myProj/myProj/Content/Graphics/Cursors/cursor_0.xnb");
Form gameForm = (Form)Control.FromHandle(Window.Handle);
I am wondering if this absolute path above will also work if the project folder is for example moved to D:/ partition or another folder on C:/? Can I do something like this:
string myStr = Assembly.GetExecutingAssembly().Location;
string output = myStr.Replace("bin\\Windows\\Debug\\myProj.exe", "Content\\Graphics\\Cursors\\cursor_0.xnb");
And use output for the path of the cursor file? Is Assembly.GetExecutingAssembly().Location dynamic (changes every time the program folder is moved)? Or is it always the same as when the project was built?
You can use Environment.CurrentDirectory. Example:
string cursorPath = #"Content\Graphics\Cursors\cursor_0.xnb";
string output = Path.Combine(Environment.CurrentDirectory, cursorPath);
Environment.CurrentDirectory returns the full path of the current working directory and you can use # to escape the literal \ all at once if you place it in front of the string.
This approach is not the best way.
Better to use relative paths, not absolute.
You can use ".." to move one folder up from your current location.
For example
var output = #"..\..\..\Content\Graphics\Cursors\cursor_0.xnb";
Is equals to
string myStr = System.Reflection.Assembly.GetExecutingAssembly().Location;
string output = myStr.Replace("bin\\Windows\\Debug\\myProj.exe", "Content\\Graphics\\Cursors\\cursor_0.xnb");
Your absolute path will only keep working if your cursor file lives at the exact path specified: #"C:/Users/User/Documents/Visual Studio 2015/Projects/myProj/myProj/Content/Graphics/Cursors/cursor_0.xnb". Relating the cursor file's location to your .EXE file is a better idea, but you'll need to maintain the relative folder structure you specify in code. Your code now depends on the .EXE living in directory <appRoot>\bin\Windows\Debug, which you probably won't want when deploying your application. (Instead you'll probably put the .EXE in the application's root folder, with resource files going into subdirectories.) For such a structure, you could write (code was never compiled and may therefore contain typos or other errors):
var exe = Assembly.GetExecutingAssembly().Location;
var exeFolder = System.IO.Path.GetDirectoryName(exe);
var cursorFile = System.IO.Path.Combine(exeFolder, "relative/path/to/your.cur";
(For added benefit, this code will keep working when the .EXE file is renamed.)
With this approach, you'll only need to ensure that the cursor file will be found in a particular location relative to your .EXE. Of course the same structure will need to exist both on your dev box and on target machines. Use MSBuild tasks in Visual Studio to copy your resource files to $(OutDir)\relative\path\to. For deployment to other machines, simply copy+paste the contents of your output folder, or create an installer that deploys files to the required folder structure.

Get directory name from full directory path regardless of trailing slash

I need to get the directory name from its path regardless of any of having a trailing backslash. For example, user may input one of the following 2 strings and I need the name of logs directory:
"C:\Program Files (x86)\My Program\Logs"
"C:\Program Files (x86)\My Program\Logs\"
None of the following gives correct answer ("Logs"):
Path.GetDirectoryName(m_logsDir);
FileInfo(m_logsDir).Directory.Name;
They apparently analyze the path string and in the 1st example decide that Logs is a file while it's really a directory.
So it should check if the last word (Logs in our case) is really a directory; if yes, return it, if no (Logs might be a file too), return a parent directory. If would require dealing with the actual filesystem rather than analyzing the string itself.
Is there any standard function to do that?
new DirectoryInfo(m_logsDir).Name;
This may help
var result = System.IO.Directory.Exists(m_logsDir) ?
m_logsDir:
System.IO.Path.GetDirectoryName(m_logsDir);
For this we have a snippet of code along the lines of:
m_logsDir.HasFlag(FileAttribute.Directory); //.NET 4.0
or
(File.GetAttributes(m_logsDir) & FileAttributes.Directory) == FileAttributes.Directory; // Before .NET 4.0
Let me rephrase my answer, because you have two potential flaws by the distinguishing factors. If you do:
var additional = #"C:\Program Files (x86)\My Program\Logs\";
var path = Path.GetDirectoryName(additional);
Your output would be as intended, Logs. However, if you do:
var additional = #"C:\Program Files (x86)\My Program\Logs";
var path = Path.GetDirectoryName(additional);
Your output would be My Program, which causes a difference in output. I would either try to enforce the ending \ otherwise you may be able to do something such as this:
var additional = #"C:\Program Files (x86)\My Program\Logs";
var filter = additional.Split('\\');
var getLast = filter.Last(i => !string.IsNullOrEmpty(i));
Hopefully this helps.
Along the lines of the previous answer, you could enforce the trailing slash like this:
Path.GetDirectoryName(m_logsDir + "\");
Ugly but it seems to work - whether there's 0 or 1 slash at the end. The double-slash is treated like a single-slash by GetDirectoryName.

Visual studio project directory issue

I'm writing to a text folder which is in a folder within my project but I can't seem to get to it without writing the absolute complete path as it is on my computer which is fine on this computer but when I want to take it elsewhere I can't have that as the drives are different etc.
Here is a screenshot of the lines I'm using to get it to post to the directory on the right.
The file I'm trying to access is in a folder called AdminAccount and is called User.txt. it works fine as you can see from the commented directory link as a direct path but when I try with the directory string in use it does not work.
http://i.imgur.com/hAV55W0.png
Any help how to get around this? I tried all sorts, I tried doing
private string[] getLines = System.IO.File.ReadAllLines(#"\AdminAccount\User.txt");
private string[] getLines = System.IO.File.ReadAllLines(#"..\AdminAccount\User.txt");
No joy.
You can use,
string rootPath = Environment.CurrentDirectory;
string filePath = Path.Combine(rootPath,#"..\..\AdminAccount\User.txt");
private string[] getLines = System.IO.File.ReadAllLines(#filePath);
..\ is used to access a top level folder in the hierarchy. you can keep on adding ..\ to move up in the hierarchy.
Ex:
string path1 = #"C:\Users\Documents\Visual Studio 2010\Projects\Test\Test\bin\Debug"
string newPath = Path.Combine(path1, #"..\..\AdminAccount\User.txt");
new path would return
C:\Users\Documents\Visual Studio 2010\Projects\Test\Test\AdminAccount\User.txt
You just have to set the property "Copy to Output Directory" of the "User.txt" file to "Copy allways" or "Copy if newer".
Now you can read the lines as below
string[] getLines = File.ReadAllLines(
Path.Combine(Application.StartupPath, "AdminAccount", "User.txt"));

Ways to get the relative path of a folder

I have one folder named "Images" in my project. I want to get the path of the Image folder so that I can browse that and get the files.
I'm using below piece of code for my above requirement and it is working fine.
string basePath = AppDomain.CurrentDomain.BaseDirectory;
basePath = basePath.Replace("bin", "#");
string[] str = basePath.Split('#');
basePath = str[0];
string path = string.Format(#"{0}{1}", basePath, "Images");
string[] fileEntries = Directory.GetFiles(path);
foreach (string fileName in fileEntries)
listBox.Items.Add(fileName);
Just want to know like is there any elegant way of doing this? What are the best ways of getting the folder path?
This is what i usually use:
string appDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
Note that this returns the directory of the assembly that contains the currently executing code (i assume this is your main executable).
To get the parent directory of the resulting path, you can also use
Path.GetDirectoryName(appDirectory);
I would advice against depending on the Visual Studio project structure in your code, though. Consider adding the images folder as content to your application, so that it resides in a subdirectory in the same directory as your executable.
If you are just trying to reference a directory with a fixed relationship to another, then you can just use the same .. syntax you'd use at the command line?
You should also use the methods in the Path class (eg Path.Combine) rather than all that string manipulation.

Can Process.Start() take the system PATH into account?

I've been searching and experimenting for a while with this, but I have had no luck.
I am trying to make a console program to automate some tasks that I couldn't quite do with a BAT file. I want to call "signcode.exe" from the Windows SDK, the bin folder with all the tools in my system PATH, and I can call "signcode" from anywhere, but Process.Start is ignoring the path.
Current code:
System.Diagnostics.Process sign = new System.Diagnostics.Process();
sign.StartInfo.FileName = signCommand.Substring(0, signCommand.IndexOf(' ')); // signtool.exe
sign.StartInfo.Arguments = signCommand.Substring(signCommand.IndexOf(' ') + 1); // /sign /a file1 file2
// sign.StartInfo.EnvironmentVariables["Path"] = Environment.GetEnvironmentVariable("PATH"); // This doesn't work either
sign.StartInfo.UseShellExecute = false;
sign.StartInfo.RedirectStandardOutput = true;
sign.StartInfo.RedirectStandardError = true;
sign.Start(); // Throws Win32Exception - The system cannot find the file specified
I've confirmed that StartInfo.EnvironmentVariables["Path"] matches my system path, and contains the Windows SDK folder. Setting it manually doesn't work either.
I've even tried setting TempPath as shown on the MSDN page for EnvironmentVariables Property, but that didn't work either. I wonder why you would be able to set this if it has no effect.
If System.Diagnostics.Process cannot use the path, are there any other functions I could use? I'd like to see the output of the command in my console application as well.
Here is some additional debug values:
Console.WriteLine("Sign Filename = '{0}'", sign.StartInfo.FileName);
Sign Filename = 'signtool.exe'
Console.WriteLine("Sign Arguments = '{0}'", sign.StartInfo.Arguments);
Sign Arguments = '/sign /f C:\Visual Studio\Projects\MGInsight\MGInsight\APPARENTINC.pfx /t http://timestamp.comodoca.com/authenticode "C:\Visual Studio\Projects\MGInsight\MGInsight\Publish\Application Files\\MGInsight_0_9_1_85\MGInsight.exe" "C:\Visual Studio\Projects\MGInsight\MGInsight\Publish\Application Files\\MGInsight_0_9_1_85\XPXScanner.dll" "C:\Visual Studio\Projects\MGInsight\MGInsight\Publish\Application Files\\MGInsight_0_9_1_85\NetworkCalculations.dll"'
Console.WriteLine("Sign Path = '{0}'", sign.StartInfo.EnvironmentVariables["Path"]);
Sign Path = 'C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;"C:\Program Files\Intel\WiFi\bin\";"C:\Program Files\Common Files\Intel\WirelessCommon\";"C:\Program Files (x86)\cwRsync\bin";"C:\Program Files (x86)\Git\cmd";"C:\Program Files (x86)\Git\bin";"C:\Program Files (x86)\Zend\ZendServer\bin";"C:\Program Files (x86)\Zend\ZendServer\share\ZendFramework\bin";"C:\Program Files\Java\jre6\bin";"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\";"C:\Program Files\Microsoft Windows Performance Toolkit\";C:\MinGW\bin;"C:\Program Files (x86)\Microsoft\ILMerge";"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin";C:\Program Files (x86)\Nmap'
The path "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin" is where signtool.exe is, and I can run it from a command prompt by simply typing signtool, but if I run this application from the same prompt, it doesn't register that path.
Adding to mhutch's answer: It does indeed take PATH into consideration, but I have noticed you actually need to restart Visual Studio to pick up any path changes. It is kind of sneaky.
I'm pretty sure Process.Start does respect PATH.
Are you sure your signCommand value is correct?
Is the directory value in PATH specified using quotes? The docs mention that such values will not be respected.
Note that FileName can also be a full path to the executable.
If you updated PATH recently, be sure to restart Visual Studio. Environment variables are loaded at the launch of Visual Studio. Note that this applies to DEBUG mode execution.
Well, I guess the problem was related to what mhutch said, from the MSDN docs:
If you have a path variable declared in your system using quotes,
you must fully qualify that path when starting any process found
in that location. Otherwise, the system will not find the path. For
example, if c:\mypath is not in your path, and you add it using
quotation marks: path = %path%;"c:\mypath", you must fully qualify
any process in c:\mypath when starting it.
I saw that initially, but it seemed strange so I disregard it. Not sure why that's the case but it seems to be.
I tried copying signtool.exe to C:\sign\tool\bin and added that to my path, and then my code worked, so I guess because I have quotes in that path due to the spaces, I am SOL and will have to manually search the path for the windows SDK path unless there is some way to add something with spaces to the path without using quotes.
I believe you're looking for the ProcessStartInfo.WorkingDirectory property.
The StartInfo filename is actually the full path to the executable
For example, on a wrapper I have for x264, it looks like this:
x264Start.FileName = Directory.GetCurrentDirectory() + #"\Tools\x264\x264x86-r1995.exe";
I'd sense-check the code by adding a try {}, catch {} in there and writing the actual filename you are trying to call into the debug if you have an error.
Otherwise add something like the below:
if (File.exists(sign.FileName))
{
sign.Start();
}
else
{
Console.WriteLine("Can't find {0}", sign.FileName);
throw new Exception("File doesn't exist");
}
EDIT: Added a full example - this searches for CCleaner and then runs it with the "/AUTO" switch. Just tested and works fine.
// detect if 64-bit system
string programFiles = "";
if (Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE").Contains("64"))
{
Console.WriteLine("#info# x64 detected");
programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
}
else
{
Console.WriteLine("#info# x86 detected");
programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
}
// search
string[] dirs = Directory.GetDirectories(programFiles, "CCleaner", SearchOption.AllDirectories);
string[] exes = Directory.GetFiles(programFiles, "CCleaner64.exe", SearchOption.AllDirectories);
//debug only
foreach (string s in dirs)
{
Console.WriteLine(s);
}
foreach (string s in exes)
{
Console.WriteLine(s);
}
// access directly
ProcessStartInfo CCleaner = new ProcessStartInfo(exes[0], "/AUTO");
Process.Start(CCleaner);
Your code does seem to take the path into account for me.
It's hard to say what might be wrong in your case, but you might try running your command through cmd.exe:
sign.StartInfo.FileName = "cmd";
sign.StartInfo.Arguments = "/c signtool.exe ...";

Categories

Resources