This code is meant to check if USB key(s) are using a valid hardware serial number.
It could be used as a good replacement for dongle software copy protection, by checking the unique serial number(s) found.
Note: It requires the use of WMI, and José Roca's include files.
'+--------------------------------------------------------------------------+
'| |
'| zUSB 1.00 |
'| |
'| USB unique serial number detection |
'| Based on the Win32_DiskDrive class |
'| |
'+--------------------------------------------------------------------------+
'| |
'| public domain |
'| |
'| Patrice Terrier http://www.zapsolution.com |
'| |
'+--------------------------------------------------------------------------+
'| Project started on : 10-18-2011 (MM-DD-YYYY) |
'| Last revised : 10-18-2011 (MM-DD-YYYY) |
'+--------------------------------------------------------------------------+
#COMPILE EXE "zUSB.exe"
#INCLUDE "WMI.INC" '// José Roca's include file.
DECLARE FUNCTION zTrace LIB "zTrace.DLL" ALIAS "zTrace" (zMessage AS ASCIIZ) AS LONG
'// Retrieves all the properties of the Win32_DiskDrive class.
FUNCTION DetectUSB () AS STRING
LOCAL hr AS LONG '// HRESULT
LOCAL pService AS ISWbemServices '// Services object
LOCAL pObjectSet AS ISWbemObjectSet '// ISWbemObjectSet interface
LOCAL pEnum AS IEnumVariant '// Generic collection's enumerator reference
LOCAL bstrDisplayName AS WSTRING '// Display name
LOCAL bstrQuery AS WSTRING '// Query string
LOCAL oItem AS DISPATCH '// Generic object variable
LOCAL vItem AS VARIANT '// Generic object variant
LOCAL vRes AS VARIANT '// General purpose variant
LOCAL sMsg, sSer AS STRING
'// Variants to store the property values
LOCAL vAvailability AS VARIANT '// Unsigned 16-bit integer
LOCAL vBytesPerSector AS VARIANT '// Unsigned 32-bit integer
LOCAL vCapabilities AS VARIANT '// Array - Unsigned 16-bit integer
LOCAL vCapabilityDescriptions AS VARIANT '// Array - String
LOCAL vCaption AS VARIANT '// String
LOCAL vCompressionMethod AS VARIANT '// String
LOCAL vConfigManagerErrorCode AS VARIANT '// Unsigned 32-bit integer
LOCAL vConfigManagerUserConfig AS VARIANT '// Boolean value
LOCAL vCreationClassName AS VARIANT '// String
LOCAL vDefaultBlockSize AS VARIANT '// Unsigned 64-bit integer
LOCAL vDescription AS VARIANT '// String
LOCAL vDeviceID AS VARIANT '// String
LOCAL vErrorCleared AS VARIANT '// Boolean value
LOCAL vErrorDescription AS VARIANT '// String
LOCAL vErrorMethodology AS VARIANT '// String
LOCAL vFirmwareRevision AS VARIANT '// String
LOCAL vIndex AS VARIANT '// Unsigned 32-bit integer
LOCAL vInstallDate AS VARIANT '// Date/time value
LOCAL vInterfaceType AS VARIANT '// String
LOCAL vLastErrorCode AS VARIANT '// Unsigned 32-bit integer
LOCAL vManufacturer AS VARIANT '// String
LOCAL vMaxBlockSize AS VARIANT '// Unsigned 64-bit integer
LOCAL vMaxMediaSize AS VARIANT '// Unsigned 64-bit integer
LOCAL vMediaLoaded AS VARIANT '// Boolean value
LOCAL vMediaType AS VARIANT '// String
LOCAL vMinBlockSize AS VARIANT '// Unsigned 64-bit integer
LOCAL vModel AS VARIANT '// String
LOCAL vName AS VARIANT '// String
LOCAL vNeedsCleaning AS VARIANT '// Boolean value
LOCAL vNumberOfMediaSupported AS VARIANT '// Unsigned 32-bit integer
LOCAL vPartitions AS VARIANT '// Unsigned 32-bit integer
LOCAL vPNPDeviceID AS VARIANT '// String
LOCAL vPowerManagementCapabilities AS VARIANT '// Array - Unsigned 16-bit integer
LOCAL vPowerManagementSupported AS VARIANT '// Boolean value
LOCAL vSCSIBus AS VARIANT '// Unsigned 32-bit integer
LOCAL vSCSILogicalUnit AS VARIANT '// Unsigned 16-bit integer
LOCAL vSCSIPort AS VARIANT '// Unsigned 16-bit integer
LOCAL vSCSITargetId AS VARIANT '// Unsigned 16-bit integer
LOCAL vSectorsPerTrack AS VARIANT '// Unsigned 32-bit integer
LOCAL vSerialNumber AS VARIANT '// String
LOCAL vSignature AS VARIANT '// Unsigned 32-bit integer
LOCAL vSize AS VARIANT '// Unsigned 64-bit integer
LOCAL vStatus AS VARIANT '// String
LOCAL vStatusInfo AS VARIANT '// Unsigned 16-bit integer
LOCAL vSystemCreationClassName AS VARIANT '// String
LOCAL vSystemName AS VARIANT '// String
LOCAL vTotalCylinders AS VARIANT '// Unsigned 64-bit integer
LOCAL vTotalHeads AS VARIANT '// Unsigned 32-bit integer
LOCAL vTotalSectors AS VARIANT '// Unsigned 64-bit integer
LOCAL vTotalTracks AS VARIANT '// Unsigned 64-bit integer
LOCAL vTracksPerCylinder AS VARIANT '// Unsigned 32-bit integer
'// Connect to WMI using a moniker
bstrDisplayName = "winmgmts:{impersonationLevel=impersonate}!\\.\root\CIMV2"
pService = WmiGetObject(bstrDisplayName)
IF ISOBJECT(pService) THEN
'// Execute a query to get a reference to the collection of objects
bstrQuery = "SELECT * FROM Win32_DiskDrive"
pObjectSet = pService.ExecQuery(bstrQuery, "WQL", %wbemFlagReturnImmediately)
IF ISOBJECT(pObjectSet) THEN
'// Retrieve a reference to the collection's enumerator
pEnum = pObjectSet.NewEnum_
IF ISOBJECT(pEnum) THEN
'// Iterate through the collection of objects
DO
'// Retrieve a reference to the next object in the collection
hr = pEnum.Next(1, vItem, BYVAL %NULL)
IF hr <> %S_OK THEN EXIT DO
'// Assign the VT_DISPATCH variant to the object variable
oItem = vItem : vItem = EMPTY
IF ISNOTHING(oItem) THEN EXIT DO
'// Retrieve the values of the properties
OBJECT GET oItem.Availability TO vAvailability
OBJECT GET oItem.BytesPerSector TO vBytesPerSector
OBJECT GET oItem.Capabilities TO vCapabilities
OBJECT GET oItem.CapabilityDescriptions TO vCapabilityDescriptions
OBJECT GET oItem.Caption TO vCaption
OBJECT GET oItem.CompressionMethod TO vCompressionMethod
OBJECT GET oItem.ConfigManagerErrorCode TO vConfigManagerErrorCode
OBJECT GET oItem.ConfigManagerUserConfig TO vConfigManagerUserConfig
OBJECT GET oItem.CreationClassName TO vCreationClassName
OBJECT GET oItem.DefaultBlockSize TO vDefaultBlockSize
OBJECT GET oItem.Description TO vDescription
OBJECT GET oItem.DeviceID TO vDeviceID
OBJECT GET oItem.ErrorCleared TO vErrorCleared
OBJECT GET oItem.ErrorDescription TO vErrorDescription
OBJECT GET oItem.ErrorMethodology TO vErrorMethodology
OBJECT GET oItem.FirmwareRevision TO vFirmwareRevision
OBJECT GET oItem.Index TO vIndex
OBJECT GET oItem.InstallDate TO vInstallDate
OBJECT GET oItem.InterfaceType TO vInterfaceType
OBJECT GET oItem.LastErrorCode TO vLastErrorCode
OBJECT GET oItem.Manufacturer TO vManufacturer
OBJECT GET oItem.MaxBlockSize TO vMaxBlockSize
OBJECT GET oItem.MaxMediaSize TO vMaxMediaSize
OBJECT GET oItem.MediaLoaded TO vMediaLoaded
OBJECT GET oItem.MediaType TO vMediaType
OBJECT GET oItem.MinBlockSize TO vMinBlockSize
OBJECT GET oItem.Model TO vModel
OBJECT GET oItem.Name TO vName
OBJECT GET oItem.NeedsCleaning TO vNeedsCleaning
OBJECT GET oItem.NumberOfMediaSupported TO vNumberOfMediaSupported
OBJECT GET oItem.Partitions TO vPartitions
OBJECT GET oItem.PNPDeviceID TO vPNPDeviceID
OBJECT GET oItem.PowerManagementCapabilities TO vPowerManagementCapabilities
OBJECT GET oItem.PowerManagementSupported TO vPowerManagementSupported
OBJECT GET oItem.SCSIBus TO vSCSIBus
OBJECT GET oItem.SCSILogicalUnit TO vSCSILogicalUnit
OBJECT GET oItem.SCSIPort TO vSCSIPort
OBJECT GET oItem.SCSITargetId TO vSCSITargetId
OBJECT GET oItem.SectorsPerTrack TO vSectorsPerTrack
OBJECT GET oItem.SerialNumber TO vSerialNumber
OBJECT GET oItem.Signature TO vSignature
OBJECT GET oItem.Size TO vSize
OBJECT GET oItem.Status TO vStatus
OBJECT GET oItem.StatusInfo TO vStatusInfo
OBJECT GET oItem.SystemCreationClassName TO vSystemCreationClassName
OBJECT GET oItem.SystemName TO vSystemName
OBJECT GET oItem.TotalCylinders TO vTotalCylinders
OBJECT GET oItem.TotalHeads TO vTotalHeads
OBJECT GET oItem.TotalSectors TO vTotalSectors
OBJECT GET oItem.TotalTracks TO vTotalTracks
OBJECT GET oItem.TracksPerCylinder TO vTracksPerCylinder
'// Release the object
oItem = NOTHING
sSer = VARIANT$(vPNPDeviceID)
IF VARIANT$(vMediaType) = "Removable Media" THEN sMsg += EXTRACT$(REMAIN$(sSer, "&VEN_"), "&") + " " + EXTRACT$(REMAIN$(sSer, "&PROD_"), "&") + CHR$(9,34) + EXTRACT$(PATHNAME$(NAME, sSer), "&") + CHR$(34) + $CR
LOOP
'// Release the enumerator
pEnum = NOTHING
END IF
'// Release the collection
pObjectSet = NOTHING
END IF
'// Release the service
pService = NOTHING
END IF
REPLACE "_" WITH " " IN sMsg
FUNCTION = sMsg
END FUNCTION
FUNCTION PBMAIN
LOCAL sMsg AS STRING
sMsg = DetectUSB() + $CR + $CR + "Done.
MsgBox sMsg, , "USB unique serial detection"
END FUNCTION
...
Instead of using the WMI Win32_DiskDrive class, you can use the MSFT_PhysicalDisk class and query for the MediaType property, that returns a UInt16 (WORD) value indicating the media type of the disk:
0 Unspecified
3 HDD
4 SSD
5 SCM
See: https://msdn.microsoft.com/en-us/library/windows/desktop/hh830532(v=vs.85).aspx