|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Visual Basic Win32 Shell Routines Undocumented Windows: Path Functions Shell32 Path Functions |
||
Posted: | Wednesday August 6, 1997 | |
Updated: | Monday December 26, 2011 | |
Applies to: | VB4-32, VB5, VB6 | |
Developed with: | VB4-32, Windows 95 | |
OS restrictions: | None | |
Author: | Brad Martinez | |
Related: |
SHChangeNotifyRegister: Receive Shell Change Notifications Undocumented Windows: Overview Undocumented Windows: Shell Dialogs Undocumented Windows: Format Disk Dialog Undocumented Windows: Change Icon Dialog |
|
Prerequisites |
None. |
|
The final undocumented installment shows a few of the many undocumented
Path functions. As undocumented APIs, they're not supported by Microsoft. More so than any of the others, these APIs may cause a GPF if care is not exercised ... most use the CopyMemory API to return pointers. And unlike most of the others demonstrated, these require several support functions to convert from Unicode to ANSI, and perform other housekeeping tasks. But the base routines are easily incorporated into a standalone BAS module for dropping into any project needing these treats. The two illustrations once again show the demo form both it appears in final and development modes, where the control names have been substituted for the captions. Note that the Drive, File and Directory list boxes have are named (Drive1, File1 and Dir1), and the pixbox is shown with a border to facilitate placement in the frame. Finally, again the arrangement makes no difference, and the control and form names indicated match the code below. Note: Many of the functions detailed here are now documented - see the FileApi listings under 'Paths'. |
BAS Module Code |
Once the form has been designed and saved, paste the following into the general declarations area of a file you name UndocSHPaths.bas: |
|
Option Explicit '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Copyright ©1996-2011 VBnet/Randy Birch, All Rights Reserved. ' Some pages may also contain other copyrights by the author. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Distribution: You can freely use this code in your own ' applications, but you may not reproduce ' or publish this code on any web site, ' online service, or distribute as source ' on any media without express permission. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '------------------------------------------------------ 'Set to True if the current OS is WinNT. Public bIsWinNT As Boolean Public Declare Function GetVersionEx Lib "kernel32" _ Alias "GetVersionExA" _ (lpVersionInformation As OSVERSIONINFO) As Long Public Type OSVERSIONINFO dwOSVersionInfoSize As Long dwMajorVersion As Long dwMinorVersion As Long dwBuildNumber As Long dwPlatformId As Long szCSDVersion As String * 128 End Type Public Const VER_PLATFORM_WIN32s = 0 Public Const VER_PLATFORM_WIN32_WINDOWS = 1 Public Const VER_PLATFORM_WIN32_NT = 2 Public Declare Function IsTextUnicode Lib "advapi32" _ (lpBuffer As Any, _ ByVal cb As Long, _ lpi As Long) As Long Public Const IS_TEXT_UNICODE_ASCII16 = &H1 Public Const IS_TEXT_UNICODE_REVERSE_ASCII16 = &H10 Public Const IS_TEXT_UNICODE_STATISTICS = &H2 Public Const IS_TEXT_UNICODE_REVERSE_STATISTICS = &H20 Public Const IS_TEXT_UNICODE_CONTROLS = &H4 Public Const IS_TEXT_UNICODE_REVERSE_CONTROLS = &H40 Public Const IS_TEXT_UNICODE_SIGNATURE = &H8 Public Const IS_TEXT_UNICODE_REVERSE_SIGNATURE = &H80 Public Const IS_TEXT_UNICODE_ILLEGAL_CHARS = &H100 Public Const IS_TEXT_UNICODE_ODD_LENGTH = &H200 Public Const IS_TEXT_UNICODE_DBCS_LEADBYTE = &H400 Public Const IS_TEXT_UNICODE_NULL_BYTES = &H1000 Public Const IS_TEXT_UNICODE_UNICODE_MASK = &HF Public Const IS_TEXT_UNICODE_REVERSE_MASK = &HF0 Public Const IS_TEXT_UNICODE_NOT_UNICODE_MASK = &HF00 Public Const IS_TEXT_UNICODE_NOT_ASCII_MASK = &HF000 Public Const MAX_PATH As Long = 260 '------------------------------------------------------ 'Handles overlapped source and destination blocks Public Declare Sub CopyMemory Lib "kernel32" _ Alias "RtlMoveMemory" _ (pDest As Any, _ pSource As Any, _ ByVal ByteLen As Long) Public Declare Function GetWindowsDirectory Lib "kernel32" _ Alias "GetWindowsDirectoryA" _ (ByVal lpBuffer As String, _ ByVal nSize As Long) As Long '------------------------------------------------------ 'Path functions (sorted by ordinal): '------------------------------------------------------ 'Rtns pointer to the last dot in szPath and the 'string following it (includes the dot with the extension). 'Rtns 0 if szPath contains no dot. 'For the function to succeed, szPath should be 'null terminated and be allocated to MAX_PATH bytes (260). 'Does not check szPath for validity. '(could be called "GetStrAtLastDot") Public Declare Function SHGetExtension Lib "shell32" _ Alias "#31" _ (ByVal szPath As String) As Long '------------------------------------------------------ 'Inserts a backslash before the first null char in szPath. 'szPath is unchanged if it already contains a backslash 'before the first null char or contains no null char at all. 'Rtn pointer to? 'Does not check szPath for validity. '(the name almost fits...) Public Declare Function SHAddBackslash Lib "shell32" _ Alias "#32" _ (ByVal szPath As String) As Long '------------------------------------------------------ 'Rtn a pointer to the string in szPath after the last backslash. 'Rtns 0 if szPath contains no backslash or no char follows the 'last backslash. For the function to succeed, szPath should 'be null terminated and be allocated to MAX_PATH bytes (260). 'Does not check szPath for validity. '(could be called "GetStrAfterLastBackslash") Public Declare Function SHGetFileName Lib "shell32" _ Alias "#34" _ (ByVal szPath As String) As Long '------------------------------------------------------ 'Fills szPath w/ the path portion of the passed path & filename. 'szPath must be a valid absolute path. 'For the function to work correctly, szPath should be 'null terminated and be allocated to MAX_PATH bytes (260). '(could be called "GetStrBeforeLastBackslash") Public Declare Function SHGetPath Lib "shell32" _ Alias "#35" _ (ByVal szPath As String) As Long '------------------------------------------------------ 'Rtns non-zero if szPath does not evaluate to a UNC path. '(if either the first char is not a backslash "\" 'or the 2nd char is not a colon ":") 'Does not check szPath for validity. '(the name almost fits...) Public Declare Function SHPathIsRelative Lib "shell32" _ Alias "#40" _ (ByVal szPath As String) As Long '------------------------------------------------------ 'Rtns non-zero if szPath has an executable extension. '(if last 4 char are either ".exe", ".com", '".bat" or ".pif") 'Does not check szPath for validity. '(could be called "HasExeExtension") Public Declare Function SHPathIsExe Lib "shell32" _ Alias "#43" _ (ByVal szPath As String) As Long '------------------------------------------------------ 'Rtns non-zero if szPath is valid absolute UNC path. 'Accepts file, folder or network paths. 'Rtns True for a relative path only if it exists 'in the curdir. (the name actually fits...) Public Declare Function SHFileExists Lib "shell32" _ Alias "#45" _ (ByVal szPath As String) As Long '------------------------------------------------------ 'Rtns a pointer to the string after first space in szPath. 'Rtns null pointer if szPath contains no space or no char 'following the first space. 'For the function to succeed, szPath should be 'null terminated and be allocated to MAX_PATH bytes (260). 'Does not check szPath for validity. '(could be called "GetStrAfterFirstSpace") Public Declare Function SHGetPathArgs Lib "shell32" _ Alias "#52" _ (ByVal szPath As String) As Long '------------------------------------------------------ 'Rtns a pointer to the string after first space in szPath. 'Rtns null pointer if szPath contains no space or no char 'following the first space. 'For the function to succeed, szPath should be 'null terminated and be allocated to MAX_PATH bytes (260). 'Does not check szPath for validity. '(could be called "AddQuotesIfPathHasASpace") Public Declare Function SHAddPathQuotes Lib "shell32" _ Alias "#55" _ (ByVal szPath As String) As Long '------------------------------------------------------ 'Rtns a pointer to the string after first space in szPath. 'Rtns null pointer if szPath contains no space or no char 'following the first space. 'For the function to succeed, szPath should be null terminated 'and be allocated to MAX_PATH bytes (260). 'Does not check szPath for validity. '(could be called "RemoveQuotesIfPathHasASpace") Public Declare Function SHRemovePathQuotes Lib "shell32" _ Alias "#56" _ (ByVal szPath As String) As Long '------------------------------------------------------ 'Fills szPath w/its DOS (8.3) file system string. 'If successful, rtns non-zero (sometimes is a 'pointer to szPath, sometimes not!). Rtns zero 'if path is invalid. szPath must be a valid absolute 'path. Rtns non-zero for a relative path only if 'it exists in the curdir. For the function to work 'correctly, szPath should be null terminated 'and be allocated to MAX_PATH bytes (260). '(the name definitely fits...) Public Declare Function SHGetShortPathName Lib "shell32" _ Alias "#92" _ (ByVal szPath As String) As Long '-------------------------- 'Wrappers for Path functions (see respective 'API description above): '-------------------------- Public Function FileExists(sPath As String) As Boolean 'convert to Unicode if required sPath = CheckString(sPath) 'call the API FileExists = SHFileExists(sPath) End Function Public Function GetFileName(sPathIn As String) As String Dim sPathOut As String 'pad the string with nulls sPathOut = MakeMaxPath(sPathIn) 'convert to Unicode if required sPathOut = CheckString(sPathOut) GetFileName = GetStrFromPtr(SHGetFileName(sPathOut), MAX_PATH) End Function Public Function GetArgs(sPathIn As String) As String Dim sPathOut As String sPathOut = MakeMaxPath(sPathIn) sPathOut = CheckString(sPathOut) 'return the passed arguments GetArgs = GetStrFromPtr(SHGetPathArgs(sPathOut), Len(sPathOut)) End Function Public Function GetShortPath(sPathIn As String) As String Dim sPathOut As String sPathOut = MakeMaxPath(sPathIn) sPathOut = CheckString(sPathOut) SHGetShortPathName sPathOut GetShortPath = GetStrFromBuffer(sPathOut) End Function Public Function IsPathRelative(sPath As String) As Boolean sPath = CheckString(sPath) 'return true or false IsPathRelative = SHPathIsRelative(sPath) End Function Public Function QualifyPath(sPath As String) As String 'check the string type (ANSI or Unicode), 'converting as required. 'Check with If .. Then for a slash; if it's 'needed, add a trailing null string after the 'Checked string, as SHAddBackslash inserts a 'backslash before the first null char in szPath. sPath = CheckString(sPath) If Right$(sPath, 1) <> "\" Then 'do what is says sPath = sPath & vbNullChar SHAddBackslash sPath End If 'and return the string QualifyPath = sPath End Function Public Function MakeMaxPath(ByVal sPath As String) As String 'Terminates sPath w/ null chars making 'the return string MAX_PATH chars long. MakeMaxPath = sPath & String$(MAX_PATH - Len(sPath), 0) End Function Public Function IsPathExe(sPath As String) As Boolean sPath = CheckString(sPath) 'return true or false IsPathExe = SHPathIsExe(sPath) End Function Public Function GetExtension(sPathIn) As String Dim sPathOut As String sPathOut = MakeMaxPath(sPathIn) sPathOut = CheckString(sPathOut) 'Does not fill sPathOut w/ extention, just 'returns the pointer to the extention GetExtension = GetStrFromPtr(SHGetExtension(sPathOut), Len(sPathOut)) End Function Public Function StripFileName(sPathIn) Dim sPathOut As String sPathOut = MakeMaxPath(sPathIn) sPathOut = CheckString(sPathOut) SHGetPath sPathOut StripFileName = GetStrFromBuffer(sPathOut) End Function Public Function PathAddQuotes(sPathIn) As String Dim sPathOut As String sPathOut = MakeMaxPath(sPathIn) sPathOut = CheckString(sPathOut) SHAddPathQuotes sPathOut PathAddQuotes = GetStrFromBuffer(sPathOut) End Function Public Function PathRemoveQuotes(sPathIn) As String Dim sPathOut As String sPathOut = MakeMaxPath(sPathIn) sPathOut = CheckString(sPathOut) SHRemovePathQuotes sPathOut PathRemoveQuotes = GetStrFromBuffer(sPathOut) End Function Public Function IsWinNT() As Boolean 'Returns True if the current operating system is WinNT Dim osvi As OSVERSIONINFO osvi.dwOSVersionInfoSize = Len(osvi) GetVersionEx osvi IsWinNT = (osvi.dwPlatformId = VER_PLATFORM_WIN32_NT) End Function Public Function CheckString(msg As String) As String If bIsWinNT Then CheckString = StrConv(msg, vbUnicode) Else CheckString = msg End If End Function Public Function GetStrFromPtr(lpszStr As Long, nBytes As Integer) As String 'Returns string before first null char 'encountered (if any) from a string pointer. 'lpszStr = memory address of first byte in string 'nBytes = number of bytes to copy. 'StrConv used for both ANSII and Unicode strings 'BE CAREFUL! ReDim ab(nBytes) As Byte 'zero-based (nBytes + 1 elements) CopyMemory ab(0), ByVal lpszStr, nBytes GetStrFromPtr = GetStrFromBuffer(StrConv(ab(), vbUnicode)) End Function Public Function GetStrFromBuffer(szStr As String) As String 'Returns string before first null 'char encountered (if any) from either an ANSII or 'Unicode string buffer. If IsUnicodeStr(szStr) Then szStr = StrConv(szStr, vbFromUnicode) If InStr(szStr, vbNullChar) Then GetStrFromBuffer = Left$(szStr, InStr(szStr, vbNullChar) - 1) Else GetStrFromBuffer = szStr End If End Function Public Function IsUnicodeStr(sBuffer As String) As Boolean 'Returns True if sBuffer evaluates to 'a Unicode string Dim dwRtnFlags As Long dwRtnFlags = IS_TEXT_UNICODE_UNICODE_MASK IsUnicodeStr = IsTextUnicode(ByVal sBuffer, Len(sBuffer), dwRtnFlags) End Function |
Form Code |
Add a new form containing a picture box (Picture1), a Dir list (Dir1), File list (File1) and Drive list (Drive1), along with a textbox (Text1) and command button Command1). Add the following code: |
|
Option Explicit Private sInitDirectory As String Private sInitDrive As String Private Sub Command1_Click() Unload Me End Sub Private Sub Dir1_Change() Dir1.Path = Drive1.Drive File1 = Dir1.Path Text1.Text = Dir1.Path End Sub Private Sub Drive1_Change() Dir1.Path = Drive1.Drive End Sub Private Sub File1_Click() Text1.Text = QualifyPath(File1.Path) & LCase$(File1.FileName) Call DoPathFunctions End Sub Private Sub Form_Load() Dim r As Long Dim sDirBuff As String 'We'll need this flag to determine if 'strings should be converted to Unicode bIsWinNT = IsWinNT Move (Screen.Width - Width) * 0.5, (Screen.Height - Height) * 0.5 'we'll be moving around a bit, so save 'the current directory into a variable 'for reseting on exit. sInitDirectory = CurDir sInitDrive = Left$(sInitDirectory, 1) sDirBuff = Space$(MAX_PATH) r = GetWindowsDirectory(sDirBuff, MAX_PATH) If r > 0 Then Drive1.Drive = LCase$(Left$(sDirBuff, 3)) 'ie "c:\" Dir1.Path = LCase$(GetStrFromBuffer(sDirBuff))'ie "c:\windows" End If End Sub Private Sub DoPathFunctions() Dim sTx As String Dim sOut As String 'covers any invalid path selection On Error GoTo DoPathFunctions_Error 'Make the displayed path the current drive and 'dir so that FileExists evaluates relative paths ChDrive Drive1 ChDir Dir1 On Error GoTo 0 sTx = Text1.Text sOut = "VB CurDir: " & vbTab & Dir1 & vbCrLf sOut = sOut & "SHGetPath:" & vbTab & StripFileName(sTx) & vbCrLf & vbCrLf sOut = sOut & "FileExists:" & vbTab & FileExists(sTx) & vbCrLf sOut = sOut & "GetExtension:" & vbTab & GetExtension(sTx) & vbCrLf sOut = sOut & "GetFileName:" & vbTab & GetFileName(sTx) & vbCrLf sOut = sOut & "IsPathRelative:" & vbTab & IsPathRelative(sTx) & vbCrLf sOut = sOut & "IsPathExe:" & vbTab & IsPathExe(sTx) & vbCrLf sOut = sOut & "GetArgs: " & vbTab & GetArgs(sTx) & vbCrLf sOut = sOut & "GetShortPath:" & vbTab & GetShortPath(sTx) & vbCrLf sOut = sOut & "PQSpaces:" & vbTab & PathAddQuotes(sTx) & vbCrLf sOut = sOut & "PUQSpaces:" & vbTab & PathRemoveQuotes(sTx) picPaths.AutoRedraw = True picPaths.Cls picPaths.Print sOut picPaths.AutoRedraw = False DoPathFunctions_Error: If Err Then MsgBox Err.Description End Sub Private Sub Form_Unload(Cancel As Integer) 'Restore the initial startup drive & 'directory (cached at app start) Dim sCurrDirectory As String Dim sCurrDrive As String sCurrDirectory = CurDir sCurrDrive = Left$(sInitDirectory, 1) If sCurrDrive <> sInitDrive Then ChDrive sInitDrive If sCurrDirectory <> sInitDirectory Then ChDir sInitDirectory End Sub |
Comments |
Save the project before running. Navigate around clicking
files, noting how each API reacts.
To test the GetArgs function, try entering a path/filename with command line arguments (i.e. c:\win\someapp.exe /b /s /-r test.text") & vbCrLf into the textbox, and then hit Execute. Notice that if the path or filename has a space, then the args begin after the space, even if this is in the middle of the path, not the space before the args themselves (see the demo illustration above. The file selected is _New Text.txt, but the API returns "Text.txt" as arguments because of the space. |
|
|
|
|||||
|
|||||
|
|||||
Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved. |