PDA

View Full Version : Retrieve Volume(s) GUID(s) in windows



DirectuX
22-10-2018, 16:20
Hi,
until now I've thought about two methods to get the GUIDs :


method 1: Capturing mountvol.exe output
Adapting this : https://www.thinbasic.com/community/showthread.php?8357-Capturing-OS_ShellExecute-Output




'---Load Console Module
Uses "Console"

USES "File"
uses "Console"
uses "OS"

Dim sTempFile as string 'complete path to the temporary file that will contain the output of the command
dim sCommande as string 'command to send to shell
dim sBuffer as string 'this will contain the output of the command read from the temporary file

sTempFile = OS_GetTempDir & "test1.txt"
sCommande = OS_ENVIRON("COMSPEC") & " /C " & " mountvol.exe > " & sTempFile
msgboxw 0,sCommande 'check what will be send to shell

'RunOnce: set console font for thinBasic and cdm.exe
'
'uses "registry"
'Registry_SetValue("HKEYCU", "Console\O:_thinBasic_thinBasic.exe", "FaceName", "Lucida Console")
'Registry_SetValue("HKEYCU", "Console\C:_Windows_System32_cmd.exe", "FaceName", "Lucida Console")
'/!\01 Doesn't behave as whished, had to manually change font setting in console's property panel. Rebooting doesn't help.


Console_SetOutputCP(65001)'Set output codepage to UTF-8
Console_Writeline("Test Console App: éùçàëØ€")'Test console output
'Display is ok after manual console-font change, this is not convenient for final user


OS_Shell(sCommande, %OS_WNDSTYLE_HIDE, %OS_SHELL_SYNC)
sBuffer += file_load(sTempFile)
' /!\02 File contains many commas where accentuated characters have to be

msgboxw 0, sBuffer
' /!\03 Text displayed contains many `?` where accentuated characters have to be

PrintL "Press a key to end program"
'---Wait for a key press
WaitKey




