Get Full Path of File from Process in C#? - c#

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.

Related

How to get the Full Path of a File?

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();
}

Environment.CurrentDirectory returns odd results

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;
}

Finding Network Drive from UNC Path

I created a button that grabs the text contents of the Clipboard class, checks if it's a folder path and creates a hyperlink out of it. The user will probably just copy the path from the explorer window. The problem I have, which seems to be the opposite of a lot of questions I found, is that I want the drive path (T:\Natzely) instead of the UNC path (\SEOMAFIL02\Trash\Natzely).
When I ctrl+v the copied path into either Word, Notepad or Outlook it gets copied as drive path, but when I copy it to Chrome's address bar or try to retrieve it from the Clipboard class it gets copied as the UNC path. How does Microsoft deal with this?
My drive letters stay pretty much static, so I don't have to worry about the T drive not being the Trash drive in the future.
EDIT
Here is the code
object clipboardText = Clipboard.GetText();
if(!Directory.Exists(clipboardText.ToString()))
{
//Show error message
}
doc = GetDoc(); //Get document to add the hyperlink to
sel = doc.Selection;
range = sel.Range;
hyperlinks = sel.Hyperlinks;
hyperlinks.Add(range, ref clipboardText);
EDIT Numero Dos
It seems to be more of an issue with Hyperlinks.Add than the clipboard. If I add a space before the clipboard text object clipboardText = " " + Clipboard.GetText() it seems to correct the issue, the hyperlink will now have and extra space before but the link still works.
Have you see this question from Microsoft?
Translate UNC path to local mounted driveletter.
Seems like there are two ways. The first way is to get the value from the registry:
string UNCtoMappedDrive(string uncPath)
{
Microsoft.Win32.RegistryKey rootKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("network");
foreach (string subKey in rootKey.GetSubKeyNames())
{
Microsoft.Win32.RegistryKey mappedDriveKey = rootKey.OpenSubKey(subKey);
if (string.Compare((string)mappedDriveKey.GetValue("RemotePath", ""), uncPath, true) == 0)
return subKey.ToUpperInvariant() + #":\";
}
return uncPath;
}
The other way is with System.Management. The example is large, but here's the core of it:
private void LoadDrives()
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT RemoteName, LocalName FROM Win32_NetworkConnection");
List<UncToDrive> drives = new List<UncToDrive>();
foreach (ManagementObject obj in searcher.Get())
{
object localObj = obj["LocalName"];
if (!Object.Equals(localObj, null))
drives.Add(new UncToDrive(obj["RemoteName"].ToString(), localObj.ToString()));
}
}
They search for all paths with the drive (LocalName) and without (RemoteName), then save them in a list for later lookup.

Search files through a string causes error

When i search files through a string in my local drives it shows the following error and it stops to search further.The reason is some of the windows files are used by the OS when the search is in progress.How to overcome this.
The process cannot access the file 'C:\hiberfil.sys'(Hibernate Files) because it is being used by another process.
TextReader rff = null;
rff = new StreamReader(fi.FullName);
try
{
String lne1 = rff.ReadToEnd();
if (lne1.IndexOf(txt) >= 0)
{
z = fi.FullName;
list22.Add(fi.FullName);
You should narrow down your search wildcard to avoid hitting system or locked files or you will always get this exception. In .NET 4.0 you could use the EnumerateFiles method which will perform the search lazily and you could catch the exception.
c:\hiberfil.sys is a system file that is locked against reading. You won't be able to read it because of that. There is no call you can do in c# to determine if a file is locked before you attempt to open it, so put Try/Catch blocks around your attempt to open it and if it throws an exception, just go on to the next file.
TextReader rff = null;
try
{
rff = new StreamReader(fi.FullName);
String lne1 = rff.ReadToEnd();
if (lne1.IndexOf(txt) >= 0)
{
z = fi.FullName;
list22.Add(fi.FullName);

hijack program’s command to run notepad

I have a utility programs’s EXE file, when i run this file there is a winform only and there is button when we click on it, it run windows’s notepad. Now I want to hijack this program’s command to run notepad and instead of running notepad I want to run MS Word. I know C# and VB.NET. What I need to do this ?
You can try to add in folder with this program your own program called notepad.exe that should do only one thing: run word.
If you want to do it programatically in C then you should read this page - maybe it helps: Intercepted: Windows Hacking via DLL Redirection
You can use a trick to replace programs with another by making changes to the registry. This will work even if the program you are running uses absolute paths to run notepad. It overrides any instance of the running program with the chosen one no matter where it resides. And you won't have to patch the file. The key you'd be interested in is:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
Add a key with the name of the program and add a Debugger string with the path to the program you want to replace it with. Of course you need to have permissions to make the necessary modifications. This page explains how you can replace Windows Notepad with another program. You can apply the same process here.
Though you'll probably not want to have this permanent change, so you can write up a program to temporarily add/change the key, run your program then change it back. Here's a complete one I just whipped up to temporarily replace Notepad with Word for a demonstration. Seems to work perfectly fine (though as always, use at your own risk). Just make all the necessary changes to fit your situation.
using System.Diagnostics;
using Microsoft.Win32;
namespace ProgramLauncher
{
class Program
{
// change the following constants as needed
const string PROGRAM_NAME = #"notepad.exe";
const string REPLACEMENT_PATH = #"C:\Program Files (x86)\Microsoft Office\Office12\WINWORD.EXE";
const string RUNNING_PATH = #"C:\Windows\notepad.exe";
// root key
const string KEY = #"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options";
static void Main(string[] args)
{
using (var rootKey = Registry.LocalMachine.OpenSubKey(KEY, RegistryKeyPermissionCheck.ReadWriteSubTree))
{
var oldPath = default(string);
var needsRestoration = false;
try
{
oldPath = BackupKey(rootKey, PROGRAM_NAME, REPLACEMENT_PATH);
needsRestoration = true;
Process.Start(RUNNING_PATH).WaitForExit();
}
finally
{
if (needsRestoration)
RestoreKey(rootKey, PROGRAM_NAME, oldPath);
}
}
}
static string BackupKey(RegistryKey rootKey, string programName, string newPath)
{
Debug.Assert(rootKey != null);
Debug.Assert(!string.IsNullOrEmpty(programName));
Debug.Assert(!string.IsNullOrEmpty(newPath) && System.IO.File.Exists(newPath));
if (newPath.Contains(" "))
newPath = string.Format("\"{0}\"", newPath);
using (var programKey = rootKey.CreateSubKey(programName, RegistryKeyPermissionCheck.ReadWriteSubTree))
{
var oldDebugger = programKey.GetValue("Debugger") as string;
programKey.SetValue("Debugger", newPath, RegistryValueKind.String);
return oldDebugger;
}
}
static void RestoreKey(RegistryKey rootKey, string programName, string oldPath)
{
Debug.Assert(rootKey != null);
Debug.Assert(!string.IsNullOrEmpty(programName));
if (oldPath != null)
{
using (var programKey = rootKey.OpenSubKey(programName, RegistryKeyPermissionCheck.ReadWriteSubTree))
programKey.SetValue("Debugger", oldPath);
}
else
{
rootKey.DeleteSubKey(programName);
}
}
}
}

Categories

Resources