How do you get the directory target of a shortcut folder? I've search everywhere and only finds target of shortcut file.
I think you will need to use COM and add a reference to "Microsoft Shell Control And Automation", as described in this blog post:
Here's an example using the code provided there:
namespace Shortcut
{
using System;
using System.Diagnostics;
using System.IO;
using Shell32;
class Program
{
public static string GetShortcutTargetFile(string shortcutFilename)
{
string pathOnly = System.IO.Path.GetDirectoryName(shortcutFilename);
string filenameOnly = System.IO.Path.GetFileName(shortcutFilename);
Shell shell = new Shell();
Folder folder = shell.NameSpace(pathOnly);
FolderItem folderItem = folder.ParseName(filenameOnly);
if (folderItem != null)
{
Shell32.ShellLinkObject link = (Shell32.ShellLinkObject)folderItem.GetLink;
return link.Path;
}
return string.Empty;
}
static void Main(string[] args)
{
const string path = #"C:\link to foobar.lnk";
Console.WriteLine(GetShortcutTargetFile(path));
}
}
}
In windows 10 it needs to be done like this, first add COM reference to "Microsoft Shell Control And Automation"
// new way for windows 10
string targetname;
string pathOnly = System.IO.Path.GetDirectoryName(LnkFileName);
string filenameOnly = System.IO.Path.GetFileName(LnkFileName);
Shell shell = new Shell();
Shell32.Folder folder = shell.NameSpace(pathOnly);
FolderItem folderItem = folder.ParseName(filenameOnly);
if (folderItem != null) {
Shell32.ShellLinkObject link = (Shell32.ShellLinkObject)folderItem.GetLink;
targetname = link.Target.Path; // <-- main difference
if (targetname.StartsWith("{")) { // it is prefixed with {54A35DE2-guid-for-program-files-x86-QZ32BP4}
int endguid = targetname.IndexOf("}");
if (endguid > 0) {
targetname = "C:\\program files (x86)" + targetname.Substring(endguid + 1);
}
}
If you don't want to use dependencies you can use https://blez.wordpress.com/2013/02/18/get-file-shortcuts-target-with-c/
But lnk format is undocumented, so do it only if you understand risks.
An even simpler way to get the linked path that I use is:
private static string LnkToFile(string fileLink)
{
string link = File.ReadAllText(fileLink);
int i1 = link.IndexOf("DATA\0");
if (i1 < 0)
return null;
i1 += 5;
int i2 = link.IndexOf("\0", i1);
if (i2 < 0)
return link.Substring(i1);
else
return link.Substring(i1, i2 - i1);
}
But it will of course break if the lnk-file format changes.
public static string GetLnkTarget(string lnkPath)
{
var shl = new Shell();
var dir = shl.NameSpace(Path.GetDirectoryName(lnkPath));
var itm = dir.Items().Item(Path.GetFileName(lnkPath));
var lnk = (ShellLinkObject)itm.GetLink;
if (!File.Exists(lnk.Path)){
return lnk.Path.Replace("Program Files (x86)", "Program Files");
}
else{
return lnk.Path;
}
}
if you want find your application path that has shortcut on desktop, an easy way that i use, is the following:
Process.GetCurrentProcess().MainModule.FileName.Substring(0, Process.GetCurrentProcess().MainModule.FileName.LastIndexOf("\\")
this code return any exe path that is running,Regardless that who requested file
Thanks to Mohsen.Sharify's answer I got more neat piece of code:
var fileName = Process.GetCurrentProcess().MainModule.FileName;
var folderName = Path.Combine(fileName, ".."); //origin folder
All file shortcuts have a .lnk file extension you can check for. Using a string for example, you could use string.EndsWith(".lnk") as a filter.
All URL shortcuts have a .url file extension, so you will need to account for those as well if needed.
Related
This question already has answers here:
How do I determine a mapped drive's actual path?
(14 answers)
Closed 2 years ago.
I have ran into an issue where by when the user adds a file directory to my project the link is stored as their own mapped drive. For example;
C:\Location\Location
However, some users may have the C: drive on the server mapped as M: for example. Thus are unable to locate the file.
What I would like to do is replace this with the actual server name, ie
\\ArtServer\
I know that I could achieve this by replacing the opening part of the string, however if more servers are added in the future then this will obviously fallover in a huge mess. Currently the user grabs the file path using a standard get file dialogue;
public static string GetFilePath(string filter = "All Files (*.*)|*.*", string initialDirectory = #"This PC")
{
OpenFileDialog fileDialog = new OpenFileDialog();
fileDialog.Filter = filter;
fileDialog.FilterIndex = 1;
fileDialog.Multiselect = false;
fileDialog.InitialDirectory = Directory.Exists(initialDirectory) ? initialDirectory : #"This PC";
if (fileDialog.ShowDialog() == true)
{
return fileDialog.FileName;
}
else
{
return null;
}
}
Is there anyway I can achieve this with what I currently have?
Thank you to #ADyson for all of your help. I decided to use an answer provided by ibram from the thread linked above. For anyone else who has the same issue I have posted what I had done;
public static string GetUNCPath(string path)
{
if (path.StartsWith(#"\\"))
{
return path;
}
ManagementObject mo = new ManagementObject();
mo.Path = new ManagementPath(String.Format("Win32_LogicalDisk='{0}'", path));
// DriveType 4 = Network Drive
if (Convert.ToUInt32(mo["DriveType"]) == 4)
{
return Convert.ToString(mo["ProviderName"]);
}
else
{
return path;
}
}
public static string GetFilePath(string filter = "All Files (*.*)|*.*", string initialDirectory = #"This PC")
{
OpenFileDialog fileDialog = new OpenFileDialog();
fileDialog.Filter = filter;
fileDialog.FilterIndex = 1;
fileDialog.Multiselect = false;
fileDialog.InitialDirectory = Directory.Exists(initialDirectory) ? initialDirectory : #"This PC";
if (fileDialog.ShowDialog() == true)
{
// Split the file directory to gain root path
// Use GetUNCPath to convert root path to server name
string s = fileDialog.FileName;
int index = s.IndexOf(':') + 1;
string rootPath = GetUNCPath(s.Substring(0, index));
string directory = s.Substring(index);
return rootPath + directory;
}
else
{
return null;
}
}
I'm editing my question to make it more readily understood.
Here is an example solution: https://github.com/mckenn55/PowershellTest
I created a brand new net47 MVC project. I added an area called "Database" and a controller within that area called "Update". Within that controller, I have the following:
public ActionResult Index()
{
return View(Execute());
}
public static List<string> Execute()
{
var returnable = new List<string>();
var assembly = Assembly.GetExecutingAssembly();
string codeBase = assembly.CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
var assemblyLocation = Path.GetDirectoryName(path);
string resourcePath = "";
string ModuleName = assembly.ManifestModule.Name;
ModuleName = ModuleName.Substring(0, ModuleName.LastIndexOf("."));
ModuleName = ModuleName.Replace(' ', '_').Replace(".", "");
string FolderPath = "Areas.Database.SQL";
FolderPath = FolderPath.Replace(' ', '_');
if (FolderPath != null && FolderPath.Length > 0 && FolderPath[FolderPath.Length - 1] == '.')
FolderPath = FolderPath.Substring(0, FolderPath.Length - 1);
StringBuilder filepath = new StringBuilder();
filepath.Append(ModuleName);
if (FolderPath != null && FolderPath.Length > 0)
{
filepath.Append('.' + FolderPath);
filepath.Append('.');
}
resourcePath = filepath.ToString();
string[] resourceNames = assembly.GetManifestResourceNames();
foreach (var resourceName in resourceNames)
{
if (Regex.Match(resourceName, "^" + resourcePath).Success)
{
returnable.Add(resourceName);
}
}
var orderedFileNames = new List<string>();
if (returnable != null && returnable.Any())
{
orderedFileNames = returnable.OrderBy(q => q).ToList();
}
else
{
returnable.Add("No files found");
}
return returnable;
}
Within the Database area, I have a directory called "SQL" and within that directory, I have a single file, TestFile.sql, included in the solution as an embedded resource. The results of the Execute() method, when viewed using the index action is "PSTest.Areas.Database.SQL.TestFile.sql". I would like to see the same thing in Powershell. I have tried the following:
> Add-Type -path "C:\Temp\PSTest\PSTest\bin\PSTest.dll"
> [PSTest.Areas.Database.UpdateController]::Execute()
No Files Found
Is my goal possible through powershell and if so, how?
I was not able to find a solution to this using powershell alone. I wrote a C# console app that is able to perform these actions without any issue.
I have to create a file and the path to the file contains a folder that is actually a shortcut.
Example:
D:\SQL Backups\LocumsExcite_04-Sep-2013 16-36-59-135.bak
The SQL Backups folder is a shortcut in D: drive. The shortcut takes us to C:\Users\Administrator\Dropbox\SQL Backups\ folder
I looked at the Shell examples everywhere but its not working for me.
public static string GetShortcutTarget(string shortcutFilename)
{
string pathOnly = System.IO.Path.GetDirectoryName(shortcutFilename);
string filenameOnly = System.IO.Path.GetFileName(shortcutFilename);
Shell32.Shell shell = new Shell32.ShellClass();
Shell32.Folder folder = shell.NameSpace(pathOnly);
Shell32.FolderItem folderItem = folder.ParseName(filenameOnly);
if (folderItem != null)
{
if (folderItem.IsLink)
{
Shell32.ShellLinkObject link = (Shell32.ShellLinkObject)folderItem.GetLink;
return link.Path;
}
return shortcutFilename;
}
return ""; // not found
}
The line shell.NameSpace(pathOnly); always return a null. Any suggestions?
How can i get the root directory of a folder +1?
Example:
Input: C:\Level1\Level2\level3
output should be:
Level1
If input is Level1
output should be Level1
if input is C:\ output should be empty string
Is there is a .Net function handles this?
Directory.GetDirectoryRoot will always returns C:\
You can use the Path-class + Substring + Split to remove the root and get the top-folder.
// your directory:
string dir = #"C:\Level1\Level2\level3";
// C:\
string root = Path.GetPathRoot(dir);
// Level1\Level2\level3:
string pathWithoutRoot = dir.Substring(root.Length);
// Level1
string firstFolder = pathWithoutRoot.Split(Path.DirectorySeparatorChar).First();
Another way is using the DirectoryInfo class and it's Parent property:
DirectoryInfo directory = new DirectoryInfo(#"C:\Level1\Level2\level3");
string firstFolder = directory.Name;
while (directory.Parent != null && directory.Parent.Name != directory.Root.Name)
{
firstFolder = directory.Parent.Name;
directory = directory.Parent;
}
However, i would prefer the "lightweight" string methods.
string dir = #"C:\foo\bar\woah";
var dirSegments = dir.Split(new char[] { Path.DirectorySeparatorChar },
StringSplitOptions.RemoveEmptyEntries);
if (dirSegments.Length == 1)
{
return string.Empty;
}
else
{
return dirSegments[0] + Path.DirectorySeparatorChar + dirSegments[1];
}
You could loop up using the directory info class using the following structure by adding the code section below into a method
string path = "C:\Level1\Level2\level3";
DirectoryInfo d = new DirectoryInfo(path);
while (d.Parent.FullName != Path.GetPathRoot(path))
{
d = d.Parent;
}
return d.FullName;
You could use DirectoryInfo and a while loop.
DirectoryInfo info = new DirectoryInfo(path);
while (info.Parent != null && info.Parent.Parent != null)
info = info.Parent;
string result = info.FullName;
Not sure whether this is the correct way but you do:
string s = #"C:\Level1\Level2\Level3";
string foo = s.split(#"\")[1];
Not sure whether DirectoryInfo object can get this in a cleaner manner..
DirectoryInfo di = new DirectoryInfo(#"C:\Level1\Level2\Level3");
di.Root;
One possible solution, but may not be the best, is to find the position of #"\", and do some manual processing yourself. Below code is not fully tested, just snippet:
//var positions = inputString.IndexOfAny(new [] {'\'}); //Original one
//Updated, thanks to Snixtor's implementation
var positions = inputString.IndexOfAny(new [] {Path.DirectorySeparatorChar});
int n=positions.Length;
if(n>=1)
{
var pos = positions[1]; //The 2nd '\';
return inputString.SubString(0, pos);
}
return null;
Of course, this only works if we are sure we want chop substrings after the 2nd '\'.
A happy linq one liner:
string level1_2 = Directory.GetDirectoryRoot(path) + path.Split(Path.DirectorySeparatorChar).Skip(1).Take(1).DefaultIfEmpty("").First();
var di = new System.IO.DirectoryInfo(#"C:\a\b\c");
Func<DirectoryInfo, DirectoryInfo> root = null;
root = (info) => info.Parent.FullName.EndsWith("\\") ? info : root(info.Parent);
var rootName = root(di).Name; //#a
Why not just use System.IO.Path to retrieve the name?
MessageBox.Show(System.IO.Path.GetFileName(
System.IO.Path.GetDirectoryName(
System.IO.Path.GetDirectoryName(#"C:\Level1\Level2\Level3")
)
));
This returns Level 1.
MessageBox.Show(System.IO.Path.GetFileName(
System.IO.Path.GetDirectoryName(
System.IO.Path.GetDirectoryName(#"C:\")
)
));
This returns empty string.
I want to check if a specific folder exists in a folder in c#.
Currently I'm using the following code.
string currentPath = Directory.GetCurrentDirectory();
foreach (string subDir in Directory.GetDirectories(currentPath))
{
if (subDir == currentPath + #"\Database") // if "Database" folder exists
{
dbinRoot = true;
}
}
if (dbinRoot == true)
{
currentPath = currentPath + #"\Database\SMAStaff.accdb";
}
else
{
for (int i = 0; i < 2; i++)
{
currentPath = currentPath.Remove(currentPath.LastIndexOf("\\"));
}
currentPath = currentPath + "\\Database\\SMAStaff.accdb";
}
I wish to know if there is a better way of doing this ???
you can use:
Directory.Exists(currentPath + #"\Database")
It also more reliably to use Path.Combine method to concatenate parts of path
if (Directory.Exists(Path.Combine(Directory.GetCurrentDirectory(), #"Database"))){}
You are probably directly looking for
currentPath = Directory.GetCurrentDirectory();
bool fileExists = File.Exists(Path.Combine(currentPath, "Database\\SMAStaff.accdb")))
And the for cycle may contain for better readability
currentPath = Directory.GetParent(currentPath).FullName;