I have some issues with Environment.CurrentDirectory, it sometimes goes to the System32 folder. I looked online and found out why this happens and what alternatives I have (like Application.StartupPath and stuff like that) but the problem is the code is in a .dll that I am using and I can't edit it (or can I).
Is there anything I can do about this?
EDIT: In the duplicate issue the person writes their own dll. I don't own the dll I'm having problems with and I can't change it.
You can try to grab the path directly from the executable if CurrentDirectory is giving you an issue:
private void GetFilePath()
{
string filepath = string.Empty;
var processes = Process.GetProcessesByName("exe name");
foreach (var process in processes)
{
filepath = process.MainModule.FileName;
}
return filepath;
}
Related
I try to get the Full Path of a File. ie. calc
Input: calc
Expected output: C:\WINDOWS\system32\calc.exe
I could find out how to do it with PowerShell:
(Get-Command calc).Source
Or with CommandLine:
where.exe calc
But unfortunately I can not get it done with C#.
The documentation for Get-Command says:
Get-Command * gets all types of commands, including all of the non-PowerShell files in the Path environment variable ($env:Path), which it lists in the Application command type.
So we will need to get the Path environment variable and iterate over the directories it lists, looking for files with extensions that indicate the file is a program, for example "*.com" and "*.exe".
The problem with the Path environment variable is that it can become polluted with non-existent directories, so we will have to check for those.
The case of the filename and extension don't matter, so case-insensitive comparisons need to be made.
static void ShowPath(string progName)
{
var extensions = new List<string> { ".com", ".exe" };
string envPath = Environment.GetEnvironmentVariable("Path");
var dirs = envPath.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string d in dirs.Where(f => Directory.Exists(f)))
{
foreach (var f in (Directory.EnumerateFiles(d).
Where(thisFile => extensions.Any(h => Path.GetExtension(thisFile).Equals(h, StringComparison.InvariantCultureIgnoreCase)))))
{
if (Path.GetFileNameWithoutExtension(f).Equals(progName, StringComparison.InvariantCultureIgnoreCase))
{
Console.WriteLine(f);
return;
}
}
}
Console.WriteLine("Not found.");
}
static void Main(string[] args)
{
ShowPath("calc");
Console.ReadLine();
}
Output:
C:\WINDOWS\system32\calc.exe
There is always the possibility that the current user does not have permission to list the files from somewhere in the path, so checks should be added for that. Also, you might want to use StringComparison.CurrentCultureIgnoreCase for the comparison.
You can get the Pathenvironment variable, split it with ; as delimiter and loop over that result. Then, check if the file path + #"\" + name + ".exe" exists.
var findMe = "calc";
var pathes = Environment.GetEnvironmentVariable("Path").Split(';');
foreach (var path in pathes)
{
var testMe = $#"{path}\{findMe}.exe";
if (File.Exists(testMe))
{
Console.WriteLine(testMe);
}
}
This outputs :
C:\WINDOWS\system32\calc.exe
I do not know about any way of doing that exact thing from C# either. However the paths are usually well known and can be retreived via the SpecialFolders Enumeration:
using System;
using System.Diagnostics;
using System.IO;
namespace RunAsAdmin
{
class Program
{
static void Main(string[] args)
{
/*Note: Running a batch file (.bat) or similar script file as admin
Requires starting the interpreter as admin and handing it the file as Parameter
See documentation of Interpreting Programm for details */
//Just getting the Absolute Path for Notepad
string windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
string FullPath = Path.Combine(windir, #"system32\notepad.exe");
//The real work part
//This is the programm to run
ProcessStartInfo startInfo = new ProcessStartInfo(FullPath);
//This tells it should run Elevated
startInfo.Verb = "runas";
//And that gives the order
//From here on it should be 100% identical to the Run Dialog (Windows+R), except for the part with the Elevation
System.Diagnostics.Process.Start(startInfo);
}
}
}
I did not just use System (37) back then, as I wrote it when x32/x86 Systems were still a thing. You would need to check how it resolves nowadays.
Note that most of those paths are duplicated in the PATH System Variable, so you could look it up: https://www.architectryan.com/2018/03/17/add-to-the-path-on-windows-10/
Path Variables in turn go back to the old DOS days. Basically if you gave the Commandline a command/filename it would try the build-in commands, then Executables in the current working Directory (.bat, .com, .exe), and then go look over the path directories to again look for executeables. And only if all that failed, would it complain.
I finally tried to combine all three answers and came up with this:
I post it here in case someone has the same problem.
public static string[] GetPathOf(string cmd)
{
var list = new List<string>();
list.AddRange(Environment.GetEnvironmentVariable("path", EnvironmentVariableTarget.Machine).Split(';'));
list.AddRange(Environment.GetEnvironmentVariable("path", EnvironmentVariableTarget.Process).Split(';'));
list.AddRange(Environment.GetEnvironmentVariable("path", EnvironmentVariableTarget.User).Split(';'));
list = list.Distinct().Where(e=>Directory.Exists(e)).SelectMany(e=> new DirectoryInfo(e).GetFiles()).Where(e=>Regex.IsMatch(e.Name,"(?i)^"+cmd+"\\.(?:exe|cmd|com)")).Select(e=>e.FullName).ToList();
return list.ToArray();
}
private static string getPath(object id11)
{
string wmiQuery = string.Format("select CommandLine from Win32_Process where ProcessId={0}", id11);
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(wmiQuery))
{
using (ManagementObjectCollection retObjectCollection = searcher.Get())
{
foreach (ManagementObject retObject in retObjectCollection)
{
if (retObject["CommandLine"] != null)
{
string s= (string.Format("[{0}]", retObject["CommandLine"]));
string k = s.Substring(s.IndexOf("EXE")+4);
k = k.Remove(k.IndexOf("]"));
return k;
}
return null;
}
return null;
}
}
I use this code to get the notepad full path. This code is work fine when notepad file is open using double click. But When i open file inside notepad like (File->Open)... than this code not work to get a full path. Is there any way to find the path of file open like this. And one more thing i need file path not notepad Executable Path. Or suggest me some other solutions.
Your code looks at the command line arguments sent to a process. As you have rightly found, when you double click a file (.txt or .doc), it may be send as command line argument to the file. Your solution rightly finds the file in those cases.
But, when you open the file from the application, there is no command line argument.
One way is to use a tool like Handle to get the list of process which has your file open.
Sample screen shot:
You can use the Process class to run it and parse the output.
Certain processes (like notepad) will NOT lock the file. So, this tool will not give you the names of those files.
thanks because you has been come to this post.
I have an error with my script, that log says:
Access to the path 'C:\Windows\system32\Com\dmp' is denied.
I want to set my application to windows startup, so when that computer client started, my software is automatically running. So i put this script on my Main Load.
private void Main_Load(object sender, EventArgs e)
{
//Menjadikan software ke dalam Startup Windows, sehingga dapat berjalan ketika pc pertama kali dinyalakan
RegistryKey reg = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
reg.SetValue("PR Reminder", Application.ExecutablePath.ToString());
listMapel();
bool notif = Properties.Settings.Default.Notification;
if (notif == true)
{
checkExpired(); //Mengecek tanggal penyerahan dan pemberian
}
The result is work. But I'm getting an error like this.
After explored more deeply, the center of the issue is the method listMapel();
where he was tasked to search for files ending in .db in the local directory.
I dont know why this method is got error. When i try to remove this method, my application running fine when startup.
I think the problem is on system.io.
This is my listMapel(); method script
public void listMapel()
{
comboListMapel.Items.Clear();
string path = Directory.GetCurrentDirectory(); //Lokal direktori
string[] files = Directory.GetFiles(path, "*.db", SearchOption.AllDirectories);
foreach (string file in files)
{
nama = file.Split(".".ToCharArray()); //Hasil result yang ditampilkan Matapelajaran.db (Tapi dengan ini kita mengambil string sebelum .db
comboListMapel.Items.Add(Path.GetFileName(nama[0]));
}
}
You should always run as an administrator.
Hope this helps link
And another one
Give the access to your file like FileMode.Create, FileAccess.Write, FileShare.None
try this it will may work.
Okay so I've been looking around and I can't find an answer anywhere.
What I want my program to do is every time I run it, the name that shows up in the task manager is randomized.
There is a program called 'Liberation' that when you run it, it will change the process name to some random characters like AeB4B3wf52.tmp or something. I'm not sure what it is coded in though, so that might be the issue.
Is this possible in C#?
Edit:
I made a sloppy work around, I created a separate program that will check if there is a file named 'pb.dat', it will copy it to the temp folder, rename it to a 'randomchars.tmp' and run it.
Code if anyone was interested:
private void Form1_Load(object sender, EventArgs e)
{
try
{
if (!Directory.Exists(Environment.CurrentDirectory + #"\temp")) // Create a temp directory.
Directory.CreateDirectory(Environment.CurrentDirectory + #"\temp");
DirectoryInfo di = new DirectoryInfo(Environment.CurrentDirectory + #"\temp");
foreach (FileInfo f in di.GetFiles()) // Cleaning old .tmp files
{
if (f.Name.EndsWith(".tmp"))
f.Delete();
}
string charList = "abcdefghijklmnopqrstuvwxyz1234567890";
char[] trueList = charList.ToCharArray();
string newProcName = "";
for (int i = 0; i < 8; i++) // Build the random name
newProcName += trueList[r.Next(0, charList.Length)];
newProcName += ".tmp";
if (File.Exists(Environment.CurrentDirectory + #"\pb.dat")) // Just renaming and running.
{
File.Copy(Environment.CurrentDirectory + #"\pb.dat", Environment.CurrentDirectory + #"\temp\" + newProcName);
ProcessStartInfo p = new ProcessStartInfo();
p.FileName = Environment.CurrentDirectory + #"\temp\" + newProcName;
p.UseShellExecute = false;
Process.Start(p);
}
}
catch (Exception ex)
{
MessageBox.Show("I caught an exception! This is a bad thing...\n\n" + ex.ToString(), "Exception caught!");
}
Environment.Exit(-1); // Close this program anyway.
}
The process name in the task manager bases on the executable name without the extension, which you can not change while it is running.
Read the documentation:
The ProcessName property holds an executable file name, such as
Outlook, that does not include the .exe extension or the path. It is
helpful for getting and manipulating all the processes that are
associated with the same executable file.
in visual studio go to Project - Properties - Application - Assembly information and change Title
I would implement a host application to do this that simply runs and monitors a sub process (other executable). You may rename a file as such:
System.IO.File.Move("oldfilename", "newfilename");
and start the process like this:
Process.Start("newfilename");
This would mean that instead of one process you would have two, but the owner process only needs to be alive under startup - in order to change the name.
A DirectoryNotFound exception keeps happening for no apparent reason. The exception is thrown in:
public static string[] getKeywords(string filename)
{
string[] keywords = XElement.Load(filename).Elements("Keyword").Attributes("name").Select(n => n.Value).ToArray();
return keywords;
}
BUT it is called in this method:
public static void SyntaxHighlight(SyntaxHighlighter.SyntaxRichTextBox textbox, Language language)
{
switch (language)
{
case Language.Cmake:
textbox.Settings.Comment = "#";
string[] CmakeKeywords = getKeywords("APIs\\cmake.xml");
textbox.Settings.Keywords.AddRange(CmakeKeywords);
break;
case Language.CSharp:
textbox.Settings.Comment = "//";
string[] CSharpKeywords = getKeywords("APIs\\cs.xml");
textbox.Settings.Keywords.AddRange(CSharpKeywords);
break;
case Language.HTML:
textbox.Settings.Comment = "<!";
string[] HTMLKeywords = getKeywords("APIs\\html.xml");
textbox.Settings.Keywords.AddRange(HTMLKeywords);
break;
case Language.Python:
textbox.Settings.Comment = "#";
string[] PythonKeywords = getKeywords("APIs\\python.xml");
textbox.Settings.Keywords.AddRange(PythonKeywords);
break;
}
}
UPDATE:
I have a folder in my project called APIs. I checked the file names several times. Here is the exception: Could not find a part of the path 'C:\Users\Mohit\Documents\Visual Studio 2010\Projects\Notepad\Notepad\bin\Debug\APIs\cs.xml'. Thats the EXACT path of the file!
There's little hope of your program ever finding that folder. When you deploy your app, there is no project folder. The best way to organize it is to add the .xml files to your project with Project + Add Existing. Select them in Solution Explorer and in the Properties window set Build action = None and Copy to Output Directory = Copy if Newer. Build. That puts the files in the same directory as your .exe.
Find them back at runtime with System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location)
You might have better luck a fully qualified URL (eg #"D:\mypage\APIs\html.xml")
Looks like you are assuming the current directory is the install directory? You should find the directory like...
string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) +
Environment.DirectorySeparatorChar +
"file you are looking for".
Are you sure your working directory is the right one? Try to print the absolute path of the . directory. Also, list it's contents and see if that directory really is there.
I'd recommend changing your getKeywords method to be something like this to aid debugging
public static string[] getKeywords(string filename)
{
var file = new FileInfo(filename);
if (!file.Exists)
{
throw new FileNotFoundException("The requested file was not found: " + file.FullName);
}
string[] keywords = XElement.Load(filename).Elements("Keyword").Attributes("name").Select(n => n.Value).ToArray();
return keywords;
}
This should give you the full path at which it was attempting to load the file, which should make the problematic path clear to you.
I tried #nobugz method and it worked. Once you set the properties of each *.xml file to:
The best way to organize it is to add the .xml files to your project with Project + Add Existing. Select them in Solution Explorer and in the Properties window set Build action = None and Copy to Output Directory = Copy if Newer. Build. That puts the files in the same directory as your .exe.
Then put this to get your path for each case in your switch case replacing "filename" with each filename you have.
string[] YourVar = getKeywords(string.Format("{0}\\APIs\\filename.xml", Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)));