Mbee Posted April 25, 2018 Share Posted April 25, 2018 I'm writing up the interface code for _WinAPI_GetVolumePathNamesForVolumeName(). Here's a link to it's MSDN page: GetVolumePathNamesForVolumeName The primary return from that function is array/string of substrings that are each zero-terminated, and the entire thing is terminated by double zero. It would be easy to separate the substrings with built-in AutoIt functions, except I can't imagine that such a multi-string will survive the first operation; I think the first time I try to use that multi-string, it will be truncated at the first zero. And that first operation, to my limited knowledge, will be when I try to extract the multi-string from the DLL return structure. Here's what I coded for the interface function... Local $Lf_RetStruct = DllCall('kernel32.dll', 'bool', 'GetVolumePathNamesForVolumeNameW', 'wstr', $Lf_VolGUIDStr, _ 'wstr', $Lf_VolNamesBufAra, 'dword', 255, 'dword', $Lf_VolNamesBufAraLen ) Maybe if I change that second 'wstr' into something else I could treat it as a character or byte array? But I don't know enough to know what to replace it with, or how to avoid truncation at the first zero. Of course, I'm open to any suggestion for handling multi-strings... Thanks! Link to comment Share on other sites More sharing options...
Mbee Posted April 25, 2018 Author Share Posted April 25, 2018 'WCHAR[255]' instead of 'wstr' maybe? Link to comment Share on other sites More sharing options...
Bilgus Posted April 25, 2018 Share Posted April 25, 2018 (edited) Those single null separated double null terminated strings have always been a pain Spoiler Old Code: #include <WinAPIFiles.au3> Global $sMountedPath = "c:\" Global $sGuid = _WinAPI_GetVolumeNameForVolumeMountPoint($sMountedPath) ConsoleWrite("Vol Guid: " & $sGuid & @CRLF) ConsoleWrite("Mount Point(s): " & __WinAPI_GetVolumePathNamesForVolumeName($sGuid) & @CRLF) Func __WinAPI_GetVolumePathNamesForVolumeName($sVolumeGUID) Local $tVolNames, $tSize = DllStructCreate('int iSize') ;First call to get size of buffer req'd Local $aRet = DllCall('kernel32.dll', 'bool', 'GetVolumePathNamesForVolumeNameW', 'wstr', $sVolumeGUID, 'ptr', 0, 'dword', 0, 'ptr', DllStructGetPtr($tSize)) $tVolNames = DllStructCreate("byte [" & DllStructGetData($tSize, 1) * 2 & "]") ;Multiply by 2 since TChars are 2 bytes each as far as Autoit is concerned ;Second call gets the actual data $aRet = DllCall('kernel32.dll', 'bool', 'GetVolumePathNamesForVolumeNameW', 'wstr', $sVolumeGUID, _ 'ptr', DllStructGetPtr($tVolNames), 'dword', DllStructGetSize($tVolNames), 'ptr', DllStructGetPtr($tSize)) If @error Or Not $aRet[0] Then Return SetError(@error + 10, @extended, '') ;Step through the data turn the nulls into invalid unicode char (0xFFFD) @jchd For $i = 1 To DllStructGetSize($tVolNames) Step 2 ;skip every other byte ;(Pointers (offset) is zero based where as DllStruct indices are 1-based) $tChar = DllStructCreate("ushort", DllStructGetPtr($tVolNames) + $i - 1) ; Load 2 bytes into struct (8 bits * 2 = 16 bits = ushort) If DllStructGetData($tChar, 1) = 0x0000 Then DllStructSetData($tChar, 1, 0xFFFD) Next Return StringTrimRight(BinaryToString(DllStructGetData($tVolNames, 1), $SB_UTF16LE), 2) ;discard last 2 (',,') EndFunc ;==>__WinAPI_GetVolumePathNamesForVolumeName #include <WinAPIFiles.au3> Global $sMountedPath = "c:\" ; Don't forget to end with final '\' Global $sGuid = _WinAPI_GetVolumeNameForVolumeMountPoint($sMountedPath) ConsoleWrite("Vol Guid: " & $sGuid & @CRLF) ConsoleWrite("Mount Point(s): " & __WinAPI_GetVolumePathNamesForVolumeName($sGuid) & @CRLF) Func __WinAPI_GetVolumePathNamesForVolumeName($sVolumeGUID) Local $tVolNames, $tSize = DllStructCreate('int iSize') ;First call to get size of buffer req'd Local $aRet = DllCall('kernel32.dll', 'bool', 'GetVolumePathNamesForVolumeNameW', 'wstr', $sVolumeGUID, 'ptr', 0, 'dword', 0, 'ptr', DllStructGetPtr($tSize)) $tVolNames = DllStructCreate("wchar [" & DllStructGetData($tSize, 1) & "]") ;Second call gets the actual data $aRet = DllCall('kernel32.dll', 'bool', 'GetVolumePathNamesForVolumeNameW', 'wstr', $sVolumeGUID, _ 'ptr', DllStructGetPtr($tVolNames), 'dword', DllStructGetSize($tVolNames), 'ptr', DllStructGetPtr($tSize)) If @error Or Not $aRet[0] Then Return SetError(@error + 10, @extended, '') ;Step through the data turn the nulls into invalid unicode char (0xFFFD) @jchd For $i = 1 To (DllStructGetSize($tVolNames)/2) - 2 ;each character occupies 2 bytes and we ignore the last two characters If DllStructGetData($tVolNames, 1, $i) = CHRW(0x0000) Then DllStructSetData($tVolNames, 1, CHRW(0xFFFD), $i) Next Return DllStructGetData($tVolNames, 1) EndFunc ;==>__WinAPI_GetVolumePathNamesForVolumeName There are probably other ways to do this but its what I came up with off the top of my head see here: https://docs.microsoft.com/en-us/windows-server/storage/disk-management/assign-a-mount-point-folder-path-to-a-drive it'll show you how to add multiple mount point folders to a drive so you can test it Edited April 25, 2018 by Bilgus Cleaned code up a bit Mbee 1 Link to comment Share on other sites More sharing options...
jchd Posted April 25, 2018 Share Posted April 25, 2018 55 minutes ago, Bilgus said: ;Step through the data turn the nulls into ',' (0x2c00) That could be a problem if the multi_sz may contain comma(s). Choose another separator character that you should never encounter, like ChrW(0xFFFD) (Not a Character). And yes those multi_sz are painful and don't even accomodate empty strings if not the first and only one. Mbee and Bilgus 2 This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe hereRegExp tutorial: enough to get startedPCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta. SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt) Link to comment Share on other sites More sharing options...
Bilgus Posted April 25, 2018 Share Posted April 25, 2018 The thought of commas crossed my mind other possibilities include just about any other restricted character < (less than) > (greater than) " (double quote) / (forward slash) | (vertical bar or pipe) ? (question mark) * (asterisk) Link to comment Share on other sites More sharing options...
jchd Posted April 25, 2018 Share Posted April 25, 2018 Depending on the use of strings and their semantics, all those characters may occur. The Unicode "invalid character" is essentially guaranteed to be never encountered. Mbee 1 This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe hereRegExp tutorial: enough to get startedPCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta. SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt) Link to comment Share on other sites More sharing options...
Bilgus Posted April 25, 2018 Share Posted April 25, 2018 Sounds good @jchd Also I noticed an error while testing it ;$tChar = DllStructCreate("ushort", DllStructGetPtr($tVolNames) + $i) ;should be $tChar = DllStructCreate("ushort", DllStructGetPtr($tVolNames) + $i - 1) since the current pointer + 1 would be the second byte instead of the first byte as intended Mbee 1 Link to comment Share on other sites More sharing options...
jchd Posted April 25, 2018 Share Posted April 25, 2018 That's just a technical detail. You can as well walk the string as Word or Uint32 or Short (pick your favorite name) and check for 0x0000 then replace by 0xFFFD (or some other good, highly unlikely choice. Leave the last USHORT as 0x0000 since it will be a good end of string marker. Having the struct element union-ed as Byte[] is good to retrieve it finally as a whole, but going back to the struct element as returned (WChar) allows to grab it as AutoIt string and avoid binary to string as well. Bilgus 1 This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe hereRegExp tutorial: enough to get startedPCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta. SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt) Link to comment Share on other sites More sharing options...
TheXman Posted April 25, 2018 Share Posted April 25, 2018 _WinAPI_StructToArray will do the heavy lifting of parsing arrays of null terminated unicode strings. Slightly modified version of the previous solution. #include <WinAPIFiles.au3> #include <Array.au3> Global $sMountedPath = "c:\" Global $sGuid = _WinAPI_GetVolumeNameForVolumeMountPoint($sMountedPath) ConsoleWrite("Vol Guid: " & $sGuid & @CRLF) _ArrayDisplay(__WinAPI_GetVolumePathNamesForVolumeName($sGuid)) Func __WinAPI_GetVolumePathNamesForVolumeName($sVolumeGUID) Local $tVolNames, $tSize = DllStructCreate("int iSize") Local $aPathNames ;First call to get size of buffer req"d Local $aRet = DllCall("kernel32.dll", "bool", "GetVolumePathNamesForVolumeNameW", _ "wstr", $sVolumeGUID, _ "ptr", 0, _ "dword", 0, _ "ptr", DllStructGetPtr($tSize) _ ) $tVolNames = DllStructCreate("byte [" & DllStructGetData($tSize, 1) * 2 & "]") ;Multiply by 2 since TChars are 2 bytes each as far as Autoit is concerned ;Second call gets the actual data $aRet = DllCall("kernel32.dll", "bool", "GetVolumePathNamesForVolumeNameW", _ "wstr", $sVolumeGUID, _ "ptr", DllStructGetPtr($tVolNames), _ "dword", DllStructGetSize($tVolNames), _ "ptr", DllStructGetPtr($tSize) _ ) If @error Or Not $aRet[0] Then Return SetError(@error + 10, @extended, "") Return _WinAPI_StructToArray($tVolNames) EndFunc ;==>__WinAPI_GetVolumePathNamesForVolumeName Bilgus 1 CryptoNG UDF: Cryptography API: Next Gen jq UDF: Powerful and Flexible JSON Processor | jqPlayground: An Interactive JSON Processor Xml2Json UDF: Transform XML to JSON | HttpApi UDF: HTTP Server API | Roku Remote: Example Script About Me How To Ask Good Questions On Technical And Scientific Forums (Detailed) | How to Ask Good Technical Questions (Brief) "Any fool can know. The point is to understand." -Albert Einstein "If you think you're a big fish, it's probably because you only swim in small ponds." ~TheXman Link to comment Share on other sites More sharing options...
Bilgus Posted April 25, 2018 Share Posted April 25, 2018 (edited) @TheXman Nice! Its pretty much the same idea as above but cleaner since its already made Spoiler expandcollapse popupFunc _WinAPI_StructToArray(ByRef $tStruct, $iItems = 0);Yashied Local $iSize = 2 * Floor(DllStructGetSize($tStruct) / 2) Local $pStruct = DllStructGetPtr($tStruct) If Not $iSize Or Not $pStruct Then Return SetError(1, 0, 0) Local $tData, $iLength, $iOffset = 0 Local $aResult[101] = [0] While 1 $iLength = _WinAPI_StrLen($pStruct + $iOffset) If Not $iLength Then ExitLoop EndIf If 2 * (1 + $iLength) + $iOffset > $iSize Then Return SetError(3, 0, 0) $tData = DllStructCreate('wchar[' & (1 + $iLength) & ']', $pStruct + $iOffset) If @error Then Return SetError(@error + 10, 0, 0) __Inc($aResult);;Redims array $aResult[$aResult[0]] = DllStructGetData($tData, 1) If $aResult[0] = $iItems Then ExitLoop EndIf $iOffset += 2 * (1 + $iLength) If $iOffset >= $iSize Then Return SetError(3, 0, 0) WEnd If Not $aResult[0] Then Return SetError(2, 0, 0) __Inc($aResult, -1) Return $aResult EndFunc ;==>_WinAPI_StructToArray Func __Inc(ByRef $aData, $iIncrement = 100) Select Case UBound($aData, $UBOUND_COLUMNS) If $iIncrement < 0 Then ReDim $aData[$aData[0][0] + 1][UBound($aData, $UBOUND_COLUMNS)] Else $aData[0][0] += 1 If $aData[0][0] > UBound($aData) - 1 Then ReDim $aData[$aData[0][0] + $iIncrement][UBound($aData, $UBOUND_COLUMNS)] EndIf EndIf Case UBound($aData, $UBOUND_ROWS) If $iIncrement < 0 Then ReDim $aData[$aData[0] + 1] Else $aData[0] += 1 If $aData[0] > UBound($aData) - 1 Then ReDim $aData[$aData[0] + $iIncrement] EndIf EndIf Case Else Return 0 EndSelect Return 1 EndFunc ;==>__Inc I Included the code from WinAPIMisc.au3 (Autoit 3.3.14) here Yashied uses _WinAPI_StrLen to walk the string to the next NULL which I imagine is a bit faster Here it is functionally equivalent expandcollapse popup#include <WinAPIFiles.au3> #Include <Array.au3> Global $sMountedPath = "c:\" ; Don't forget to end with final '\' Global $sGuid = _WinAPI_GetVolumeNameForVolumeMountPoint($sMountedPath) ConsoleWrite("Vol Guid: " & $sGuid & @CRLF) _ArrayDisplay(__WinAPI_GetVolumePathNamesForVolumeName($sGuid), "Mount Point(s):") Func __WinAPI_GetVolumePathNamesForVolumeName($sVolumeGUID) Local $tVolNames, $tSize = DllStructCreate('int iSize') ;First call to get size of buffer req'd Local $aRet = DllCall('kernel32.dll', 'bool', 'GetVolumePathNamesForVolumeNameW', _ 'wstr', $sVolumeGUID, 'ptr', 0, _ 'dword', 0, _ 'ptr', DllStructGetPtr($tSize) _ ) $tVolNames = DllStructCreate("wchar [" & DllStructGetData($tSize, 1) & "]") ;Second call gets the actual data $aRet = DllCall('kernel32.dll', 'bool', 'GetVolumePathNamesForVolumeNameW', _ 'wstr', $sVolumeGUID, _ 'ptr', DllStructGetPtr($tVolNames), _ 'dword', DllStructGetSize($tVolNames), _ 'ptr', DllStructGetPtr($tSize) _ ) If @error Or Not $aRet[0] Then Return SetError(@error + 10, @extended, '') ;Step through the data turn the nulls into invalid unicode char (0xFFFD) @jchd For $i = 1 To (DllStructGetSize($tVolNames)/2) - 2 ;each character occupies (/2) two bytes and we ignore the last (-2) two nulls.. If DllStructGetData($tVolNames, 1, $i) = CHRW(0x0000) Then DllStructSetData($tVolNames, 1, CHRW(0xFFFD), $i) Next Return StringSplit(DllStructGetData($tVolNames, 1), CHRW(0xFFFD)) EndFunc ;==>__WinAPI_GetVolumePathNamesForVolumeName Edited April 25, 2018 by Bilgus TheXman 1 Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now