Archive: Converting a mapped drive to a UNC path


Converting a mapped drive to a UNC path
Hello!

I've been running into clients with network setups who have been trying to install onto mapped network drives. This can cause issues, mainly because mapped drives are associated with User logons and can be different depending upon who is logged into a machine. Moreover, services do not obtain mapped drive information, even when given rights to "Log on as:" a particular user.

UNC paths, however, do not exhibit this problem. Thus, I wrote a little function to take any windows full path and, if the path uses a mapped drive, convert it to a UNC path.

Enjoy!!


#
# GetUniversalName.nsh - by HotButteredSoul
#
# Function for getting the Universal name of a filepath.
#

!ifndef _GetUniversalName_nsh
!define _GetUniversalName_nsh

#
# get_universal_name - gets universal name of a filepath
#
# Example:
#
# Push "H:\path\file"
# Call get_universal_name
# Pop $0 ; = "\\servername\sharename\path\file"
#
# Uses mpr.lib WNetGetUniversalNameA
#
# DWORD WNetGetUniversalName(
# LPCTSTR lpLocalPath, // pointer to drive-based path for a network
# // resource
# DWORD dwInfoLevel, // specifies form of universal name to be
# // obtained
# LPVOID lpBuffer, // pointer to buffer that receives universal
# // name data structure
# LPDWORD lpBufferSize // pointer to variable that specifies size
# // of buffer
# );
#
# Parameters
# lpLocalPath
# Points to a null-terminated string that is a drive-based path for a network resource.
# For example, if drive H has been mapped to a network drive share, and the network
# resource of interest is a file named SAMPLE.DOC in the directory \WIN32\EXAMPLES on
# that share, the drive-based path is H:\WIN32\EXAMPLES\SAMPLE.DOC.
#
# dwInfoLevel
# Specifies the type of data structure that the function will store in the buffer pointed
# to by lpBuffer. This parameter can be one of the following values: Value Meaning
# UNIVERSAL_NAME_INFO_LEVEL The function will store a UNIVERSAL_NAME_INFO data
# structure in the buffer. (defined in WINNETWK.H as 1)
# REMOTE_NAME_INFO_LEVEL The function will store a REMOTE_NAME_INFO data structure in
# the buffer. (defined in WINNETWK.H as 2)
#
# The UNIVERSAL_NAME_INFO data structure points to a Universal Naming Convention (UNC)
# name string.
#
# lpBuffer
# Points to a buffer that receives the type of data structure specified by the
# dwInfoLevel parameter.
#
# lpBufferSize
# Points to a variable that specifies the size in bytes of the buffer pointed to by lpBuffer.
# If the function succeeds, it sets the variable pointed to by lpBufferSize to the size
# in bytes of the data structure stored in the buffer. If the function fails because the
# buffer is too small, indicated by the ERROR_MORE_DATA error code, it sets the variable
# pointed to by lpBufferSize to the required buffer size.
#
# Return Values
# If the function succeeds, the return value is NO_ERROR (0).
#
# If the function fails, the return value is an error code. To get extended error
# information, callGetLastError. GetLastError may return one of the following error codes:
#
# Value Meaning
# ERROR_BAD_DEVICE The string pointed to by lpLocalPath is invalid (1200).
# ERROR_CONNECTION_UNAVAIL There is no current connection to the remote device, but there
# is a remembered (persistent) connection to it (1201).
# ERROR_EXTENDED_ERROR A network-specific error occurred. Use the WNetGetLastError
# function to obtain a description of the error (1208).
# ERROR_MORE_DATA The buffer pointed to by lpBuffer is too small. The function sets the
# variable pointed to by lpBufferSize to the required buffer size. More entries are
# available with subsequent calls (234).
# ERROR_NOT_SUPPORTED The dwInfoLevel parameter was set to UNIVERSAL_NAME_INFO_LEVEL, but
# the network provider does not support UNC names. This function is not supported by
# any of the network providers (282).
# ERROR_NO_NET_OR_BAD_PATH None of the providers recognized this local name as having a
# connection. However, the network is not available for at least one provider to whom
# the connection may belong (1203).
# ERROR_NO_NETWORK There is no network present (1222).
# ERROR_NOT_CONNECTED The device specified by lpLocalPath is not redirected (2250).
#
Function get_universal_name
Exch $0 ; local path (IN)
Push $1 ; Universal Path (OUT)
Push $2 ; lpBuffer
Push $3 ; lpBufferSize
Push $4 ; GetUniversalName return value

;allocate buffer for following structure:
; typedef struct _UNIVERSAL_NAME_INFO { /* uni */
; LPTSTR lpUniversalName;
; } UNIVERSAL_NAME_INFO;
System::Call "*(&t${NSIS_MAX_STRLEN} '') i .r2"

; make call
System::Call 'mpr::WNetGetUniversalNameA(t r0, i 1,i $2, *i ${NSIS_MAX_STRLEN} r3)i .r4'
IntCmp 0 $4 success
StrCpy $1 $0 ; failed, so just use local path
GoTo clean_up

success:
System::Call "*$2(t .r1)" ; Extract universal string

clean_up:
System::Free $2
Pop $4
Pop $3
Pop $2
Exch
Pop $0
Exch $1

FunctionEnd
!endif ; _GetUniversalName_nsh

The UNIVERSAL_NAME_INFO structure contains only a pointer to a string. There is no need to allocate 1024 bytes for it, only 4. Call "*(i)" should be good enough. You can use 't' without the ampersand to make it clear the allocation is for a string pointer.

You should create a Wiki page for useful scripts so they won't get lost in the forums.


You would think so!

However, what Microsoft doesn't tell you (though they imply it in their example) is that lpBuffer not only contains the UNIVERSAL_NAME_INFO structure, it ALSO contains the string that the structure points to!!

Thus, for a 14 byte path like "\\loki\loki_c" you need 18 bytes in your lpBuffer.

Hence, my usage of NSIS_MAX_STRLEN. Note, my code is a little misleading... because of the 4 byte size of the structure the maximum length UNC path my function will return is NSIS_MAX_STRLEN-4 = 1020.

A sidenote: if you don't give WNetGetUniversalNameA a big enough buffer, it returns the buffer size it does need in lpBufferSize. However, even this non-unicode version asks for a unicode size buffer!! Thus, for "\\loki\loki_c\pub" it would ask for 32 (14*2 + 4) bytes, even though it works with 18 (but set it to 17 and it asks for 32!! lol).

Don't you just love Micro$oft?? :rolleyes:


So they expect everyone to always query for the required size and make no note of it in the documentation? That's one of the weirdest APIs I've ever seen. Why not LPSTR lpUniversalName[1] as always?


Yes, it's wacky. They actually expect you to guess a size and use it, then only do something if it fails... which, btw, I'm not doing in my script.


Wiki'd

Http://nsis.sourceforge.net/Get_Universal_Name


http://nsis.sourceforge.net/Get_Universal_Name


Hallo,
how can i download the GetUniversalName_nsh?


Originally posted by Calicebus
Hallo,
how can i download the GetUniversalName_nsh?
The code is right there on the wiki page...