Recently, a colleague here at DMC was looking for a quick method of converting a drive-based path to its corresponding universal path name. Basically, he is writing a tool in LabVIEW that will run on computers that may have the same network drive mapped to different drive letters.
After a quick search, I found that Windows has a networking function that does exactly what I'm looking for! The function is called WNetGetUniversalName, and it resides in mpr.dll.
In order to easily call this function from LabVIEW, I decided to first wrap it in a .NET class. It is rudimentary to load .NET libraries into LabVIEW, but we'll get to that in a second.
While I was searching on WNetGetUniversalName, I ran across some .NET code that had already been written for me! Pinvoke.net already had an implementation that wraps WNetGetUniversalName and adds some error handling as well. I threw this into my own .NET class, and also made a small bug fix that was mentioned by one of the comments on pinvoke.net's site. Here is the source:
public class PathConverter
{
[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 NOERROR = 0;
public string GetUniversalName(string localPath)
{
// The return value.
string retVal = null;
// The pointer in memory to the structure.
IntPtr buffer = IntPtr.Zero;
// Wrap in a try/catch block for cleanup.
try
{
// First, call WNetGetUniversalName to get the size.
int size = 0;
// Make the call.
// Pass IntPtr.Size because the API doesn't like null, even though
// size is zero. We know that IntPtr.Size will be
// aligned correctly.
int apiRetVal = WNetGetUniversalName(localPath, UNIVERSAL_NAME_INFO_LEVEL, (IntPtr)IntPtr.Size, ref size);
// If the return value is not ERROR_MORE_DATA, then
// raise an exception.
if (apiRetVal != ERROR_MORE_DATA)
// Throw an exception.
throw new Win32Exception(apiRetVal);
// Allocate the memory.
buffer = Marshal.AllocCoTaskMem(size);
// Now make the call.
apiRetVal = WNetGetUniversalName(localPath, UNIVERSAL_NAME_INFO_LEVEL, buffer, ref size);
// If it didn't succeed, then throw.
if (apiRetVal != NOERROR)
// Throw an exception.
throw new Win32Exception(apiRetVal);
// Now get the string. It's all in the same buffer, but
// the pointer is first, so offset the pointer by IntPtr.Size
// and pass to PtrToStringAnsi.
retVal = Marshal.PtrToStringAnsi(new IntPtr(buffer.ToInt64() + IntPtr.Size));
}
finally
{
// Release the buffer.
Marshal.FreeCoTaskMem(buffer);
}
// First, allocate the memory for the structure.
// That's all folks.
return retVal;
}
}
Now all that's left is to call this .NET function from LabVIEW. The code is shown below. The first step is to call the "Constructor Node" VI. This will load a new instance of the .NET class into memory. Next, call the "Invoke Node" VI and choose the function that we've created above (I named it GetUniversalName). LabVIEW will automatically detect the data types and give you the inputs/outputs that you need. Make sure to close the .NET reference when you're done to free up resources!
Learn more about DMC's LabVIEW programming expertise.