method 2: Calling kernel32.dll functions (Prefered as no file is written, no change to system's console configuration)
https://docs.microsoft.com/fr-fr/windows/desktop/FileIO/volume-management-functions

Adapting this : https://www.thinbasic.com/community/showthread.php?8199-declare-function-from-handle





Uses "OS"
Uses "console"

'---

'---Standard API functions to get a function address. Those functions are used to simulate Petr SomeDirtyAPIToTellMeHandle function

'---

Declare Function LoadLibrary Lib "KERNEL32.DLL" Alias "LoadLibraryA" (lpLibFileName As ASCIIZ) As Long
Declare Function GetProcAddress Lib "KERNEL32.DLL" Alias "GetProcAddress" (ByVal hModule As DWORD, lpProcName As ASCIIZ) As Long


'---

'---Standard API functions to Data Access and Storage. https://docs.microsoft.com/fr-fr/windows/desktop/api/fileapi/
' (Memo, this may help too: https://www.codeproject.com/Articles/27355/Inside-Mountvol-exe)
'---

Declare Function FindFirstVolume (byref lpszVolumeName As Long, cchBufferLength as dword) As Long
Declare Function FindNextVolume (byref hFindVolume as long,byref lpszVolumeName as long,byref cchBufferLength as Dword) as boolean
Declare Function FindVolumeClose (byref hFindVolume AS long) as boolean
'/!\ 01 Can't find in thinBasic's documentation any correspondance table with https://docs.microsoft.com/en-us/windows/desktop/winprog/windows-data-types , best guess:
'BOOL -> Boolean
'Handle -> Long
'LPWSTR -> Long
'DWORD -> Dword
'TCHAR -> ? (8 or 16-bit Unicode character)


'---OK, here we start simulating the assigment of the procedure address

Dim hLib As Long
Dim hProc As Long
Dim hProc2 As Long
dim hProc3 as Long


'---First we need to load the library from where we want the address of the function

hLib = LoadLibrary("KERNEL32.DLL")

'---If return value is NOT zero all is ok

If hLib <> 0 Then

'---Now we try to get the address of the prodecure inside the library

hProc = GetProcAddress(hLib, "FindFirstVolumeW")
hProc2 = GetProcAddress(hLib, "FindNextVolumeW")
hProc3 = GetProcAddress(hLib, "FindVolumeClose")

'---If return value is NOT zero all is ok

If hProc <> 0 and hproc2 <> 0 and hproc3 <> 0 Then


'---

'---Here the new thinBasic functionality. It assign a process address to a generic previously declared function allowing subsequent calling


Declare Set ADDRESS FindFirstVolume, hProc
Declare Set ADDRESS FindNextVolume, hProc2
Declare Set ADDRESS FindVolumeClose, hProc3


'---Now we try to use the functionality


dim VolumeName As String ' will contain the current VolumeName found
Dim Display as String ' will contain the concatened string to display
dim lpszVolumeName as long ' pointer to VolumeName
lpszVolumeName = STRPTR (VolumeName) '

Dim cchBufferLength as dword
cchBufferLength = 200
'/!\02 how long ? is it arbitrary ?

dim hFindVolume as long
dim isClosedVolume as boolean
isClosedVolume = false

hFindVolume = FindFirstVolume(lpszVolumeName, cchBufferLength)
'/!\03 This has not populated `VolumeName`
Display += VolumeName & $CRLF
do while not isClosedVolume
DoEvents
if FindNextVolume(hFindVolume,lpszVolumeName,cchBufferLength) then
'/!\04 This has returned `0` (false)
Display += VolumeName & $CRLF
else
isClosedVolume = FindVolumeClose(hFindVolume)
endif
loop

MSGBOXw 0, Display


Else

MSGBOX 0, "It was not possible to get the procedure address"

End If

Else

MSGBOX 0, "It was not possible to load library"

End If


WaitKey

Method 1 may work but is inconvenient (notice the /!\ in code comments). I can't get method 2 (prefered) to work and examples/discussions found in the forum didn't bring me further than what is above. What didn't I understand about declarations or usage ?

'ThinAir 1.10.5.0 on Win 8.1 x64

ErosOlmi
22-10-2018, 20:54
Ciao DirectuX

welcome to thinBasic.
Honestly this is an area I do not know from a programming point of view so I need to study it a bit.
But I searched WMI world and found something that can be useful.

I've create below script using iDispatch variable instantiating a WMI engine, asking for info about mounted volumes.

Check if it does what you need.
In the meantime I will study API SKD functions.

Ciao
Eros


#Region "Script info"
// This script uses WMI to collect and print information regarding logical volumes
#EndRegion

#Region "Web References to resources used in this script"
// WMI..................: https://msdn.microsoft.com/en-us/library/aa384642(v=vs.85).aspx
// WMI cimv2............: https://msdn.microsoft.com/en-us/library/aa384463(v=vs.85).aspx
// Win32_Volume class...: hhttps://msdn.microsoft.com/en-us/library/windows/desktop/aa394515(v=vs.85).aspx
// Example from.........: http://www.almguide.com/2008/08/getting-drive-and-mount-point-information-using-wmi/
#EndRegion

#Region "Modules"
uses "Console"
#EndRegion

printl "This script uses WMI to collect and print information regarding logical volumes"
printl "---Press a key to start---"
WaitKey

string strComputer = "."
iDispatch objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

if IsComObject(objWMIService) Then

'---Also check ... NOT NULL
iDispatch colItems = objWMIService.ExecQuery("SELECT * FROM Win32_Volume WHERE DriveLetter IS NULL")
'iDispatch colItems = objWMIService.ExecQuery("SELECT * FROM Win32_Volume WHERE DriveLetter IS NOT NULL")

if IsComObject(colItems) Then
printl "Items present in colItems collection:", colItems.Count

iDispatch objItem
For nItem as long = 1 to colItems.Count
objItem = colItems.ItemIndex(nItem - 1) '---First item in collectins starts at 0
printl "Volume:", nItem
printl $tab, "Name..............: ", objItem.Name
printl $tab, "Label.............: ", objItem.Label
printl $tab, "Caption...........: ", objItem.Caption
printl $tab, "DriveLetter.......: ", objItem.DriveLetter
printl $tab, "DriveType.........: ", objItem.DriveType
printl $tab, "FileSystem........: ", objItem.FileSystem
printl $tab, "SystemName........: ", objItem.SystemName
printl $tab, "SerialNumber......: ", objItem.SerialNumber

printl

objItem = Nothing
Next

colItems = Nothing
Else
printl "It was not possible to instantiate colItems"
end If

objWMIService = Nothing
Else
printl "It was not possible to instantiate objWMIService"
end If

printl "---All done. Press a key to end---"
WaitKey

ErosOlmi
22-10-2018, 21:58
Here your second method working without the need to get process address, just declare needed functions
Here I declared ASCII version of the functions



Uses "console"


'LPSTR data type is just a pointer to a string buffer that MUST have enough bytes to receive ... something


Declare Function FindFirstVolume Lib "KERNEL32.DLL" alias "FindFirstVolumeA" (byval lpszVolumeName As dword, byval cchBufferLength as dword) As Long
Declare Function FindNextVolume Lib "KERNEL32.DLL" alias "FindNextVolumeA" (ByVal hFindVolume as long, byval lpszVolumeName as dword, byval cchBufferLength as Dword) as boolean
Declare Function FindVolumeClose Lib "KERNEL32.DLL" alias "FindVolumeClose" (byval hFindVolume AS long) as boolean


dim VolumeName As string = $nul(255) '---Allocate the buffer with some bytes, here we init with nuls
dim hFindVolume as long
dim isClosedVolume as boolean


'---Pass string pointer of the allocated string plus the size. Function must know how much bytes are available
'---In order NOT to buffer overflow and generate a GPF
hFindVolume = FindFirstVolume(strptr(VolumeName), len(VolumeName))
printl VolumeName
do
if FindNextVolume(hFindVolume, strptr(VolumeName), len(VolumeName)) then
printl VolumeName
else
isClosedVolume = FindVolumeClose(hFindVolume)
endif
loop while not isClosedVolume


printl "---Press a key to end---"
WaitKey

DirectuX
23-10-2018, 14:56
welcome to thinBasic.

Thanks.




But I searched WMI world and found something that can be useful.

I've create below script using iDispatch variable instantiating a WMI engine, asking for info about mounted volumes.

Check if it does what you need.


I checked it. I found it to be a more difficult method to me. First, although you provided resource links, it's not easy, to me again, to find the right keywords at msdn with this way of programming. Secondly, WMI doesn't return GUID for mounted volumes but their letter instead.



Here your second method working

That's inspiring, I'll go with this. Thanks again ErosOlmi.

DirectuX
24-01-2020, 18:54
Fourth method:

(inspired by https://www.thinbasic.com/public/products/thinBasic/help/html/wmi_getdata.htm)


Eros, do you have a preference recommendation (regarding thinBasic development, help page states "WMI module is still highly experimental.") between this method (WMI_GetData) and idispatch method (https://www.thinbasic.com/community/showthread.php?12887-Retrieve-Volume(s)-GUID(s)-in-windows&p=94532&viewfull=1#post94532) ?





Uses "WMI", "OS"

Uses "CONSOLE"



Dim vData() As String

Dim nItems As Long

Dim Counter As Long

Dim ComputerName As String = OS_GetComputerName

Dim sBuffer As String



'--- Ask data to WMI system


sBuffer = WMI_GetData(ComputerName, "", "", "", "Win32_Volume")


' -- Parse returned data into single lines

nItems = Parse( sBuffer, vData(), $CRLF)



'---Print lines

For Counter = 1 To nItems

Printl vData(Counter)

Next



'---Finished

Printl "-----------------------------------------------------"

Printl "Number of lines: " & nItems

Printl "---------------------------Press a key to finish-----"



WaitKey