Feb 232010
I don’t really consider myself a C# programmer yet, but I recently had a need to figure out how to do this in C#. As it turns out I couldn’t find a handy built in function so I Binged for a solution. Unfortunately, I only found parts of solutions in different places. So, I compiled all of them in to the following code. Maybe some of this will help someone else with the same need. This code is similar to what I do in C++ and I think it is pretty safe, but make sure you test it in your application.
using System.Runtime.InteropServices;
using System.Management;
[DllImport("mpr.dll")]
[return: MarshalAs(UnmanagedType.U4)]
static extern int WNetGetUniversalName(
string lpLocalPath,
[MarshalAs(UnmanagedType.U4)] int dwInfoLevel,
IntPtr lpBuffer,
[MarshalAs(UnmanagedType.U4)] ref int lpBufferSize);
const int UNIVERSAL_NAME_INFO_LEVEL = 0x00000001;
const int REMOTE_NAME_INFO_LEVEL = 0x00000002;
const int ERROR_MORE_DATA = 234;
const int ERROR_NOT_CONNECTED = 2250;
const int ERROR_NO_NETWORK = 1222;
const int NOERROR = 0;
static public String GetUniversalName(String _path)
{
// The return value.
String UNCPath = null;
// Already a UNC, no need to convert
if (_path.StartsWith(@"\") || _path.StartsWith(@"//"))
return _path;
// WNetGetUniversalName does not allow a null buffer
IntPtr buf = Marshal.AllocCoTaskMem(5);
int size = 0;
try
{
// First call to WNetGetUniversalName to get the buffer size
int ret = WNetGetUniversalName(_path, UNIVERSAL_NAME_INFO_LEVEL, buf, ref size);
Marshal.FreeCoTaskMem(buf);
// Local Drive
if (ret == ERROR_NOT_CONNECTED || ret == ERROR_NO_NETWORK)
return GetLocalUniversalName(_path);
// If the return is not ERROR_MORE_DATA then something went wrong and the
// conversion didn't work so return the original path
if (ret != ERROR_MORE_DATA)
return _path;
}
catch (Exception ex)
{
String msg = String.Format("{0}: {1}",ex.Message, _path);
log.Error(msg);
return _path; // Something blew so just return the original path
}
buf = Marshal.AllocCoTaskMem(size);
try
{
// Look up the name of the share for the mapped drive
int ret = WNetGetUniversalName(_path, UNIVERSAL_NAME_INFO_LEVEL, buf, ref size);
// If it didn't convert just return the original path
if (ret != NOERROR)
{
// Release the temp buffer
Marshal.FreeCoTaskMem(buf);
return _path;
}
// Now convert the result to a string we can actually use
// It's all in the same buffer, but the pointer is first,
// so offset the pointer by IntPtr.Size and pass to PtrToStringAnsi.
UNCPath = Marshal.PtrToStringAnsi(new IntPtr(buf.ToInt64() + IntPtr.Size), size);
// Clean up stuff at the end of UNCPath
int actualLength = UNCPath.IndexOf('\0');
if (actualLength >= 0)
{
UNCPath = UNCPath.Substring(0, actualLength);
}
// Release the temp buffer
Marshal.FreeCoTaskMem(buf);
return UNCPath;
}
catch (Exception ex)
{
String msg = String.Format("{0}: {1}", ex.Message, _path);
log.Error(msg);
return _path;
}
}
// Get a UNC for a local share
static public String GetLocalUniversalName(String _path)
{
// Already a UNC, no need to convert
if (_path.StartsWith(@"\") || _path.StartsWith(@"//"))
return _path;
String UNCPath = _path;
ManagementClass exportedShares = new ManagementClass("Win32_Share");
ManagementObjectCollection shares = exportedShares.GetInstances();
foreach (ManagementObject share in shares)
{
String name = share["Name"].ToString();
String path = share["Path"].ToString();
String caption = share["Caption"].ToString();
if (!name.Contains("$") && _path.StartsWith(path))
{
String rest = _path.Substring(path.Length);
UNCPath = String.Format(@"<a href="file://\\{0}\{1}\{2">\\{0}\{1}\{2</a>}", Environment.MachineName, name, rest);
}
}
return UNCPath;
}
using System.Management;
[DllImport("mpr.dll")]
[return: MarshalAs(UnmanagedType.U4)]
static extern int WNetGetUniversalName(
string lpLocalPath,
[MarshalAs(UnmanagedType.U4)] int dwInfoLevel,
IntPtr lpBuffer,
[MarshalAs(UnmanagedType.U4)] ref int lpBufferSize);
const int UNIVERSAL_NAME_INFO_LEVEL = 0x00000001;
const int REMOTE_NAME_INFO_LEVEL = 0x00000002;
const int ERROR_MORE_DATA = 234;
const int ERROR_NOT_CONNECTED = 2250;
const int ERROR_NO_NETWORK = 1222;
const int NOERROR = 0;
static public String GetUniversalName(String _path)
{
// The return value.
String UNCPath = null;
// Already a UNC, no need to convert
if (_path.StartsWith(@"\") || _path.StartsWith(@"//"))
return _path;
// WNetGetUniversalName does not allow a null buffer
IntPtr buf = Marshal.AllocCoTaskMem(5);
int size = 0;
try
{
// First call to WNetGetUniversalName to get the buffer size
int ret = WNetGetUniversalName(_path, UNIVERSAL_NAME_INFO_LEVEL, buf, ref size);
Marshal.FreeCoTaskMem(buf);
// Local Drive
if (ret == ERROR_NOT_CONNECTED || ret == ERROR_NO_NETWORK)
return GetLocalUniversalName(_path);
// If the return is not ERROR_MORE_DATA then something went wrong and the
// conversion didn't work so return the original path
if (ret != ERROR_MORE_DATA)
return _path;
}
catch (Exception ex)
{
String msg = String.Format("{0}: {1}",ex.Message, _path);
log.Error(msg);
return _path; // Something blew so just return the original path
}
buf = Marshal.AllocCoTaskMem(size);
try
{
// Look up the name of the share for the mapped drive
int ret = WNetGetUniversalName(_path, UNIVERSAL_NAME_INFO_LEVEL, buf, ref size);
// If it didn't convert just return the original path
if (ret != NOERROR)
{
// Release the temp buffer
Marshal.FreeCoTaskMem(buf);
return _path;
}
// Now convert the result to a string we can actually use
// It's all in the same buffer, but the pointer is first,
// so offset the pointer by IntPtr.Size and pass to PtrToStringAnsi.
UNCPath = Marshal.PtrToStringAnsi(new IntPtr(buf.ToInt64() + IntPtr.Size), size);
// Clean up stuff at the end of UNCPath
int actualLength = UNCPath.IndexOf('\0');
if (actualLength >= 0)
{
UNCPath = UNCPath.Substring(0, actualLength);
}
// Release the temp buffer
Marshal.FreeCoTaskMem(buf);
return UNCPath;
}
catch (Exception ex)
{
String msg = String.Format("{0}: {1}", ex.Message, _path);
log.Error(msg);
return _path;
}
}
// Get a UNC for a local share
static public String GetLocalUniversalName(String _path)
{
// Already a UNC, no need to convert
if (_path.StartsWith(@"\") || _path.StartsWith(@"//"))
return _path;
String UNCPath = _path;
ManagementClass exportedShares = new ManagementClass("Win32_Share");
ManagementObjectCollection shares = exportedShares.GetInstances();
foreach (ManagementObject share in shares)
{
String name = share["Name"].ToString();
String path = share["Path"].ToString();
String caption = share["Caption"].ToString();
if (!name.Contains("$") && _path.StartsWith(path))
{
String rest = _path.Substring(path.Length);
UNCPath = String.Format(@"<a href="file://\\{0}\{1}\{2">\\{0}\{1}\{2</a>}", Environment.MachineName, name, rest);
}
}
return UNCPath;
}