I wonder if you can help me with this one. I have looked on Google but found nothing.
I have a program, that once it is finished comparing 2 files together, it writes out all the differences to a text file. I have 2 radio buttons, one to open in Notepad and the other to open in PFE (Programmers File Editor).
My PFE.exe is in "C:\Program Files (x86)\PFE\PFE.exe" and Notepad is where it normally is by default.
My code is:
using System.Diagnostics;
...
if (radioButton1.Checked)
{
Process.Start("notepad.exe", File1.Text);
}
if (radioButton2.Checked)
{
Process.Start("PFE32.exe", File1.Text);
}
Now, just "Process.Start("notepad.exe", File1.Text);" works fine, without the if statements.
So, therefore, my question is - Can you help me figure out why PFE won't open with the text file?
Thank you guys!
PFE32.exe is not found, because it is not in any of the directories declared in the PATH environment variable.
You need to either add C:\Program Files (x86)\PFE to the path variable or call PFE32.exe with the full path.
The second parameter is arguments to the command, notepad doesn't need an argument name, just the file name to work.
Perhaps PFE takes a named argument like: pfe32.exe -path:C:\myfile.txt
Related
As part of my c# learning, I have a simple game on a console application. I have now added a text file to record the top score. It has 4 lines, date, number of attempts, time in minutes and additional time in seconds. The text file is part of the project solution, and although I don't specify the full path, I can read the file with StreamReader and output the lines without any trouble. But if I have a new top score, and I wish to overwrite the values in the text file, nothing is happening. The text file remains unmodified.
I have tried using StreamWriter. It feels somewhat intuitive to use StreamReader ReadLine to bring the data into the application and StreamReader WriteLine to output. But the output piece is not working. For my sins, this is my code. There are some similar queries on stackooerflow, but I've not found a solution that appears to fit my query.
if (newBestScore== true)
{
DateTime dateToday = DateTime.Now;
string dateString=dateToday.ToShortDateString();
string currAttempts=CurrentAttempts.ToString();
using (StreamWriter newTopScore = new StreamWriter("TopScore.txt"))
{
newTopScore.WriteLine(dateString);
newTopScore.WriteLine(currAttempts);
newTopScore.WriteLine(timeTaken.Minutes);
newTopScore.WriteLine(timeTaken.Seconds);
newTopScore.Close();
}
}
I'll not fix your problem, but I'll tell you how you can fix all problems of this sort by yourself in the future.
Download Process Monitor and add a filter for your application, like so:
Process Name, contains, <myapp>.exe then Include and then add this filter by pressing the Add button.
Then start recording and look for TopScore.txt and see in which directory it looks for that file. You can use the find dialog for that (Ctrl+F):
Then have a look at the full file name of that file, so you know where it it being written. You can even right click and choose "Jump to ...".
Understand what a working directory is. The file will be written in the working directory if you didn't provide a full path.
Why go this way? This way proves that you have a problem independently. You don't change the source code. Whenever you change the source code and e.g. add diagnostics code like Console.WriteLine(Path.GetFullPath(...));, problems may disappear.
I've created a .NetCore console application in which am trying to check if a file exists using its absolute path, but am facing issues, I always get a false response even though the file exists. Even though I pass absolute path as parameter to API, it always prefixes the current working directory, so the path gets evaluated as doesn't exists.
I'm running this code on a windows 10 desktop and the application is created using .NetCore 2.1. I've tried various different methods to evaluate the existence of file like FileInfo Class instance and File.Exists static method. They've failed so far. I've diagnosed the issue, but I couldn't find a way to fix it.
using System;
using System.IO;
namespace FileAccess
{
class Program
{
static void Main(string[] args)
{
FileInfo fileInfo = new FileInfo(#"D:\ScriptData\test.zip");
Console.WriteLine($"Full Name: {fileInfo.FullName}");
Console.WriteLine($"FileInfo.Exists: {fileInfo.Exists}");
Console.Write($"File.Exists with #: {File.Exists(#"D:\ScriptData\test.zip")}")
Console.ReadLine();
}
}
}
The output of the code is:
Full Name: D:\Work\Samples\FileAccess\FileAccess\bin\Debug\netcoreapp2.1\?D:\ScriptData\test.zip
False
False
Even though am passing the absolute path, it prefixes the current working directory to the path I've passed. I've checked the Access to the file, its all fine, still I get false as response for both the cases.
Screenshot of Error
Screenshot of Debug Info
Judging your screen shot and the output, there is an invisible character at the start of the file path. That will cause .NET not to recognize it is an absolute path and automatically it will make it an absolute path itself.
If you use this code, you will notice that the inserted ? causes the problem here:
System.IO.FileInfo fi = new System.IO.FileInfo(#"?D:\some_file.ext");
Which outputs: C:\Users\...\ConsoleApp8\bin\Debug\netcoreapp2.2\?D:\some_file.ext.
Instead of:
System.IO.FileInfo fi = new System.IO.FileInfo(#"D:\some_file.ext");
Which outputs: D:\some_file.ext.
If you put your code in a HEX editor, you will see there is indeed a character before D:.
Thank goodness you cut and paste your original code! I know you did because when I cut and paste your code I can see that you have invisible characters after the open quote and before the D:\.
These two lines look identical but they're not! Cut and paste them if you don't believe me!
Your code:
FileInfo fileInfo = new FileInfo(#"D:\ScriptData\test.zip");
Fixed code:
FileInfo fileInfo = new FileInfo(#"D:\ScriptData\test.zip");
Here's what the binary editor shows.
You've got E2 80 AA secretly stuck in your source code file at the beginning of your filename. Which happens to be the UTF-8 representation of the LEFT-TO-RIGHT EMBEDDING character.
I am trying to write out a text file to: C:\Test folder\output\, but without putting C:\ in.
i.e.
This is what I have at the moment, which currently works, but has the C:\ in the beginning.
StreamWriter sw = new StreamWriter(#"C:\Test folder\output\test.txt");
I really want to write the file to the output folder, but with out having to have C:\ in the front.
I have tried the following, but my program just hangs (doesn't write the file out):
(#"\\Test folder\output\test.txt");
(#".\Test folder\output\test.txt");
("//Test folder//output//test.txt");
("./Test folder//output//test.txt");
Is there anyway I could do this?
Thanks.
Thanks for helping guys.
A colleague of mine chipped in and helped as well, but #Kami helped a lot too.
It is now working when I have:
string path = string.Concat(Environment.CurrentDirectory, #"\Output\test.txt");
As he said: "The CurrentDirectory is where the program is run from.
I understand that you would want to write data to a specified folder. The first method is to specify the folder in code or through configuration.
If you need to write to specific drive or current drive you can do the following
string driveLetter = Path.GetPathRoot(Environment.CurrentDirectory);
string path = diveLetter + #"Test folder\output\test.txt";
StreamWriter sw = new StreamWriter(path);
If the directory needs to be relative to the current application directory, then user AppDomain.CurrentDomain.BaseDirectory to get the current directory and use ../ combination to navigate to the required folder.
You can use System.IO.Path.GetDirectoryName to get the directory of your running application and then you can add to this the rest of the path..
I don't get clearly what you want from this question , hope this get it..
A common technique is to make the directory relative to your exe's runtime directory, e.g., a sub-directory, like this:
string exeRuntimeDirectory =
System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location);
string subDirectory =
System.IO.Path.Combine(exeRuntimeDirectory, "Output");
if (!System.IO.Directory.Exists(subDirectory))
{
// Output directory does not exist, so create it.
System.IO.Directory.CreateDirectory(subDirectory);
}
This means wherever the exe is installed to, it will create an "Output" sub-directory, which it can then write files to.
It also has the advantage of keeping the exe and its output files together in one location, and not scattered all over the place.
I have a batch file with following content:
echo %~dp0
CD Arvind
echo %~dp0
Even after changing directory value of %~dp0 is the same.
However, if I run this batch file from CSharp program, the value of %~dp0 changes after CD. It now points to new directory. Following is the code that I use:
Directory.SetCurrentDirectory(//Dir where batch file resides);
ProcessStartInfo ProcessInfo;
Process process = new Process();
ProcessInfo = new ProcessStartInfo("mybatfile.bat");
ProcessInfo.UseShellExecute = false;
ProcessInfo.RedirectStandardOutput = true;
process = Process.Start(ProcessInfo);
process.WaitForExit();
ExitCode = process.ExitCode;
process.Close();
Why is there a difference in output on executing same script by different ways?
Do I miss something here?
This question started the discussion on this point, and some testing was done to determine why. So, after some debugging inside cmd.exe ... (this is for a 32 bit Windows XP cmd.exe but as the behaviour is consistent on newer system versions, probably the same or similar code is used)
Inside Jeb's answer is stated
It's a problem with the quotes and %~0.
cmd.exe handles %~0 in a special way
and here Jeb is correct.
Inside the current context of the running batch file there is a reference to the current batch file, a "variable" containing the full path and file name of the running batch file.
When a variable is accessed, its value is retrieved from a list of available variables but if the variable requested is %0, and some modifier has been requested (~ is used) then the data in the running batch reference "variable" is used.
But the usage of ~ has another effect in the variables. If the value is quoted, quotes are removed. And here there is a bug in the code. It is coded something like (here simplified assembler to pseudocode)
value = varList[varName]
if (value && value[0] == quote ){
value = unquote(value)
} else if (varName == '0') {
value = batchFullName
}
And yes, this means that when the batch file is quoted, the first part of the if is executed and the full reference to the batch file is not used, instead the value retrieved is the string used to reference the batch file when calling it.
What happens then? If when the batch file was called the full path was used, then there will be no problem. But if the full path is not used in the call, any element in the path not present in the batch call needs to be retrieved. This retrieval assumes relative paths.
A simple batch file (test.cmd)
#echo off
echo %~f0
When called using test (no extension, no quotes), we obtain c:\somewhere\test.cmd
When called using "test" (no extension, quotes), we obtain c:\somewhere\test
In the first case, without quotes, the correct internal value is used. In the second case, as the call is quoted, the string used to call the batch file ("test") is unquoted and used. As we are requesting a full path, it is considered a relative reference to something called test.
This is the why. How to solve?
From the C# code
Don't use quotes : cmd /c batchfile.cmd
If quotes are needed, use the full path in the call to the batch file. That way %0 contains all the needed information.
From the batch file
Batch file can be invoked in any way from any place. The only reliable way to retrieve the information of the current batch file is to use a subroutine. If any modifier (~) is used, the %0 will use the internal "variable" to obtain the data.
#echo off
setlocal enableextensions disabledelayedexpansion
call :getCurrentBatch batch
echo %batch%
exit /b
:getCurrentBatch variableName
set "%~1=%~f0"
goto :eof
This will echo to console the full path to the current batch file independtly of how you call the file, with or without quotes.
note: Why does it work? Why the %~f0 reference inside a subroutine return a different value? The data accessed from inside the subroutine is not the same. When the call is executed, a new batch file context is created in memory, and the internal "variable" is used to initialize this context.
I'll try to explain why this behaves so oddly. A rather technical and long-winded story, I'll try to keep it condense. Starting point for this problem is:
ProcessInfo.UseShellExecute = false;
You'll see that if you omit this statement or assign true that it works as you expected.
Windows provides two basic ways to start programs, ShellExecuteEx() and CreateProcess(). The UseShellExecute property selects between those two. The former is the "smart and friendly" version, it knows a lot about the way the shell works for example. Which is why you can, say, pass the path to an arbitrary file like "foo.doc". It knows how to look up the file association for .doc files and find the .exe file that knows how to open foo.doc.
CreateProcess() is the low-level winapi function, there's very little glue between it and the native kernel function (NtCreateProcess). Note the first two arguments of the function, lpApplicationName and lpCommandLine, you can easily match them to the two ProcessStartInfo properties.
What is not so visible that CreateProcess() provides two distinct ways to start a program. The first one is where you leave lpApplicationName set to an empty string and use lpCommandLine to provide the entire command line. That makes CreateProcess friendly, it automatically expands the application name to the full path after it has located the executable. So, for example, "cmd.exe" gets expanded to "c:\windows\system32\cmd.exe". But it does not do this when you use the lpApplicationName argument, it passes the string as-is.
This quirk has an effect on programs that depend on the exact way the command line is specified. Particularly so for C programs, they assume that argv[0] contains the path to their executable file. And it has an effect on %~dp0, it too uses that argument. And flounders in your case since the path it works with is just "mybatfile.bat" instead of, say, "c:\temp\mybatfile.bat". Which makes it return the current directory instead of "c:\temp".
So what your are supposed to do, and this is totally under-documented in the .NET Framework documentation, is that it is now up to you to pass the full path name to the file. So the proper code should look like:
string path = #"c:\temp"; // Dir where batch file resides
Directory.SetCurrentDirectory(path);
string batfile = System.IO.Path.Combine(path, "mybatfile.bat");
ProcessStartInfo = new ProcessStartInfo(batfile);
And you'll see that %~dp0 now expands as you expected. It is using path instead of the current directory.
Joey's suggestion helped.
Just by replacing
ProcessInfo = new ProcessStartInfo("mybatfile.bat");
with
ProcessInfo = new ProcessStartInfo("cmd", "/c " + "mybatfile.bat");
did the trick.
It's a problem with the quotes and %~0.
cmd.exe handles %~0 in a special way (other than %~1).
It checks if %0 is a relative filename, then it prepend it with the start directory.
If there a file can be found it will use this combination, else it prepends it with the actual directory.
But when the name begins with a quote it seems to fail to remove the quotes, before prepending the directory.
That's the cause why cmd /c myBatch.bat works, as then myBatch.bat is called without quotes.
You could also start the batch with a full qualified path, then it also works.
Or you save the full path in your batch, before changing the directory.
A small test.bat can demonstrate the problems of cmd.exe
#echo off
setlocal
echo %~fx0 %~fx1
cd ..
echo %~fx0 %~fx1
Call it via (in C:\temp)
test test
The output should be
C:\temp\test.bat C:\temp\test
C:\temp\test.bat C:\test
So, cmd.exe was able to find test.bat, but only for %~fx0 it will prepend the start directory.
In the case of calling it via
"test" "test"
It fails with
C:\temp\test C:\temp\test
C:\test C:\test
cmd.exe isn't able to find the batch file even before the directory was changed, it can't expand the name to the full name of c:\temp\test.bat
EDIT: FixIt, retrieve the fullname even when %~0 has quotes
There exists a workaround with a function call.
#echo off
echo This can be wrong %~f0
call :getCorrectName
exit /b
:getCorrectName
echo Here the value is correct %~f0
exit /b
Command line interpreter cmd.exe has a bug in code on getting path of batch file if the batch file was called with double quotes and with a path relative to current working directory.
Create a directory C:\Temp\TestDir. Create inside this directory a file with name PathTest.bat and copy & paste into this batch file the following code:
#echo off
set "StartIn=%CD%"
set "BatchPath=%~dp0"
echo Batch path before changing working directory is: %~dp0
cd ..
echo Batch path after changing working directory is: %~dp0
echo Saved path after changing working directory is: %BatchPath%
cd "%StartIn%"
echo Batch path after restoring working directory is: %~dp0
Next open a command prompt window and set working directory to C:\Temp\TestDir using the command:
cd /D C:\Temp\TestDir
Now call Test.bat in following ways:
PathTest
PathTest.bat
.\PathTest
.\PathTest.bat
..\TestDir\PathTest
..\TestDir\PathTest.bat
\Temp\TestDir\PathTest
\Temp\TestDir\PathTest.bat
C:\Temp\TestDir\PathTest
C:\Temp\TestDir\PathTest.bat
Output is four times C:\Temp\TestDir\ as expected for all 10 test cases.
The test cases 7 and 8 start the batch file with a path relative to root directory of current drive.
Now let us look on results on doing the same as before, but with using double quotes around batch file name.
"PathTest"
"PathTest.bat"
".\PathTest"
".\PathTest.bat"
"..\TestDir\PathTest"
"..\TestDir\PathTest.bat"
"\Temp\TestDir\PathTest"
"\Temp\TestDir\PathTest.bat"
"C:\Temp\TestDir\PathTest"
"C:\Temp\TestDir\PathTest.bat"
Output is four times C:\Temp\TestDir\ as expected for the test cases 5 to 10.
But for the test cases 1 to 4 the second output line is just C:\Temp\ instead of C:\Temp\TestDir\.
Now use cd .. to change working directory to C:\Temp and run PathTest.bat as follows:
"TestDir\PathTest.bat"
".\TestDir\PathTest.bat"
"\Temp\TestDir\PathTest.bat"
"C:\Temp\TestDir\PathTest.bat"
The result for second output for the test cases 1 and 2 is C:\TestDir\ which does not exist at all.
Starting the batch file without the double quotes produces for all 4 test cases the right output.
This makes it very clear that the behavior is caused by a bug.
Whenever a batch file is started with double quotes and with a path relative to current working directory on start, %~dp0 is not reliable on getting the path of batch file when current working directory is changed during batch execution.
This bug is also reported to Microsoft according to Windows shell bug with how %~dp0 is resolved.
It is possible to workaround this bug by assigning the path of the batch file immediately to an environment variable as demonstrated with the code above before changing the working directory.
And then reference the value of this variable wherever path of batch file is needed with using double quotes where required. Something like %BatchPath% is always better readable as %~dp0.
Another workaround is starting the batch file always with full path (and with file extension) on using double quotes as class Process does.
Each new line in your batch called by your ProcessStart is independently considered as a new cmd command.
For example, if you give it a try like this:
echo %~dp0 && CD Arvind && echo %~dp0
It works.
I have a program that "greps" out various directory paths from a log text file and prints various results according to the word.
Examples of Directory paths:
C:/Documents and Settings/All Users/Desktop/AccessData FTK Imager.lnk
C:/Documents and Settings/All Users/Start Menu/Programs/AccessData
C:/Documents and Settings/Administrator/Desktop/AccessData FTK Imager.exe:Zone.Identifier
Therefore how can I grep out the file or folder name after the last "/"? This is to help the program to identify between files and folder. Please do take note of the multiple "." and white spaces found within a directory paths. etc "Imager.exe:Zone.Identifier". Therefore it is difficult to use if(!name.contains()".")
Etc. How to get the "AccessData FTK Imager.lnk" or "AccessData" or "AccessData FTK Imager.exe:Zone.Identifier" from the path STRING?!
May someone please advise on the methods or codes to solve this problem? Thanks!
The codes:
if (!token[7].Contains("."))
{
Console.WriteLine("The path is a folder?");
Console.WriteLine(token[7]);
Console.WriteLine(actions);
MacActions(actions);
x = 1;
}
Use the Path class when working with file paths, and use the File and Directory class when working with actual files and folders.
string str1=#"C:/Documents and Settings/All Users/Desktop/AccessData FTK Imager.lnk";
string str2=#"C:/Documents and Settings/All Users/Start Menu/Programs/AccessData";
string str3=#"C:/Documents and Settings/Administrator/Desktop/AccessData FTK Imager.exe:Zone.Identifier";
Console.WriteLine(Path.GetFileName(str1));
Console.WriteLine(Path.GetFileName(str2));
Console.WriteLine(Path.GetFileName(str3));
outputs:
AccessData FTK Imager.lnk
AccessData
Zone.Identifier <-- it chokes here because of the :
This class operates on strings, as I do not have those particular files and/or folders on my system. Also it's impossible to determine whether AccessData is meant to be a folder or a file without an extension.
I could use some common sense and declare everything with an extension to be a file (Path.GetFileExtension can be used here) and everything else to be a folder.
Or I could just check it the string in question is indeed a file or a folder on my machine using (File.Exists and Directory.Exists respectively).
if (File.Exists(str2))
Console.WriteLine("It's a file");
else if (Directory.Exists(str2))
Console.WriteLine("It's a folder");
else
Console.WriteLine("It's not a real file or folder");
Use Path.GetFileName.
The characters after the last directory character in path. If the last character of path is a directory or volume separator character, this method returns String.Empty.
This is to help the program to identify between files and folder
There is no way to determine is a path represents a file or folder, unless you access the actual file system. A directory name like 'Foo.exe' would be perfectly valid, and a file with no extension ('Foobar') would be valid too.
how about tokenized it with "/" like what you're doing ... and then you'll know that the last token is the file, and whatever before it is the path.
You can simply split the whole string by /
e.g.:
string a="C:/Documents and Settings/All Users/Desktop/AccessData FTK Imager.lnk";
string[] words=a.split('/');
int len=words.length;
so now words[len] returns the data after last slash(/)..
I hope you understand...
I guess you only have a string that represents the name of the file, if that is the case you can't really be sure. It's totally ok to have a folder namen something like Folder.doc. So if you don't have access to the actual file system it is hard to check. You can get close though using regular expression like:
(.*\\)(.+)(\..*)
Try it on: http://www.regexplanet.com/simple/index.html
If you get any output in group number 3 it's likely that it is a file and not a folder. If you don't get some output try this direct after:
(.*\\)(.+)(\..*)?
That will give you the folder in group 2.