1
1
Fork 0
mirror of https://github.com/pbatard/rufus.git synced 2024-08-14 23:57:05 +00:00

[core] add a cheat mode to reset the current USB device (cycle port)

* Will not work on Vista, Windows 7 or Server 2008
* Also update Windows version info
This commit is contained in:
Pete Batard 2017-09-15 12:40:33 +01:00
parent 13ba3e75b3
commit 8286a0f63a
6 changed files with 103 additions and 30 deletions

View file

@ -1,7 +1,7 @@
/* /*
* Rufus: The Reliable USB Formatting Utility * Rufus: The Reliable USB Formatting Utility
* Device detection and enumeration * Device detection and enumeration
* Copyright © 2014-2017 Pete Batard <pete@akeo.ie> * Copyright © 2014-2017 Pete Batard <pete@akeo.ie>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -42,7 +42,8 @@
#include "drive.h" #include "drive.h"
#include "dev.h" #include "dev.h"
extern StrArray DriveID, DriveLabel; extern StrArray DriveID, DriveLabel, DriveHub;
extern uint32_t DrivePort[MAX_DRIVES];
extern BOOL enable_HDDs, use_fake_units, enable_vmdk, usb_debug, list_non_usb_removable_drives; extern BOOL enable_HDDs, use_fake_units, enable_vmdk, usb_debug, list_non_usb_removable_drives;
/* /*
@ -123,6 +124,51 @@ out:
return r; return r;
} }
/*
* Cycle port (reset) the selected device
*/
BOOL ResetDevice(int index)
{
static uint64_t LastReset = 0;
BOOL r = FALSE;
HANDLE handle = INVALID_HANDLE_VALUE;
DWORD size;
USB_CYCLE_PORT_PARAMS cycle_port;
// Wait at least 10 secs between resets
if (_GetTickCount64() < LastReset + 10000ULL) {
uprintf("You must wait at least 10 seconds before trying to reset a device");
return FALSE;
}
if (DriveHub.String[index] == NULL)
return FALSE;
LastReset = _GetTickCount64();
handle = CreateFileA(DriveHub.String[index], GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (handle == INVALID_HANDLE_VALUE) {
uprintf("Could not open %s: %s", DriveHub.String[index], WindowsErrorString());
goto out;
}
memset(&cycle_port, 0, sizeof(cycle_port));
size = sizeof(cycle_port);
cycle_port.ConnectionIndex = DrivePort[index];
uprintf("Cycling port %d (reset) on %s", DrivePort[index], DriveHub.String[index]);
// As per https://msdn.microsoft.com/en-us/library/windows/hardware/ff537340.aspx
// IOCTL_USB_HUB_CYCLE_PORT is not supported on Windows 7, Windows Vista, and Windows Server 2008
if (!DeviceIoControl(handle, IOCTL_USB_HUB_CYCLE_PORT, &cycle_port, size, &cycle_port, size, &size, NULL)) {
uprintf(" Failed to cycle port: %s", WindowsErrorString());
goto out;
}
r = TRUE;
out:
safe_closehandle(handle);
return r;
}
static __inline BOOL IsVHD(const char* buffer) static __inline BOOL IsVHD(const char* buffer)
{ {
int i; int i;
@ -319,12 +365,13 @@ BOOL GetDevices(DWORD devnum)
LONG maxwidth = 0; LONG maxwidth = 0;
int s, score, drive_number, remove_drive; int s, score, drive_number, remove_drive;
char drive_letters[27], *device_id, *devid_list = NULL, entry_msg[128]; char drive_letters[27], *device_id, *devid_list = NULL, entry_msg[128];
char *p, *label, *entry, buffer[MAX_PATH], str[MAX_PATH], *method_str; char *p, *label, *entry, buffer[MAX_PATH], str[MAX_PATH], *method_str, *hub_path;
usb_device_props props; usb_device_props props;
IGNORE_RETVAL(ComboBox_ResetContent(hDeviceList)); IGNORE_RETVAL(ComboBox_ResetContent(hDeviceList));
StrArrayClear(&DriveID); StrArrayClear(&DriveID);
StrArrayClear(&DriveLabel); StrArrayClear(&DriveLabel);
StrArrayClear(&DriveHub);
StrArrayCreate(&dev_if_path, 128); StrArrayCreate(&dev_if_path, 128);
// Add a dummy for string index zero, as this is what non matching hashes will point to // Add a dummy for string index zero, as this is what non matching hashes will point to
StrArrayAdd(&dev_if_path, "", TRUE); StrArrayAdd(&dev_if_path, "", TRUE);
@ -452,6 +499,7 @@ BOOL GetDevices(DWORD devnum)
memset(buffer, 0, sizeof(buffer)); memset(buffer, 0, sizeof(buffer));
memset(&props, 0, sizeof(props)); memset(&props, 0, sizeof(props));
method_str = ""; method_str = "";
hub_path = NULL;
if (!SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_ENUMERATOR_NAME, if (!SetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_ENUMERATOR_NAME,
&datatype, (LPBYTE)buffer, sizeof(buffer), &size)) { &datatype, (LPBYTE)buffer, sizeof(buffer), &size)) {
uprintf("SetupDiGetDeviceRegistryProperty (Enumerator Name) failed: %s\n", WindowsErrorString()); uprintf("SetupDiGetDeviceRegistryProperty (Enumerator Name) failed: %s\n", WindowsErrorString());
@ -584,8 +632,10 @@ BOOL GetDevices(DWORD devnum)
if ((uintptr_t)htab_devid.table[j].data > 0) { if ((uintptr_t)htab_devid.table[j].data > 0) {
uuprintf(" Matched with Hub[%d]: '%s'", (uintptr_t)htab_devid.table[j].data, uuprintf(" Matched with Hub[%d]: '%s'", (uintptr_t)htab_devid.table[j].data,
dev_if_path.String[(uintptr_t)htab_devid.table[j].data]); dev_if_path.String[(uintptr_t)htab_devid.table[j].data]);
if (GetUSBProperties(dev_if_path.String[(uintptr_t)htab_devid.table[j].data], device_id, &props)) if (GetUSBProperties(dev_if_path.String[(uintptr_t)htab_devid.table[j].data], device_id, &props)) {
method_str = ""; method_str = "";
hub_path = dev_if_path.String[(uintptr_t)htab_devid.table[j].data];
}
#ifdef FORCED_DEVICE #ifdef FORCED_DEVICE
props.vid = FORCED_VID; props.vid = FORCED_VID;
props.pid = FORCED_PID; props.pid = FORCED_PID;
@ -769,6 +819,8 @@ BOOL GetDevices(DWORD devnum)
// Must ensure that the combo box is UNSORTED for indexes to be the same // Must ensure that the combo box is UNSORTED for indexes to be the same
StrArrayAdd(&DriveID, buffer, TRUE); StrArrayAdd(&DriveID, buffer, TRUE);
StrArrayAdd(&DriveLabel, label, TRUE); StrArrayAdd(&DriveLabel, label, TRUE);
if ((hub_path != NULL) && (StrArrayAdd(&DriveHub, hub_path, TRUE) >= 0))
DrivePort[DriveHub.Index - 1] = props.port;
IGNORE_RETVAL(ComboBox_SetItemData(hDeviceList, ComboBox_AddStringU(hDeviceList, entry), drive_index)); IGNORE_RETVAL(ComboBox_SetItemData(hDeviceList, ComboBox_AddStringU(hDeviceList, entry), drive_index));
maxwidth = max(maxwidth, GetEntryWidth(hDeviceList, entry)); maxwidth = max(maxwidth, GetEntryWidth(hDeviceList, entry));

View file

@ -1,7 +1,7 @@
/* /*
* Rufus: The Reliable USB Formatting Utility * Rufus: The Reliable USB Formatting Utility
* Device listing * Device listing
* Copyright © 2014-2016 Pete Batard <pete@akeo.ie> * Copyright © 2014-2016 Pete Batard <pete@akeo.ie>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -106,9 +106,12 @@ DECLSPEC_IMPORT CONFIGRET WINAPI CM_Get_Sibling(PDEVINST pdnDevInst, DEVINST dnD
// This last one is unknown from MinGW32 and needs to be fetched from the DLL // This last one is unknown from MinGW32 and needs to be fetched from the DLL
PF_TYPE_DECL(WINAPI, CONFIGRET, CM_Get_DevNode_Registry_PropertyA, (DEVINST, ULONG, PULONG, PVOID, PULONG, ULONG)); PF_TYPE_DECL(WINAPI, CONFIGRET, CM_Get_DevNode_Registry_PropertyA, (DEVINST, ULONG, PULONG, PVOID, PULONG, ULONG));
#define USB_HUB_CYCLE_PORT 273
#define USB_GET_NODE_CONNECTION_INFORMATION_EX 274 #define USB_GET_NODE_CONNECTION_INFORMATION_EX 274
#define USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 279 #define USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 279
#define IOCTL_USB_HUB_CYCLE_PORT \
CTL_CODE(FILE_DEVICE_USB, USB_HUB_CYCLE_PORT, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \ #define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \
CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 \ #define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 \
@ -172,6 +175,11 @@ typedef struct _USB_NODE_CONNECTION_INFORMATION_EX_V2 {
USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS Flags; USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS Flags;
} USB_NODE_CONNECTION_INFORMATION_EX_V2, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2; } USB_NODE_CONNECTION_INFORMATION_EX_V2, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2;
typedef struct {
ULONG ConnectionIndex;
ULONG StatusReturned;
} USB_CYCLE_PORT_PARAMS;
#pragma pack(pop) #pragma pack(pop)
const GUID _GUID_DEVINTERFACE_DISK = const GUID _GUID_DEVINTERFACE_DISK =

View file

@ -114,7 +114,8 @@ uint16_t rufus_version[3], embedded_sl_version[2];
char embedded_sl_version_str[2][12] = { "?.??", "?.??" }; char embedded_sl_version_str[2][12] = { "?.??", "?.??" };
char embedded_sl_version_ext[2][32]; char embedded_sl_version_ext[2][32];
RUFUS_UPDATE update = { {0,0,0}, {0,0}, NULL, NULL}; RUFUS_UPDATE update = { {0,0,0}, {0,0}, NULL, NULL};
StrArray DriveID, DriveLabel, BlockingProcess; StrArray DriveID, DriveLabel, DriveHub, BlockingProcess;
uint32_t DrivePort[MAX_DRIVES];
extern char* szStatusMessage; extern char* szStatusMessage;
static HANDLE format_thid = NULL, dialog_handle = NULL; static HANDLE format_thid = NULL, dialog_handle = NULL;
@ -1828,9 +1829,10 @@ static void InitDialog(HWND hDlg)
} }
IGNORE_RETVAL(ComboBox_SetCurSel(hDiskID, 0)); IGNORE_RETVAL(ComboBox_SetCurSel(hDiskID, 0));
// Create the string array // Create the string arrays
StrArrayCreate(&DriveID, MAX_DRIVES); StrArrayCreate(&DriveID, MAX_DRIVES);
StrArrayCreate(&DriveLabel, MAX_DRIVES); StrArrayCreate(&DriveLabel, MAX_DRIVES);
StrArrayCreate(&DriveHub, MAX_DRIVES);
StrArrayCreate(&BlockingProcess, 16); StrArrayCreate(&BlockingProcess, 16);
// Set various checkboxes // Set various checkboxes
CheckDlgButton(hDlg, IDC_QUICKFORMAT, BST_CHECKED); CheckDlgButton(hDlg, IDC_QUICKFORMAT, BST_CHECKED);
@ -2351,6 +2353,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
PostQuitMessage(0); PostQuitMessage(0);
StrArrayDestroy(&DriveID); StrArrayDestroy(&DriveID);
StrArrayDestroy(&DriveLabel); StrArrayDestroy(&DriveLabel);
StrArrayDestroy(&DriveHub);
StrArrayDestroy(&BlockingProcess); StrArrayDestroy(&BlockingProcess);
DestroyAllTooltips(); DestroyAllTooltips();
DestroyWindow(hLogDlg); DestroyWindow(hLogDlg);
@ -3351,7 +3354,7 @@ relaunch:
// Do our own event processing and process "magic" commands // Do our own event processing and process "magic" commands
while(GetMessage(&msg, NULL, 0, 0)) { while(GetMessage(&msg, NULL, 0, 0)) {
// ** ***** **** ** ******** * // ** ***** **** ** **********
// .,ABCDEFGHIJKLMNOPQRSTUVWXYZ // .,ABCDEFGHIJKLMNOPQRSTUVWXYZ
// Ctrl-A => Select the log data // Ctrl-A => Select the log data
@ -3359,6 +3362,7 @@ relaunch:
(msg.message == WM_KEYDOWN) && (msg.wParam == 'A') ) { (msg.message == WM_KEYDOWN) && (msg.wParam == 'A') ) {
// Might also need ES_NOHIDESEL property if you want to select when not active // Might also need ES_NOHIDESEL property if you want to select when not active
Edit_SetSel(hLog, 0, -1); Edit_SetSel(hLog, 0, -1);
continue;
} }
// Alt-. => Enable USB enumeration debug // Alt-. => Enable USB enumeration debug
if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == VK_OEM_PERIOD)) { if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == VK_OEM_PERIOD)) {
@ -3385,12 +3389,12 @@ relaunch:
PrintStatusTimeout(lmprintf(MSG_256), detect_fakes); PrintStatusTimeout(lmprintf(MSG_256), detect_fakes);
continue; continue;
} }
// Alt C => Force the update check to be successful // Alt-C => Cycle USB port for currently selected device
// This will set the reported current version of Rufus to 0.0.0.0 when performing an update
// check, so that it always succeeds. This is useful for translators.
if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'C')) { if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'C')) {
force_update = !force_update; int index = ComboBox_GetCurSel(hDeviceList);
PrintStatusTimeout(lmprintf(MSG_259), force_update); if (index < 0)
break;
ResetDevice(index);
continue; continue;
} }
// Alt-D => Delete the NoDriveTypeAutorun key on exit (useful if the app crashed) // Alt-D => Delete the NoDriveTypeAutorun key on exit (useful if the app crashed)
@ -3473,7 +3477,6 @@ relaunch:
PrintStatusTimeout(lmprintf(MSG_290), !disable_file_indexing); PrintStatusTimeout(lmprintf(MSG_290), !disable_file_indexing);
continue; continue;
} }
// Alt-R => Remove all the registry keys that may have been created by Rufus // Alt-R => Remove all the registry keys that may have been created by Rufus
if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'R')) { if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'R')) {
PrintStatus(2000, DeleteRegistryKey(REGKEY_HKCU, COMPANY_NAME "\\" APPLICATION_NAME)?MSG_248:MSG_249); PrintStatus(2000, DeleteRegistryKey(REGKEY_HKCU, COMPANY_NAME "\\" APPLICATION_NAME)?MSG_248:MSG_249);
@ -3525,11 +3528,20 @@ relaunch:
SHDeleteDirectoryExU(NULL, tmp_path, FOF_SILENT | FOF_NOERRORUI | FOF_NOCONFIRMATION); SHDeleteDirectoryExU(NULL, tmp_path, FOF_SILENT | FOF_NOERRORUI | FOF_NOCONFIRMATION);
continue; continue;
} }
// Alt Y => Force the update check to be successful
// This will set the reported current version of Rufus to 0.0.0.0 when performing an update
// check, so that it always succeeds. This is useful for translators.
if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'Y')) {
force_update = !force_update;
PrintStatusTimeout(lmprintf(MSG_259), force_update);
continue;
}
// Alt-Z => Zero the drive // Alt-Z => Zero the drive
if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'Z')) { if ((msg.message == WM_SYSKEYDOWN) && (msg.wParam == 'Z')) {
zero_drive = TRUE; zero_drive = TRUE;
// Simulate a button click for Start // Simulate a button click for Start
PostMessage(hDlg, WM_COMMAND, (WPARAM)IDC_START, 0); PostMessage(hDlg, WM_COMMAND, (WPARAM)IDC_START, 0);
continue;
} }
// Hazardous cheat modes require Ctrl + Alt // Hazardous cheat modes require Ctrl + Alt

View file

@ -369,11 +369,11 @@ enum WindowsVersion {
WINDOWS_UNDEFINED = -1, WINDOWS_UNDEFINED = -1,
WINDOWS_UNSUPPORTED = 0, WINDOWS_UNSUPPORTED = 0,
WINDOWS_XP = 0x51, WINDOWS_XP = 0x51,
WINDOWS_2003 = 0x52, // Also XP x64 WINDOWS_2003 = 0x52, // Also XP_64
WINDOWS_VISTA = 0x60, WINDOWS_VISTA = 0x60, // Also 2008
WINDOWS_7 = 0x61, WINDOWS_7 = 0x61, // Also 2008_R2
WINDOWS_8 = 0x62, WINDOWS_8 = 0x62, // Also 2012
WINDOWS_8_1 = 0x63, WINDOWS_8_1 = 0x63, // Also 2012_R2
WINDOWS_10_PREVIEW1 = 0x64, WINDOWS_10_PREVIEW1 = 0x64,
WINDOWS_10 = 0xA0, WINDOWS_10 = 0xA0,
WINDOWS_MAX WINDOWS_MAX
@ -460,6 +460,7 @@ extern DWORD GetResourceSize(HMODULE module, char* name, char* type, const char*
extern DWORD RunCommand(const char* cmdline, const char* dir, BOOL log); extern DWORD RunCommand(const char* cmdline, const char* dir, BOOL log);
extern BOOL CompareGUID(const GUID *guid1, const GUID *guid2); extern BOOL CompareGUID(const GUID *guid1, const GUID *guid2);
extern BOOL GetDevices(DWORD devnum); extern BOOL GetDevices(DWORD devnum);
extern BOOL ResetDevice(int index);
extern BOOL GetOpticalMedia(IMG_SAVE* img_save); extern BOOL GetOpticalMedia(IMG_SAVE* img_save);
extern BOOL SetLGP(BOOL bRestore, BOOL* bExistingKey, const char* szPath, const char* szPolicy, DWORD dwValue); extern BOOL SetLGP(BOOL bRestore, BOOL* bExistingKey, const char* szPath, const char* szPolicy, DWORD dwValue);
extern LONG GetEntryWidth(HWND hDropDown, const char* entry); extern LONG GetEntryWidth(HWND hDropDown, const char* entry);

View file

@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDD_DIALOG DIALOGEX 12, 12, 242, 376 IDD_DIALOG DIALOGEX 12, 12, 242, 376
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_ACCEPTFILES EXSTYLE WS_EX_ACCEPTFILES
CAPTION "Rufus 2.18.1200" CAPTION "Rufus 2.18.1201"
FONT 8, "Segoe UI Symbol", 400, 0, 0x0 FONT 8, "Segoe UI Symbol", 400, 0, 0x0
BEGIN BEGIN
LTEXT "Device",IDS_DEVICE_TXT,9,6,200,8 LTEXT "Device",IDS_DEVICE_TXT,9,6,200,8
@ -366,8 +366,8 @@ END
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,18,1200,0 FILEVERSION 2,18,1201,0
PRODUCTVERSION 2,18,1200,0 PRODUCTVERSION 2,18,1201,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -384,13 +384,13 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)" VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)"
VALUE "FileDescription", "Rufus" VALUE "FileDescription", "Rufus"
VALUE "FileVersion", "2.18.1200" VALUE "FileVersion", "2.18.1201"
VALUE "InternalName", "Rufus" VALUE "InternalName", "Rufus"
VALUE "LegalCopyright", "© 2011-2017 Pete Batard (GPL v3)" VALUE "LegalCopyright", "© 2011-2017 Pete Batard (GPL v3)"
VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html" VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html"
VALUE "OriginalFilename", "rufus.exe" VALUE "OriginalFilename", "rufus.exe"
VALUE "ProductName", "Rufus" VALUE "ProductName", "Rufus"
VALUE "ProductVersion", "2.18.1200" VALUE "ProductVersion", "2.18.1201"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View file

@ -284,20 +284,20 @@ void GetWindowsVersion(void)
switch (nWindowsVersion) { switch (nWindowsVersion) {
case 0x51: w = "XP"; case 0x51: w = "XP";
break; break;
case 0x52: w = (!GetSystemMetrics(89)?"2003":"2003_R2"); case 0x52: w = (!GetSystemMetrics(89)?"Server 2003":"Server 2003_R2");
break; break;
case 0x60: w = (ws?"Vista":"2008"); case 0x60: w = (ws?"Vista":"Server 2008");
break; break;
case 0x61: w = (ws?"7":"2008_R2"); case 0x61: w = (ws?"7":"Server 2008_R2");
break; break;
case 0x62: w = (ws?"8":"2012"); case 0x62: w = (ws?"8":"Server 2012");
break; break;
case 0x63: w = (ws?"8.1":"2012_R2"); case 0x63: w = (ws?"8.1":"Server 2012_R2");
break; break;
case 0x64: w = (ws?"10 (Preview 1)":"Server 10 (Preview 1)"); case 0x64: w = (ws?"10 (Preview 1)":"Server 10 (Preview 1)");
break; break;
// Starting with Windows 10 Preview 2, the major is the same as the public-facing version // Starting with Windows 10 Preview 2, the major is the same as the public-facing version
case 0xA0: w = (ws?"10":"Server 10"); case 0xA0: w = (ws?"10":"Server 2016");
break; break;
default: default:
if (nWindowsVersion < 0x51) if (nWindowsVersion < 0x51)