Is it always valid to use the first part of the last guid from a windows api codepack ShellObject parsing name representing a path to a USB-connected android or iphone device when transferring a file to local storage in the windows portable device (WPD) api?
Eg. an example parsing name from a ShellObject get parsing name call:
::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\?\usb#vid_22b8&pid_2e82#zy322kldjb#{6ac27878-a6fa-4155-ba85-f98f491d4f33}\{00000009-0001-0001-0000-000000000000}\{00000009-0001-0001-0000-000000000000}\{00005461-0001-0001-0000-000000000000}
As far as I can tell, this represents:
- My Computer ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\
- USB device \\?\usb#vid_22b8&pid_2e82#zy322kldjb#{6ac27878-a6fa-4155-ba85-f98f491d4f33}\
- Root folder SID-{10001,,26401026048}\
- Folder 1 {00000009-0001-0001-0000-000000000000}\
- Folder 2 {00000075-0001-0001-0000-000000000000}\
- File {00005461-0001-0001-0000-000000000000}
Enumerating the contents of Folder 2 on the device using the WPD api, I can see the same file has a file id like o5461 - this looks like part of the File part above.
Is this assumption always correct - can I always use it like in the following code, to copy one file from an android phone to local storage on the c-drive? Or is there sometimes a mismatch? Also, what does the "o" mean in the file id?
This seems to work, but I'm hesitant to use it in production-ready code.
using System.IO;
using PortableDeviceApiLib;
public class Copier
{
public void PerformCopy()
{
var deviceId = #"\\?\usb#vid_22b8&pid_2e82#zy322kldjb#{6ac27878-a6fa-4155-ba85-f98f491d4f33}";
var sourceItemGuidString = "{00005461-0001-0001-0000-000000000000}";
var destinationPath = #"C:\Test\";
var fileName = "Testing.jpg";
var size = 3738545;
// get "o5461" from the parsing path from the Shell libraries
var fileId = "o" + sourceItemGuidString.Replace("{", string.Empty).Replace("}", string.Empty).Split('-')[0].TrimStart('0');
this.TransferContentFromDevice(deviceId, fileId, size, destinationPath, fileName);
}
private void TransferContentFromDevice(string deviceId, string fileId, long length, string saveToPath, string fileName)
{
PortableDeviceClass device;
try
{
device = SomehowGetOnePortableDeviceAndConnectToIt(deviceId);
// Make sure that the target dir exists.
Directory.CreateDirectory(saveToPath);
device.Content(out var content);
content.Transfer(out var resources);
var property = new _tagpropertykey
{
fmtid = new Guid(0xE81E79BE, 0x34F0, 0x41BF, 0xB5, 0x3F, 0xF1, 0xA0, 0x6A, 0xE8, 0x78, 0x42),
pid = 0
};
uint optimalTransferSize = 0;
resources.GetStream(fileId, ref property, 0, ref optimalTransferSize, out IStream wpdStream);
var sourceStream = (System.Runtime.InteropServices.ComTypes.IStream)wpdStream;
using (var targetStream = new FileStream(Path.Combine(saveToPath, fileName), FileMode.Create, FileAccess.Write))
{
// Get the total size.
long written = 0;
long lPCt = 0;
unsafe
{
var buffer = new byte[1024];
int bytesRead;
do
{
sourceStream.Read(buffer, 1024, new IntPtr(&bytesRead));
targetStream.Write(buffer, 0, bytesRead);
written += 1024;
long PCt = length > 0 ? (100 * written) / length : 100;
if (PCt != lPCt)
{
lPCt = PCt;
Console.WriteLine("Progress: " + lPCt);
}
} while (bytesRead > 0);
}
}
}
finally
{
Disconnect(device);
}
}
}
If item has a parsing name like below, I was after the 5461 part from the final guid:
::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\?\usb#vid_22b8&pid_2e82#zy322kldjb#{6ac27878-a6fa-4155-ba85-f98f491d4f33}\SID-{10001,,26401026048}\{00000009-0001-0001-0000-000000000000}\{00000075-0001-0001-0000-000000000000}\{00005461-0001-0001-0000-000000000000}
We can use the following code which will get the WPD_OBJECT_ID property from a ShellObject. More info on this and related properties here:
string GetFileId(ShellObject item)
{
const int WPD_OBJECT_ID = 2;
var property = item.Properties.GetProperty(new PropertyKey(new Guid("{ef6b490d-5cd8-437a-affc-da8b60ee4a3c}"), WPD_OBJECT_ID));
return property.ValueAsObject as string;
}
This returns the file id o5641, which can then be used in my TransferContentFromDevice method directly.
Thanks to #SimonMourier for pointing me in the right direction.
I use this following code to remove the write protection folder so that I can delete that. But It won't work.
File.SetAttributes(#"F:\File", FileAttributes.Normal);
File.Delete(#"F:\File");
How can I remove the write protection?
If I can remove file protection from the disk, so give some codes to do that.
Any help will be appreciated
Thanks in advance
There is a difference between Folder and File. With this you will remove the readonly atribute and delete the folder.
var di = new DirectoryInfo(#"F:\File");
di.Attributes &= ~FileAttributes.ReadOnly;
di.Delete(true);
EDIT:
Formating USB drive. You can read the article.
public static bool FormatDrive(string driveLetter,
string fileSystem = "NTFS", bool quickFormat=true,
int clusterSize = 8192, string label = "", bool enableCompression = false )
{
if (driveLetter.Length != 2 || driveLetter[1] != ':'|| !char.IsLetter(driveLetter[0]))
return false;
//query and format given drive
ManagementObjectSearcher searcher = new ManagementObjectSearcher
(#"select * from Win32_Volume WHERE DriveLetter = '" + driveLetter + "'");
foreach (ManagementObject vi in searcher.Get())
{
vi.InvokeMethod("Format", new object[]
{ fileSystem, quickFormat,clusterSize, label, enableCompression });
}
return true;
}
You should put the driveLetter like this: "F:"
I'm trying to find a particular USB device (1 or more) connected to my computer and retrieve the relevant path to the mounted drive. Ideally, it would be by finding the VID/PID of the USB device, but I'm not sure how to do that yet. The following works, but there must be some way to get the data in a single query.
What I'm doing here is looking or a physical drive that has a model matching HS SD Card Bridge USB Device and finding the physical drive # associated and using that to find the mounted partition..
foreach (ManagementObject disk in disks.Get()) {
//look for drives that match our string
Match m = Regex.Match(disk["model"].ToString(), "HS SD Card Bridge USB Device");
if (m.Success) {
m = Regex.Match(disk["DeviceID"].ToString(), #"PHYSICALDRIVE(\d+)");
if (m.Success) {
int driveNumber = Int32.Parse(m.Groups[1].ToString());
ManagementObjectSearcher mapping = new ManagementObjectSearcher("SELECT * FROM Win32_LogicalDiskToPartition");
foreach (ManagementObject map in mapping.Get()) {
m = Regex.Match(map["Antecedent"].ToString(), #"Disk #" + driveNumber + ",");
if (m.Success) {
string drive = map["Dependent"].ToString();
m = Regex.Match(drive, #"([A-Z]):");
if (m.Success) {
drive = m.Groups[1].ToString(); //< -- **FOUND**
}
}
}
//USBDevice dev = new USBDevice("", "");
// list.Items.Add();
Console.WriteLine("");
}
}
}
is there a way to do this from the VID/PID and a way to construct the search query so it requires just one query?
This is the one I used earlier . This will not be the answer. But will help you .
public int GetAvailableDisks()
{
int deviceFound = 0;
try
{
// browse all USB WMI physical disks
foreach (ManagementObject drive in
new ManagementObjectSearcher(
"select DeviceID, Model from Win32_DiskDrive where InterfaceType='USB'").Get())
{
ManagementObject partition = new ManagementObjectSearcher(String.Format(
"associators of {{Win32_DiskDrive.DeviceID='{0}'}} where AssocClass = Win32_DiskDriveToDiskPartition",
drive["DeviceID"])).First();
if (partition == null) continue;
// associate partitions with logical disks (drive letter volumes)
ManagementObject logical = new ManagementObjectSearcher(String.Format(
"associators of {{Win32_DiskPartition.DeviceID='{0}'}} where AssocClass = Win32_LogicalDiskToPartition",
partition["DeviceID"])).First();
if (logical != null)
{
// finally find the logical disk entry to determine the volume name - Not necesssary
//ManagementObject volume = new ManagementObjectSearcher(String.Format(
// "select FreeSpace, Size, VolumeName from Win32_LogicalDisk where Name='{0}'",
// logical["Name"])).First();
string temp = logical["Name"].ToString() + "\\";
// +" " + volume["VolumeName"].ToString(); Future purpose if Device Name required
deviceFound++;
if (deviceFound > 1)
{
MessageBox.Show(#"Multiple Removeable media found. Please remove the another device");
deviceFound--;
}
else
{
driveName = temp;
}
}
}
}
catch (Exception diskEnumerateException)
{
}
return deviceFound;
}
Using C#, I would like my application to return whether a folder (with an already known path) is located in a network or in my computer.
How can I do that?
If you are talking about a mapped network drive, you can use the DriveInfo's DriveType property:
var driveInfo = new DriveInfo("S:\");
if(driveInfo.DriveType == DriveType.Network)
// do something
Original Answer from another SO question, Check if path is on network.
Use PathIsNetworkPath (pinvoke reference):
class Program
{
[DllImport("shlwapi.dll")]
private static extern bool PathIsNetworkPath(string pszPath);
static void Main(string[] args)
{
Console.WriteLine(PathIsNetworkPath("i:\Backup"));
}
}
Based on #jgauffin and #Daniel answers, you could try this little hack:
private static bool IsNetwork(String path)
{
if (path.StartsWith(#"\\"))
return true;
var dir = new DirectoryInfo(path);
var drive = new DriveInfo(dir.Root.ToString());
return drive.DriveType == DriveType.Network;
}
var dirInfo = new DirectoryInfo(yourPath);
var driveInfo = new DriveInfo(dirInfo.Root);
if (driveInfo.DriveType == DriveType.Network)
Console.WriteLine("Is a network drive!");
You can use folowing method to get UNC path for a folder. Not exactly what you are looking for but might be useful
public static string GetUniversalPath(string folderPath)
{
if (String.IsNullOrEmpty(folderPath) || folderPath.IndexOf(":") > 1)
return folderPath;
if (folderPath.StartsWith("\\"))
{
return folderPath;
}
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT RemoteName FROM win32_NetworkConnection WHERE LocalName = '" + folderPath.Substring(0, 2) + "'");
foreach (ManagementObject managementObject in searcher.Get())
{
string remoteName = managementObject["RemoteName"] as String;
if (!String.IsNullOrEmpty(remoteName))
{
remoteName += folderPath.Substring(2);
return remoteName;
}
}
return folderPath;
}
Try the following from Shell Lightweight Utility API:
class Class
{
[DllImport("shlwapi.dll")]
private static extern bool PathIsNetworkPath(string Path);
[STAThread]
static void Main(string[] args)
{
string strPath = "D:\\Temp\\tmpfile.txt";
bool blnIsLocalPath = IsLocalPath(strPath);
Console.WriteLine(blnIsLocalPath.ToString());
Console.ReadLine();
}
private static bool IsLocalPath(string Path)
{
return !PathIsNetworkPath(Path);
}
}
Things to take into consideration:
Paths that begin with two backslash characters (\) are interpreted as Universal Naming Convention (UNC) paths.
Paths that begin with a letter followed by a colon (:) are interpreted as a mounted network drive. However, PathIsNetworkPath cannot recognize a network drive mapped to a drive letter through the Microsoft MS-DOS SUBST command or the DefineDosDevice function
How do I determine a mapped drive's actual path?
So if I have a mapped drive on a machine called "Z" how can I using .NET determine the machine and path for the mapped folder?
The code can assume it's running on the machine with the mapped drive.
I looked at Path, Directory, FileInfo objects, but can't seem to find anything.
I also looked for existing questions, but could not find what I'm looking for.
I expanded on ibram's answer and created this class (which has been updated per comment feedback). I've probably over documented it, but it should be self-explanatory.
/// <summary>
/// A static class to help with resolving a mapped drive path to a UNC network path.
/// If a local drive path or a UNC network path are passed in, they will just be returned.
/// </summary>
/// <example>
/// using System;
/// using System.IO;
/// using System.Management; // Reference System.Management.dll
///
/// // Example/Test paths, these will need to be adjusted to match your environment.
/// string[] paths = new string[] {
/// #"Z:\ShareName\Sub-Folder",
/// #"\\ACME-FILE\ShareName\Sub-Folder",
/// #"\\ACME.COM\ShareName\Sub-Folder", // DFS
/// #"C:\Temp",
/// #"\\localhost\c$\temp",
/// #"\\workstation\Temp",
/// #"Z:", // Mapped drive pointing to \\workstation\Temp
/// #"C:\",
/// #"Temp",
/// #".\Temp",
/// #"..\Temp",
/// "",
/// " ",
/// null
/// };
///
/// foreach (var curPath in paths) {
/// try {
/// Console.WriteLine(string.Format("{0} = {1}",
/// curPath,
/// MappedDriveResolver.ResolveToUNC(curPath))
/// );
/// }
/// catch (Exception ex) {
/// Console.WriteLine(string.Format("{0} = {1}",
/// curPath,
/// ex.Message)
/// );
/// }
/// }
/// </example>
public static class MappedDriveResolver
{
/// <summary>
/// Resolves the given path to a full UNC path if the path is a mapped drive.
/// Otherwise, just returns the given path.
/// </summary>
/// <param name="path">The path to resolve.</param>
/// <returns></returns>
public static string ResolveToUNC(string path) {
if (String.IsNullOrWhiteSpace(path)) {
throw new ArgumentNullException("The path argument was null or whitespace.");
}
if (!Path.IsPathRooted(path)) {
throw new ArgumentException(
string.Format("The path '{0}' was not a rooted path and ResolveToUNC does not support relative paths.",
path)
);
}
// Is the path already in the UNC format?
if (path.StartsWith(#"\\")) {
return path;
}
string rootPath = ResolveToRootUNC(path);
if (path.StartsWith(rootPath)) {
return path; // Local drive, no resolving occurred
}
else {
return path.Replace(GetDriveLetter(path), rootPath);
}
}
/// <summary>
/// Resolves the given path to a root UNC path if the path is a mapped drive.
/// Otherwise, just returns the given path.
/// </summary>
/// <param name="path">The path to resolve.</param>
/// <returns></returns>
public static string ResolveToRootUNC(string path) {
if (String.IsNullOrWhiteSpace(path)) {
throw new ArgumentNullException("The path argument was null or whitespace.");
}
if (!Path.IsPathRooted(path)) {
throw new ArgumentException(
string.Format("The path '{0}' was not a rooted path and ResolveToRootUNC does not support relative paths.",
path)
);
}
if (path.StartsWith(#"\\")) {
return Directory.GetDirectoryRoot(path);
}
// Get just the drive letter for WMI call
string driveletter = GetDriveLetter(path);
// Query WMI if the drive letter is a network drive, and if so the UNC path for it
using (ManagementObject mo = new ManagementObject()) {
mo.Path = new ManagementPath(string.Format("Win32_LogicalDisk='{0}'", driveletter));
DriveType driveType = (DriveType)((uint)mo["DriveType"]);
string networkRoot = Convert.ToString(mo["ProviderName"]);
if (driveType == DriveType.Network) {
return networkRoot;
}
else {
return driveletter + Path.DirectorySeparatorChar;
}
}
}
/// <summary>
/// Checks if the given path is a network drive.
/// </summary>
/// <param name="path">The path to check.</param>
/// <returns></returns>
public static bool isNetworkDrive(string path) {
if (String.IsNullOrWhiteSpace(path)) {
throw new ArgumentNullException("The path argument was null or whitespace.");
}
if (!Path.IsPathRooted(path)) {
throw new ArgumentException(
string.Format("The path '{0}' was not a rooted path and ResolveToRootUNC does not support relative paths.",
path)
);
}
if (path.StartsWith(#"\\")) {
return true;
}
// Get just the drive letter for WMI call
string driveletter = GetDriveLetter(path);
// Query WMI if the drive letter is a network drive
using (ManagementObject mo = new ManagementObject()) {
mo.Path = new ManagementPath(string.Format("Win32_LogicalDisk='{0}'", driveletter));
DriveType driveType = (DriveType)((uint)mo["DriveType"]);
return driveType == DriveType.Network;
}
}
/// <summary>
/// Given a path will extract just the drive letter with volume separator.
/// </summary>
/// <param name="path"></param>
/// <returns>C:</returns>
public static string GetDriveLetter(string path) {
if (String.IsNullOrWhiteSpace(path)) {
throw new ArgumentNullException("The path argument was null or whitespace.");
}
if (!Path.IsPathRooted(path)) {
throw new ArgumentException(
string.Format("The path '{0}' was not a rooted path and GetDriveLetter does not support relative paths.",
path)
);
}
if (path.StartsWith(#"\\")) {
throw new ArgumentException("A UNC path was passed to GetDriveLetter");
}
return Directory.GetDirectoryRoot(path).Replace(Path.DirectorySeparatorChar.ToString(), "");
}
}
I can't remember where I found this, but it works without p/invoke. It's what rerun posted before.
you need to reference System.Management.dll:
using System.IO;
using System.Management;
code:
public void FindUNCPaths()
{
DriveInfo[] dis = DriveInfo.GetDrives();
foreach( DriveInfo di in dis )
{
if(di.DriveType == DriveType.Network)
{
DirectoryInfo dir = di.RootDirectory;
// "x:"
MessageBox.Show( GetUNCPath( dir.FullName.Substring( 0, 2 ) ) );
}
}
}
public 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;
}
}
Update:
Explicitly running as administrator will not show mapped drives. Here is an explanation of this behaviour:
https://stackoverflow.com/a/11268410/448100
(in short: administrator has a different user context, so no access to mapped drives of normal user)
Here are some code samples:
Using P/Invoke
All of the magic derives from a Windows function:
[DllImport("mpr.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int WNetGetConnection(
[MarshalAs(UnmanagedType.LPTStr)] string localName,
[MarshalAs(UnmanagedType.LPTStr)] StringBuilder remoteName,
ref int length);
Example invocation:
var sb = new StringBuilder(512);
var size = sb.Capacity;
var error = Mpr.WNetGetConnection("Z:", sb, ref size);
if (error != 0)
throw new Win32Exception(error, "WNetGetConnection failed");
var networkpath = sb.ToString();
I've written a method for this. It returns a UNC path if it is a mapped drive, otherwise it returns the path unchanged.
public static string UNCPath(string path)
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey("Network\\" + path[0]))
{
if (key != null)
{
path = key.GetValue("RemotePath").ToString() + path.Remove(0, 2).ToString();
}
}
return path;
}
EDIT
You now can use the Method even with already UNC paths. The above version of the method throws an exception if given a UNC path.
public static string UNCPath(string path)
{
if (!path.StartsWith(#"\\"))
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey("Network\\" + path[0]))
{
if (key != null)
{
return key.GetValue("RemotePath").ToString() + path.Remove(0, 2).ToString();
}
}
}
return path;
}
I think you can use the "Network" key From the "Current User" Hive, In the Registry.
The Mapped Drives Are Listed There With Their Shared Path On Server.
If there is no mapped drive in the system, so there is no "Network" Key In The "Current User" Hive.
Now, I'm using this way, no external dll nor anything else.
You can use WMI to interrogate the Win32_LogicalDrive collection on your machine. Here is an example of how to do it with scripting. Changing this over to C# is pretty well explained in other places.
Slightly modified VB.NET code from the article:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim strComputer = "."
Dim objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Dim colDrives = objWMIService.ExecQuery("Select * From Win32_LogicalDisk Where DriveType = 4")
For Each objDrive In colDrives
Debug.WriteLine("Drive letter: " & objDrive.DeviceID)
Debug.WriteLine("Network path: " & objDrive.ProviderName)
Next
End Sub
End Class
I could not replicate ibram's or Vermis' answer due to the problem I mentioned in a comment under Vermis' answer, about a type initializer exception.
Instead, I discovered I could query for all the drives currently on the computer and then loop through them, like so:
using System.IO; //For DirectoryNotFound exception.
using System.Management;
/// <summary>
/// Given a local mapped drive letter, determine if it is a network drive. If so, return the server share.
/// </summary>
/// <param name="mappedDrive"></param>
/// <returns>The server path that the drive maps to ~ "////XXXXXX//ZZZZ"</returns>
private string CheckUNCPath(string mappedDrive)
{
//Query to return all the local computer's drives.
//See http://msdn.microsoft.com/en-us/library/ms186146.aspx, or search "WMI Queries"
SelectQuery selectWMIQuery = new SelectQuery("Win32_LogicalDisk");
ManagementObjectSearcher driveSearcher = new ManagementObjectSearcher(selectWMIQuery);
//Soem variables to be used inside and out of the foreach.
ManagementPath path = null;
ManagementObject networkDrive = null;
bool found = false;
string serverName = null;
//Check each disk, determine if it is a network drive, and then return the real server path.
foreach (ManagementObject disk in driveSearcher.Get())
{
path = disk.Path;
if (path.ToString().Contains(mappedDrive))
{
networkDrive = new ManagementObject(path);
if (Convert.ToUInt32(networkDrive["DriveType"]) == 4)
{
serverName = Convert.ToString(networkDrive["ProviderName"]);
found = true;
break;
}
else
{
throw new DirectoryNotFoundException("The drive " + mappedDrive + " was found, but is not a network drive. Were your network drives mapped correctly?");
}
}
}
if (!found)
{
throw new DirectoryNotFoundException("The drive " + mappedDrive + " was not found. Were your network drives mapped correctly?");
}
else
{
return serverName;
}
}
This works for x64 Windows 7, for .NET 4. It should be usable in case you're getting that exception that was mentioned above.
I did this using the stuff given from MSDN and bits from ibram's or Vermis' answers, though it was a bit difficult to find specific examples on the MSDN. Resources used:
MSDN : Win32_LogicalDisk Class
MSDN : System.Management namespace
MSDN : WMI Queries example:
using System;
using System.Management;
class Query_SelectQuery
{
public static int Main(string[] args)
{
SelectQuery selectQuery = new
SelectQuery("Win32_LogicalDisk");
ManagementObjectSearcher searcher =
new ManagementObjectSearcher(selectQuery);
foreach (ManagementObject disk in searcher.Get())
{
Console.WriteLine(disk.ToString());
}
Console.ReadLine();
return 0;
}
}
QueryDosDevice translates a drive letter into the path that it expands to.
Note that this will translate ALL drive letters, not just those that are mapped to network connections. You need to already know which are network paths, or to parse the output to see which are network.
Here's the VB signature
Declare Function QueryDosDevice Lib "kernel32" Alias "QueryDosDeviceA" (
ByVal lpDeviceName As String,
ByVal lpTargetPath As String,
ByVal ucchMax As Integer) As Integer
And the C# one
[DllImport("kernel32.dll")]
static extern uint QueryDosDevice(string lpDeviceName, IntPtr lpTargetPath, uint ucchMax);
Similar to ibram's answer with a few modifications:
public static String GetUNCPath(String path) {
path = path.TrimEnd('\\', '/') + Path.DirectorySeparatorChar;
DirectoryInfo d = new DirectoryInfo(path);
String root = d.Root.FullName.TrimEnd('\\');
if (!root.StartsWith(#"\\")) {
ManagementObject mo = new ManagementObject();
mo.Path = new ManagementPath(String.Format("Win32_LogicalDisk='{0}'", root));
// DriveType 4 = Network Drive
if (Convert.ToUInt32(mo["DriveType"]) == 4)
root = Convert.ToString(mo["ProviderName"]);
else
root = #"\\" + System.Net.Dns.GetHostName() + "\\" + root.TrimEnd(':') + "$\\";
}
return Recombine(root, d);
}
private static String Recombine(String root, DirectoryInfo d) {
Stack s = new Stack();
while (d.Parent != null) {
s.Push(d.Name);
d = d.Parent;
}
while (s.Count > 0) {
root = Path.Combine(root, (String) s.Pop());
}
return root;
}
Seems it's need a P/Invoke: Converting a mapped drive letter to a network path using C#
This guy built a managed class to deal with it: C# Map Network Drive (API)
You can also use WMI Win32_LogicalDisk to get all the information you need. use the ProviderName from the class to get the UNC path.
As far as Windows cares, what's needed is a call to WNetGetConnection. I don't know of a front-end for that in .NET, so you may have to call it via P/Invoke (fortunately, it has only one parameter, the P/Invoke code isn't too awful).
This post describe how to get the absolute path of a drive which is mapped to a local folder?
For example, I have a "c:\test" folder and an "x:" drive which is
mapped to c:\test.
I'm looking for a function which will return "c:\test" when I pass in
"x:"
The answer is:
SUBST uses DefineDosDevice (XP and later) to create the drive/path
mapping. You can use the QueryDosDevice to get the path of a SUBSTed
drive:
[DllImport("kernel32.dll")]
private static extern uint QueryDosDevice(string lpDeviceName, StringBuilder lpTargetPath, int ucchMax);
static String GetPhysicalPath(String path)
{
if (String.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}
// Get the drive letter
string pathRoot = Path.GetPathRoot(path);
if(String.IsNullOrEmpty(pathRoot))
{
throw new ArgumentNullException("path");
}
string lpDeviceName = pathRoot.Replace("\\", "");
const String substPrefix = #"\??\";
StringBuilder lpTargetPath = new StringBuilder(260);
if (0 != QueryDosDevice(lpDeviceName, lpTargetPath, lpTargetPath.Capacity))
{
string result;
// If drive is substed, the result will be in the format of "\??\C:\RealPath\".
if (lpTargetPath..ToString().StartsWith(substPrefix))
{
// Strip the \??\ prefix.
string root = lpTargetPath.ToString().Remove(0, substPrefix.Length);
result = Path.Combine(root, path.Replace(Path.GetPathRoot(path), ""));
}
else
{
// TODO: deal with other types of mappings.
// if not SUBSTed, just assume it's not mapped.
result = path;
}
return result;
}
else
{
// TODO: error reporting
return null;
}
}
Here is a solution that doesn't care if it is local or remote
private string uncpath_check(string path)
{
string rval = path;
string driveprefix = path.Substring(0, 2);
string unc;
if (driveprefix != "\\")
{
ManagementObject mo = new ManagementObject();
try
{
mo.Path = new ManagementPath(String.Format("Win32_LogicalDisk='{0}'", driveprefix));
unc = (string)mo["ProviderName"];
rval = path.Replace(driveprefix, unc);
}
catch
{
throw;
}
}
if (rval == null)
{ rval = path; }
return rval;
}