Created
July 2, 2020 12:08
-
-
Save EpicVoyage/5e587f43c449e6990143b9228bdcdadc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Shared in the hope that this will be useful to others who need USB serial numbers in Windows. This should be valid | |
* for Windows 2000, XP, Vista, 7. | |
* | |
* It is possible for USB devices to not have a serial number. When that happens Windows assigns one based on the bus | |
* that the device is attached to. I do not have a thumb drive like that to test it with, but others have said that when | |
* the second character of the “serial” is an ampersand that it means the device does not have a serial number. This | |
* code does not test for it. | |
*/ | |
int NextDevice(DWORD *index, TCHAR *drive, TCHAR *serial, DWORD szs, TCHAR *parentidprefix, DWORD szpip) | |
{ | |
/* Declare some variables we'll need */ | |
TCHAR *val, *data, *ptr, *ptr2; | |
DWORD sz, len, szval, szdata, type; | |
int ret = 0; | |
/* Make the HKEY instance persistent */ | |
static HKEY hkey = NULL; | |
/* Obtain share name and timeout from HKEY_CURRENT_USER first */ | |
if (hkey == NULL) | |
{ | |
if (RegOpenKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\MountedDevices"), &hkey) != ERROR_SUCCESS) | |
hkey = NULL; | |
} | |
if (hkey != NULL) | |
{ | |
/* Size of largest data blocks we anticipate needing */ | |
szval = 256; | |
szdata = 1024; | |
/* Create buffers big enough for the anticipated data */ | |
val = (TCHAR *)malloc(sizeof(TCHAR) * szval); | |
data = (TCHAR *)malloc(sizeof(TCHAR) * szdata); | |
while ((sz = RegEnumValue(hkey, (*index)++, val, &szval, NULL, &type, (LPBYTE)data, &szdata)) == ERROR_SUCCESS) | |
{ | |
/* Windows XP/2000 mehod: | |
* Only use the entries that have a DOS drive letter associated with them. | |
*/ | |
if ((wcsncmp(val, _T("\\DosDevices\\"), 12) == 0) && (wcsncmp(data, _T("\\??\\STORAGE#RemovableMedia#"), 27) == 0)) | |
{ | |
/* Get the drive letter */ | |
wcsncpy_s(drive, 3, val + 12, 2); | |
/* Supply our own terminating NULL character */ | |
*(drive + 2) = 0; | |
/* This should pull the data between the 2nd and 3rd hash marks since | |
* we want to use it instead of the registry key that it represents. | |
*/ | |
ptr = wcschr(data + 26, '#') + 1; | |
ptr2 = wcschr(ptr, '#'); | |
/* Copy out the ParentIdPrefix to return */ | |
len = (DWORD)(((DWORD)(ptr2 - ptr) < szpip) ? (ptr2 - ptr) : (szpip - 1)); | |
wcsncpy_s(parentidprefix, szpip, ptr, len); | |
/* Let's add a NULL to this string also */ | |
*(parentidprefix + len) = 0; | |
/* Kill the Removable Device tag on the string */ | |
if (ptr = wcsstr(parentidprefix, _T("&RM"))) | |
{ | |
*ptr++ = 0; | |
*ptr++ = 0; | |
*ptr++ = 0; | |
} | |
ret = 1; | |
break; | |
} | |
/* Vista/7 mehod: | |
* Only use the entries that have a DOS drive letter associated with them. Vista | |
* uses "_??_", Windows 7 uses "#??#". | |
*/ | |
else if (((wcsncmp(data + 1, _T("??"), 2) == 0) && (wcsncmp(data + 4, _T("USBSTOR#Disk"), 12) == 0)) && | |
(wcsncmp(val, _T("\\DosDevices\\"), 12) == 0)) | |
{ | |
/* Get the drive letter */ | |
wcsncpy_s(drive, 3, val + 12, 2); | |
/* Supply our own terminating NULL character */ | |
*(drive + 2) = 0; | |
/* This should pull the data between the 2nd and 3rd hash marks since | |
* we want to use it instead of the registry key that it represents. | |
*/ | |
ptr = wcschr(data + 4, '#') + 1; | |
ptr = wcschr(ptr, '#') + 1; | |
ptr2 = wcschr(ptr, '#'); | |
/* Copy out the serial number to return */ | |
len = (DWORD)(((DWORD)(ptr2 - ptr) < szs) ? (ptr2 - ptr) : (szs - 1)); | |
wcsncpy_s(serial, szs, ptr, len); | |
/* Let's add a NULL to this string also */ | |
*(serial + len) = 0; | |
/* Kill the &# at the end of the string… */ | |
if (ptr = wcsstr(serial, _T("&"))) | |
{ | |
while (*ptr) | |
*ptr++ = 0; | |
} | |
ret = 1; | |
break; | |
} | |
szval = 256; | |
szdata = 1024; | |
} | |
/* Free up the memory we were using… */ | |
free(val); | |
free(data); | |
if (sz != ERROR_SUCCESS) | |
RegCloseKey(hkey); | |
} | |
return ret; | |
} | |
int GetXPSerial(TCHAR *parentidprefix, TCHAR *serial, DWORD szs, TCHAR *friendlyname, DWORD szfn) | |
{ | |
/* Declare some variables we'll need */ | |
TCHAR *path, *val, *pip, *ptr; | |
DWORD index, index2, szval, len; | |
HKEY hkey, hkey2, hkey3; | |
path = (TCHAR *)malloc(sizeof(wchar_t) * 255); | |
wcscpy_s(path, 255, _T("SYSTEM\\CurrentControlSet\\Enum\\USBSTOR")); | |
/* Obtain share name and timeout from HKEY_CURRENT_USER first */ | |
if (RegOpenKey(HKEY_LOCAL_MACHINE, path, &hkey) == ERROR_SUCCESS) | |
{ | |
/* Size of largest data blocks we anticipate needing */ | |
szval = 256; | |
/* Create buffers big enough for the anticipated data */ | |
val = (TCHAR *)malloc(sizeof(TCHAR) * szval); | |
pip = (TCHAR *)malloc(sizeof(TCHAR) * szval); | |
/* Iterate through the child keys. This will probably be a list of all USB | |
* storage devices ever connected to the computer. | |
*/ | |
index = 0; | |
while (RegEnumKeyEx(hkey, index++, val, &szval, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) | |
{ | |
/* Prepare the path for another iteration */ | |
*(val + szval) = 0; | |
wcscpy_s(path, 255, _T("SYSTEM\\CurrentControlSet\\Enum\\USBSTOR\\")); | |
wcscat_s(path, 255, val); | |
len = (DWORD)wcslen(path); | |
/* Descend into each key. This will be the serial number of the device, if | |
* it has one, with "&0" added to the end. | |
*/ | |
if (RegOpenKey(HKEY_LOCAL_MACHINE, path, &hkey2) == ERROR_SUCCESS) | |
{ | |
/* We should only have on serial number but we still have to call this | |
* function. | |
*/ | |
index2 = 0; | |
szval = 256; | |
while (RegEnumKeyEx(hkey2, index2++, val, &szval, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) | |
{ | |
/* Prepare the path for another peek… */ | |
*(val + szval) = 0; | |
/* Use a little trickery to truncate path, just in case there are multiple serials */ | |
wcscpy_s(path + len, 255 - len, _T("\\")); | |
wcscat_s(path, 255, val); | |
if (RegOpenKey(HKEY_LOCAL_MACHINE, path, &hkey3) == ERROR_SUCCESS) | |
{ | |
szval = 256; | |
if (RegQueryValueEx(hkey3, _T("ParentIdPrefix"), NULL, NULL, (LPBYTE)pip, &szval) == ERROR_SUCCESS) | |
{ | |
*(pip + szval) = 0; | |
if (wcscmp(pip, parentidprefix) == 0) | |
{ | |
/* Copy the Serial number back */ | |
wcscpy_s(serial, szs, val); | |
/* Kill "&0" from the end */ | |
if (ptr = wcsstr(serial, _T("&0"))) | |
{ | |
*ptr++ = 0; | |
*ptr++ = 0; | |
} | |
/* It's easier to just return from here instead of trying | |
* to break from two loops. | |
*/ | |
free(val); | |
free(pip); | |
RegCloseKey(hkey2); | |
RegCloseKey(hkey); | |
return 1; | |
} | |
} | |
} | |
szval = 256; | |
} | |
RegCloseKey(hkey2); | |
} | |
szval = 256; | |
} | |
/* Free up the memory we were using… */ | |
free(val); | |
free(pip); | |
RegCloseKey(hkey); | |
} | |
return 0; | |
} | |
// Pick out USB removable drives | |
void LoadDevices() | |
{ | |
/* Create some variables */ | |
TCHAR *validdrives, *drive, *parentidprefix, *serial, *friendlyname, *ptr; | |
DWORD sz = 64, szvd, index = 0; | |
/* And allocate them… */ | |
parentidprefix = (TCHAR *)malloc(sizeof(wchar_t) * sz); | |
friendlyname = (TCHAR *)malloc(sizeof(wchar_t) * sz); | |
serial = (TCHAR *)malloc(sizeof(wchar_t) * sz); | |
drive = (TCHAR *)malloc(sizeof(wchar_t) * 3); | |
*serial = 0; | |
/* Get a list of currently valid drive letters */ | |
szvd = GetLogicalDriveStrings(0, NULL); | |
validdrives = (TCHAR *)malloc(sizeof(wchar_t) * szvd + 2); | |
GetLogicalDriveStrings(szvd, validdrives); | |
while (NextDevice(&index, drive, serial, sz, parentidprefix, sz)) | |
{ | |
ptr = validdrives; | |
while (*ptr) | |
{ | |
if (wcsncmp(ptr++, drive, 2) == 0) | |
{ | |
if (*serial == 0) | |
GetXPSerial(parentidprefix, serial, sz, friendlyname, sz); | |
MessageBox(NULL, serial, drive, MB_OK); | |
break; | |
} | |
ptr += wcslen(ptr) + 1; | |
} | |
*serial = 0; | |
} | |
free(parentidprefix); | |
free(friendlyname); | |
free(validdrives); | |
free(serial); | |
free(drive); | |
return; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment