Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 08/30/2025 in all areas

  1. Trong

    Windows Version Detector

    Windows Version Detector v2: #include-once ; #INDEX# ======================================================================================================================= ; Title .........: Windows Version Detector v2 ; AutoIt Version : 3.3.16.1+ ; Description ...: Functions for accurately detecting Windows version for current or offline systems ; Author(s) .....: Dao Van Trong - TRONG.PRO ; Dll(s) ........: kernel32.dll, advapi32.dll, version.dll ; =============================================================================================================================== ; #CURRENT# ===================================================================================================================== ; _DetectWindowsVersion ; _OSVersion ; _GetOSVersionDetailed ; =============================================================================================================================== ; #INTERNAL_USE_ONLY# =========================================================================================================== ; __IsSystemLive ; __IsFileLocked ; __GetLiveSystemInfo ; __GetOfflineSystemInfo ; __ReadVersionFromFiles ; __ReadOfflineRegistryDirect ; __ParseVersionFromNtoskrnl ; __ParseVersionFromKernel32 ; __GetVersionNameByBuild ; __GetEditionType ; __LoadOfflineHive ; __UnloadOfflineHive ; __EnableBackupPrivilege ; __NormalizePath ; __IsValidWindowsPath ; __ExtractBuildFromPE ; __ReadFileVersionInfo ; =============================================================================================================================== ; #CONSTANTS# =================================================================================================================== #include <FileConstants.au3> #include <APIRegConstants.au3> #include <APIErrorsConstants.au3> #include <WinAPIFiles.au3> #include <Array.au3> ; Additional constants Global Const $IMAGE_DOS_SIGNATURE = 0x5A4D ; "MZ" Global Const $IMAGE_NT_SIGNATURE = 0x00004550 ; "PE\0\0" ;~ Global Const $IMAGE_FILE_MACHINE_I386 = 0x014c ;~ Global Const $IMAGE_FILE_MACHINE_AMD64 = 0x8664 ; =============================================================================================================================== ; #FUNCTION# ==================================================================================================================== ; Name ..........: _DetectWindowsVersion ; Description ...: Detects Windows version information for current or offline systems ; Syntax ........: _DetectWindowsVersion([$sWindowsPath = @WindowsDir]) ; Parameters ....: $sWindowsPath - [optional] Path to Windows directory. Default is @WindowsDir. ; Return values .: Success - Array with Windows information: ; |[0] - Product Name (e.g., "Windows 11 Pro") ; |[1] - Build Number with metadata (e.g., "22621|23H2|2009|10.0|2787") ; |[2] - Version Name (e.g., "Windows 11 Version 23H2 (2023 Update) (Build 22621.2787)") ; |[3] - Edition Type (e.g., "Professional", "Server", "Home") ; |[4] - System Type ("Live" or "Offline") ; Failure - Empty array, @error set: ; |1 - Failed to get live system info ; |2 - Failed to get offline system info ; |3 - Invalid Windows path ; Author ........: Dao Van Trong - TRONG.PRO ; Remarks .......: This function automatically detects whether the target system is live or offline ; Related .......: _OSVersion, _GetOSVersionDetailed ; =============================================================================================================================== Func _DetectWindowsVersion($sWindowsPath = @WindowsDir) Local $aResult[6] = ["", "", "", "", ""] ; [0] = ProductName, [1] = BuildNumber, [2] = VersionName, [3] = Edition, [4] = SystemType (Live/Offline), [5]=EditionID ; Validate and normalize path $sWindowsPath = __NormalizePath($sWindowsPath) If Not __IsValidWindowsPath($sWindowsPath) Then Return SetError(3, 0, $aResult) EndIf ; Determine if system is live or offline Local $bIsLive = __IsSystemLive($sWindowsPath) $aResult[4] = $bIsLive ? "Live" : "Offline" ; Get Windows information based on system type If $bIsLive Then If Not __GetLiveSystemInfo($aResult) Then Return SetError(1, 0, $aResult) EndIf Else If Not __GetOfflineSystemInfo($sWindowsPath, $aResult) Then Return SetError(2, 0, $aResult) EndIf EndIf ; Ensure we have basic information If $aResult[0] = "" Or $aResult[1] = "" Then Return SetError(2, 0, $aResult) EndIf ; Determine version name from build number $aResult[2] = __GetVersionNameByBuild($aResult[1], $aResult[0]) ; Determine edition type If $aResult[5] <> "" Then $aResult[3] = __GetEditionType($aResult[0], $aResult[5]) Else $aResult[3] = __GetEditionType($aResult[0]) EndIf Return $aResult EndFunc ;==>_DetectWindowsVersion ; #FUNCTION# ==================================================================================================================== ; Name ..........: _OSVersion ; Description ...: Returns short OS version string similar to @OSVersion macro ; Syntax ........: _OSVersion([$sWindowsPath = @WindowsDir]) ; Parameters ....: $sWindowsPath - [optional] Path to Windows directory. Default is @WindowsDir. ; Return values .: Success - Short version string: ; |Windows Client: "WIN_11", "WIN_10", "WIN_81", "WIN_8", "WIN_7", "WIN_VISTA", "WIN_XP", "WIN_XPe" ; |Windows Server: "WIN_2025", "WIN_2022", "WIN_2019", "WIN_2016", "WIN_2012R2", "WIN_2012", "WIN_2008R2", "WIN_2008", "WIN_2003" ; |Legacy: "WIN_NT4", "WIN_2000", "WIN_ME", "WIN_98SE", "WIN_98", "WIN_95" ; Failure - "UNKNOWN" ; Author ........: Dao Van Trong - TRONG.PRO ; Remarks .......: This function provides a quick way to identify OS version for conditional logic ; Related .......: _DetectWindowsVersion, _GetOSVersionDetailed ; =============================================================================================================================== Func _OSVersion($sWindowsPath = @WindowsDir) Local $aInfo = _DetectWindowsVersion($sWindowsPath) If @error Then Return "UNKNOWN" ; Parse build info Local $aBuildParts = StringSplit($aInfo[1], "|") If $aBuildParts[0] < 1 Then Return "UNKNOWN" Local $iBuild = Int($aBuildParts[1]) Local $sProductName = $aInfo[0] Local $bIsServer = (StringInStr($sProductName, "Server") > 0) ; Return appropriate short version string Select ;--- Windows NT Classic --- Case $iBuild = 528 Return "WIN_NT31" Case $iBuild = 807 Return "WIN_NT35" Case $iBuild = 1057 Return "WIN_NT351" Case $iBuild = 1381 Return "WIN_NT4" ;--- Windows 9x/ME --- Case $iBuild >= 950 And $iBuild <= 1111 Return "WIN_95" Case $iBuild = 1998 Return "WIN_98" Case $iBuild = 2222 Return "WIN_98SE" Case $iBuild = 3000 Return "WIN_ME" ;--- Windows 2000/XP/Server 2003 --- Case $iBuild = 2195 Return $bIsServer ? "WIN_2000SRV" : "WIN_2000" Case $iBuild = 2600 ; Check if it's Windows XP Embedded If StringInStr($sProductName, "Embedded") Then Return "WIN_XPe" Else Return "WIN_XP" EndIf Case $iBuild = 3790 Return $bIsServer ? "WIN_2003" : "WIN_XP64" ;--- Windows Vista/Server 2008 --- Case $iBuild >= 6000 And $iBuild <= 6003 Return $bIsServer ? "WIN_2008" : "WIN_VISTA" ;--- Windows 7/Server 2008 R2 --- Case $iBuild = 7600 Or $iBuild = 7601 Return $bIsServer ? "WIN_2008R2" : "WIN_7" ;--- Windows 8/Server 2012 --- Case $iBuild = 9200 Return $bIsServer ? "WIN_2012" : "WIN_8" ;--- Windows 8.1/Server 2012 R2 --- Case $iBuild = 9600 Return $bIsServer ? "WIN_2012R2" : "WIN_81" ;--- Windows 10/Server 2016 --- Case $iBuild >= 10240 And $iBuild <= 19045 If $bIsServer Then ; Determine server version by build If $iBuild = 14393 Then Return "WIN_2016" ElseIf $iBuild = 17763 Then Return "WIN_2019" ElseIf $iBuild = 20348 Then Return "WIN_2022" Else Return "WIN_2016" ; Default for Windows 10 era servers EndIf Else Return "WIN_10" EndIf ;--- Windows Server 2022 --- Case $iBuild = 20348 Return "WIN_2022" ;--- Windows 11/Server 2025 --- Case $iBuild >= 22000 And $iBuild <= 26200 If $bIsServer Then If $iBuild = 26100 Then Return "WIN_2025" Else Return "WIN_2022" ; Fallback for unknown server builds EndIf Else Return "WIN_11" EndIf ;--- Future versions --- Case $iBuild > 26200 Return $bIsServer ? "WIN_SRV_FUTURE" : "WIN_FUTURE" ;--- Unknown --- Case Else Return "UNKNOWN" EndSelect EndFunc ;==>_OSVersion ; #FUNCTION# ==================================================================================================================== ; Name ..........: _GetOSVersionDetailed ; Description ...: Returns detailed OS version information in a structured array format ; Syntax ........: _GetOSVersionDetailed([$sWindowsPath = @WindowsDir]) ; Parameters ....: $sWindowsPath - [optional] Path to Windows directory. Default is @WindowsDir. ; Return values .: Success - Array with detailed version information: ; |[0] - Short Version (e.g., "WIN_11", "WIN_10", "WIN_2022") ; |[1] - Build Number with UBR (e.g., "22621.2787", "19045.3448") ; |[2] - Display Version (e.g., "23H2", "22H2", "21H2" or "N/A") ; |[3] - Product Name (e.g., "Windows 11 Pro", "Windows Server 2022 Datacenter") ; |[4] - Edition Type (e.g., "Professional", "Server", "Home", "Enterprise") ; |[5] - System Type ("Live" or "Offline") ; Failure - Array with error values, @error set: ; |Same error codes as _DetectWindowsVersion ; Author ........: Dao Van Trong - TRONG.PRO ; Remarks .......: This function provides comprehensive version information for detailed analysis ; Related .......: _DetectWindowsVersion, _OSVersion ; =============================================================================================================================== Func _GetOSVersionDetailed($sWindowsPath = @WindowsDir) Local $aInfo = _DetectWindowsVersion($sWindowsPath) If @error Then Local $aResult[6] = ["UNKNOWN", "0", "N/A", "Unknown", "Unknown", "Error"] Return SetError(@error, 0, $aResult) EndIf ; Parse build info Local $aBuildParts = StringSplit($aInfo[1], "|") If $aBuildParts[0] < 1 Then Local $aResult[6] = ["UNKNOWN", "0", "N/A", "Unknown", "Unknown", "Error"] Return SetError(4, 0, $aResult) EndIf Local $iBuild = Int($aBuildParts[1]) Local $sDisplayVersion = ($aBuildParts[0] >= 2) ? $aBuildParts[2] : "" Local $sUBR = ($aBuildParts[0] >= 5) ? $aBuildParts[5] : "" ; Create detailed result array Local $aResult[6] $aResult[0] = _OSVersion($sWindowsPath) ; Short version $aResult[1] = $iBuild & ($sUBR <> "" And $sUBR <> "0" ? "." & $sUBR : "") ; Full build with UBR $aResult[2] = $sDisplayVersion <> "" ? $sDisplayVersion : "N/A" ; Display version like 23H2 $aResult[3] = $aInfo[0] ; Product name $aResult[4] = $aInfo[3] ; Edition type $aResult[5] = $aInfo[4] ; System type (Live/Offline) Return $aResult EndFunc ;==>_GetOSVersionDetailed ; #INTERNAL_USE_ONLY# =========================================================================================================== Func __IsSystemLive($sWindowsPath) ; Check if we're analyzing the current running system Local $sCurrentWin = __NormalizePath(@WindowsDir) If $sWindowsPath = $sCurrentWin Then Return True ; Check if critical system files are locked (indicating live system) Local $aSystemFiles = [ _ $sWindowsPath & "\System32\Config\SYSTEM", _ $sWindowsPath & "\System32\Config\SOFTWARE", _ $sWindowsPath & "\System32\ntoskrnl.exe" _ ] For $sFile In $aSystemFiles If __IsFileLocked($sFile) Then Return True Next Return False EndFunc ;==>__IsSystemLive Func __IsFileLocked($sFilePath) If Not FileExists($sFilePath) Then Return False ; Try to open file exclusively - if fails, it's locked Local $hFile = DllCall("kernel32.dll", "handle", "CreateFileW", _ "wstr", $sFilePath, _ "dword", $GENERIC_READ, _ "dword", 0, _ ; No sharing "ptr", 0, _ "dword", $OPEN_EXISTING, _ "dword", 0, _ "handle", 0) If @error Or $hFile[0] = -1 Then Return True DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $hFile[0]) Return False EndFunc ;==>__IsFileLocked Func __GetLiveSystemInfo(ByRef $aResult) ; Read from live registry Local $sRegPath = "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" Local $sEditionID = RegRead($sRegPath, "EditionID") $aResult[0] = RegRead($sRegPath, "ProductName") If @error Then $aResult[0] = $sEditionID ; Fallback $aResult[1] = RegRead($sRegPath, "CurrentBuildNumber") If @error Then $aResult[1] = RegRead($sRegPath, "CurrentBuild") ; Get additional version info for precise identification Local $sDisplayVersion = RegRead($sRegPath, "DisplayVersion") ; 22H2, 23H2, etc. Local $sReleaseId = RegRead($sRegPath, "ReleaseId") ; 1903, 1909, 2004, etc. Local $sCurrentVersion = RegRead($sRegPath, "CurrentVersion") ; 6.3, 10.0, etc. Local $sUBR = RegRead($sRegPath, "UBR") ; Update Build Revision ; Store additional info for precise version detection If $sDisplayVersion <> "" Then $aResult[1] &= "|" & $sDisplayVersion If $sReleaseId <> "" Then $aResult[1] &= "|" & $sReleaseId If $sCurrentVersion <> "" Then $aResult[1] &= "|" & $sCurrentVersion If $sUBR <> "" Then $aResult[1] &= "|" & $sUBR ; Thêm EditionID vào vị trí [5] $aResult[5] = $sEditionID Return ($aResult[0] <> "" And StringLen($aResult[1]) > 0) EndFunc ;==>__GetLiveSystemInfo Func __GetOfflineSystemInfo($sWindowsPath, ByRef $aResult) ; Multiple strategies for offline detection with fallback chain Local $bSuccess = False ; Strategy 1: Try to load offline registry hive (requires admin privileges) If Not $bSuccess Then $bSuccess = __ReadOfflineRegistryDirect($sWindowsPath, $aResult) EndIf ; Strategy 2: Read version from PE files (kernel32.dll, ntoskrnl.exe) If Not $bSuccess Then $bSuccess = __ReadVersionFromFiles($sWindowsPath, $aResult) EndIf ; Strategy 3: Extract build from PE headers directly If Not $bSuccess Then $bSuccess = __ExtractBuildFromPE($sWindowsPath, $aResult) EndIf Return $bSuccess EndFunc ;==>__GetOfflineSystemInfo Func __ReadOfflineRegistryDirect($sWindowsPath, ByRef $aResult) ; Try to load offline registry hive Local $sTempHive = "TEMP_SW_" & Random(1000, 9999, 1) Local $sHivePath = $sWindowsPath & "\System32\Config\SOFTWARE" If Not FileExists($sHivePath) Then Return False If __LoadOfflineHive($sHivePath, $sTempHive) Then Local $sRegPath = "HKLM\" & $sTempHive & "\Microsoft\Windows NT\CurrentVersion" Local $sEditionID = RegRead($sRegPath, "EditionID") $aResult[5] = $sEditionID $aResult[0] = RegRead($sRegPath, "ProductName") If @error Or $aResult[0] = "" Then $aResult[0] = $sEditionID EndIf $aResult[1] = RegRead($sRegPath, "CurrentBuildNumber") If @error Or $aResult[1] = "" Then $aResult[1] = RegRead($sRegPath, "CurrentBuild") EndIf ; Get additional version info for precise identification Local $sDisplayVersion = RegRead($sRegPath, "DisplayVersion") Local $sReleaseId = RegRead($sRegPath, "ReleaseId") Local $sCurrentVersion = RegRead($sRegPath, "CurrentVersion") Local $sUBR = RegRead($sRegPath, "UBR") ; Store additional info for precise version detection If $sDisplayVersion <> "" Then $aResult[1] &= "|" & $sDisplayVersion If $sReleaseId <> "" Then $aResult[1] &= "|" & $sReleaseId If $sCurrentVersion <> "" Then $aResult[1] &= "|" & $sCurrentVersion If $sUBR <> "" Then $aResult[1] &= "|" & $sUBR __UnloadOfflineHive($sTempHive) ; If registry read successful, return If $aResult[0] <> "" And $aResult[1] <> "" Then Return True EndIf Return False EndFunc ;==>__ReadOfflineRegistryDirect Func __ReadVersionFromFiles($sWindowsPath, ByRef $aResult) ; Priority order for version files Local $aFiles = [ _ $sWindowsPath & "\System32\kernel32.dll", _ $sWindowsPath & "\System32\ntoskrnl.exe", _ $sWindowsPath & "\explorer.exe", _ $sWindowsPath & "\System32\user32.dll", _ $sWindowsPath & "\System32\shell32.dll" _ ] For $sFile In $aFiles If FileExists($sFile) Then ; Enhanced version reading using version.dll If __ReadFileVersionInfo($sFile, $aResult) Then Return True EndIf EndIf Next Return False EndFunc ;==>__ReadVersionFromFiles Func __ReadFileVersionInfo($sFilePath, ByRef $aResult) ; Get version info size Local $iSize = DllCall("version.dll", "dword", "GetFileVersionInfoSizeW", "wstr", $sFilePath, "ptr", 0) If @error Or $iSize[0] = 0 Then Return False ; Allocate buffer and get version info Local $tBuffer = DllStructCreate("byte[" & $iSize[0] & "]") Local $bRet = DllCall("version.dll", "bool", "GetFileVersionInfoW", _ "wstr", $sFilePath, _ "dword", 0, _ "dword", $iSize[0], _ "ptr", DllStructGetPtr($tBuffer)) If @error Or Not $bRet[0] Then Return False ; Query fixed file info Local $tFixedInfo, $iLen Local $bQuery = DllCall("version.dll", "bool", "VerQueryValueW", _ "ptr", DllStructGetPtr($tBuffer), _ "wstr", "\", _ "ptr*", 0, _ "uint*", 0) If @error Or Not $bQuery[0] Or $bQuery[4] = 0 Then Return False ; Read VS_FIXEDFILEINFO structure $tFixedInfo = DllStructCreate("dword[13]", $bQuery[3]) ; Extract version numbers (high and low parts) Local $iFileVersionMS = DllStructGetData($tFixedInfo, 1, 3) ; dwFileVersionMS Local $iFileVersionLS = DllStructGetData($tFixedInfo, 1, 4) ; dwFileVersionLS ; Convert to version components Local $iMajor = BitShift($iFileVersionMS, 16) Local $iMinor = BitAND($iFileVersionMS, 0xFFFF) Local $iBuild = BitShift($iFileVersionLS, 16) Local $iRevision = BitAND($iFileVersionLS, 0xFFFF) ; Set build number If $iBuild > 0 Then $aResult[1] = String($iBuild) If $iRevision > 0 Then $aResult[1] &= "||||" & String($iRevision) EndIf ; Get product name from string info Local $aLanguages = __GetVersionLanguages($tBuffer) For $sLang In $aLanguages Local $sProductName = __GetVersionString($tBuffer, $sLang, "ProductName") If $sProductName <> "" Then $aResult[0] = $sProductName ExitLoop EndIf Next ; Fallback to FileDescription if ProductName is empty If $aResult[0] = "" Then For $sLang In $aLanguages Local $sFileDesc = __GetVersionString($tBuffer, $sLang, "FileDescription") If $sFileDesc <> "" Then $aResult[0] = $sFileDesc ExitLoop EndIf Next EndIf Return ($aResult[0] <> "" And $aResult[1] <> "") EndFunc ;==>__ReadFileVersionInfo Func __GetVersionLanguages($tVersionBuffer) Local $aLanguages[0] ; Query for translation table Local $bQuery = DllCall("version.dll", "bool", "VerQueryValueW", _ "ptr", DllStructGetPtr($tVersionBuffer), _ "wstr", "\VarFileInfo\Translation", _ "ptr*", 0, _ "uint*", 0) If @error Or Not $bQuery[0] Or $bQuery[4] = 0 Then ; Default to common language codes ReDim $aLanguages[2] $aLanguages[0] = "040904B0" ; English US $aLanguages[1] = "000004B0" ; Neutral Return $aLanguages EndIf ; Read translation pairs Local $iCount = $bQuery[4] / 4 ; Each entry is 4 bytes (DWORD) Local $tTranslation = DllStructCreate("dword[" & $iCount & "]", $bQuery[3]) ReDim $aLanguages[$iCount] For $i = 1 To $iCount Local $iTranslation = DllStructGetData($tTranslation, 1, $i) $aLanguages[$i - 1] = StringFormat("%04X%04X", BitAND($iTranslation, 0xFFFF), BitShift($iTranslation, 16)) Next Return $aLanguages EndFunc ;==>__GetVersionLanguages Func __GetVersionString($tVersionBuffer, $sLanguage, $sKey) Local $sSubBlock = "\StringFileInfo\" & $sLanguage & "\" & $sKey Local $bQuery = DllCall("version.dll", "bool", "VerQueryValueW", _ "ptr", DllStructGetPtr($tVersionBuffer), _ "wstr", $sSubBlock, _ "ptr*", 0, _ "uint*", 0) If @error Or Not $bQuery[0] Or $bQuery[4] = 0 Then Return "" ; Read string value Local $tString = DllStructCreate("wchar[" & $bQuery[4] & "]", $bQuery[3]) Return DllStructGetData($tString, 1) EndFunc ;==>__GetVersionString Func __ExtractBuildFromPE($sWindowsPath, ByRef $aResult) ; Try ntoskrnl.exe first as it's the kernel Local $sKernelPath = $sWindowsPath & "\System32\ntoskrnl.exe" If FileExists($sKernelPath) Then If __ParseVersionFromNtoskrnl($sKernelPath, $aResult) Then Return True EndIf ; Try kernel32.dll as fallback Local $sKernel32Path = $sWindowsPath & "\System32\kernel32.dll" If FileExists($sKernel32Path) Then If __ParseVersionFromKernel32($sKernel32Path, $aResult) Then Return True EndIf Return False EndFunc ;==>__ExtractBuildFromPE Func __ParseVersionFromNtoskrnl($sFilePath, ByRef $aResult) ; Open file for reading Local $hFile = FileOpen($sFilePath, $FO_READ + $FO_BINARY) If $hFile = -1 Then Return False ; Read DOS header Local $tDOSHeader = FileRead($hFile, 64) If @error Or BinaryLen($tDOSHeader) < 64 Then FileClose($hFile) Return False EndIf ; Check DOS signature Local $iDOSSignature = Number("0x" & StringMid($tDOSHeader, 1, 4)) If $iDOSSignature <> $IMAGE_DOS_SIGNATURE Then FileClose($hFile) Return False EndIf ; Get PE header offset Local $iPEOffset = Number("0x" & StringMid($tDOSHeader, 121, 8)) ; Seek to PE header and read FileSetPos($hFile, $iPEOffset, $FILE_BEGIN) Local $tPEHeader = FileRead($hFile, 24) If @error Or BinaryLen($tPEHeader) < 24 Then FileClose($hFile) Return False EndIf ; Check PE signature Local $iPESignature = Number("0x" & StringMid($tPEHeader, 1, 8)) If $iPESignature <> $IMAGE_NT_SIGNATURE Then FileClose($hFile) Return False EndIf ; Extract timestamp (can be used to determine build) Local $iTimestamp = Number("0x" & StringMid($tPEHeader, 17, 8)) FileClose($hFile) ; Use FileGetVersion as more reliable method Local $sVersion = FileGetVersion($sFilePath) If Not @error And $sVersion <> "" Then Local $aVer = StringSplit($sVersion, ".") If $aVer[0] >= 3 Then $aResult[1] = $aVer[3] ; Build number $aResult[0] = FileGetVersion($sFilePath, "ProductName") If @error Or $aResult[0] = "" Then $aResult[0] = FileGetVersion($sFilePath, "FileDescription") EndIf Return ($aResult[0] <> "" And $aResult[1] <> "") EndIf EndIf Return False EndFunc ;==>__ParseVersionFromNtoskrnl Func __ParseVersionFromKernel32($sFilePath, ByRef $aResult) ; Similar to ntoskrnl parsing but for kernel32.dll Local $sVersion = FileGetVersion($sFilePath) If Not @error And $sVersion <> "" Then Local $aVer = StringSplit($sVersion, ".") If $aVer[0] >= 3 Then $aResult[1] = $aVer[3] ; Build number $aResult[0] = FileGetVersion($sFilePath, "ProductName") If @error Or $aResult[0] = "" Then $aResult[0] = FileGetVersion($sFilePath, "FileDescription") EndIf Return ($aResult[0] <> "" And $aResult[1] <> "") EndIf EndIf Return False EndFunc ;==>__ParseVersionFromKernel32 Func __GetVersionNameByBuild($sBuildData, $sProductName = "") ; Parse build info and additional data If Not StringInStr($sBuildData, "|") And StringIsDigit($sBuildData) Then ; Simple build number without metadata $sBuildData &= "||||" EndIf Local $aBuildParts = StringSplit($sBuildData, "|") If $aBuildParts[0] < 1 Then Return "Unknown Windows Version" Local $iBuild = Int($aBuildParts[1]) Local $sDisplayVersion = ($aBuildParts[0] >= 2) ? $aBuildParts[2] : "" Local $sReleaseId = ($aBuildParts[0] >= 3) ? $aBuildParts[3] : "" Local $sCurrentVersion = ($aBuildParts[0] >= 4) ? $aBuildParts[4] : "" Local $sUBR = ($aBuildParts[0] >= 5) ? $aBuildParts[5] : "" Local $bIsServer = (StringInStr($sProductName, "Server") > 0) Local $sVersionName = "" ; Precise version detection based on build number and additional info Select ;--- Windows NT Classic --- Case $iBuild = 528 $sVersionName = "Windows NT 3.1" Case $iBuild = 807 $sVersionName = "Windows NT 3.5" Case $iBuild = 1057 $sVersionName = "Windows NT 3.51" Case $iBuild = 1381 $sVersionName = "Windows NT 4.0" ;--- Windows 9x/ME --- Case $iBuild >= 950 And $iBuild <= 1111 $sVersionName = ($iBuild <= 1110) ? "Windows 95" : "Windows 95 OSR2" Case $iBuild = 1998 $sVersionName = "Windows 98" Case $iBuild = 2222 $sVersionName = "Windows 98 Second Edition" Case $iBuild = 3000 $sVersionName = "Windows Millennium Edition" ;--- Windows 2000/XP/Server 2003 --- Case $iBuild = 2195 $sVersionName = $bIsServer ? "Windows 2000 Server" : "Windows 2000 Professional" Case $iBuild = 2600 $sVersionName = "Windows XP" Case $iBuild = 3790 $sVersionName = $bIsServer ? "Windows Server 2003 R2" : "Windows XP Professional x64 Edition" ;--- Windows Vista/Server 2008 --- Case $iBuild = 6000 $sVersionName = $bIsServer ? "Windows Server 2008" : "Windows Vista" Case $iBuild = 6001 $sVersionName = $bIsServer ? "Windows Server 2008 SP1" : "Windows Vista SP1" Case $iBuild = 6002 Or $iBuild = 6003 $sVersionName = $bIsServer ? "Windows Server 2008 SP2" : "Windows Vista SP2" ;--- Windows 7/Server 2008 R2 --- Case $iBuild = 7600 $sVersionName = $bIsServer ? "Windows Server 2008 R2" : "Windows 7" Case $iBuild = 7601 $sVersionName = $bIsServer ? "Windows Server 2008 R2 SP1" : "Windows 7 SP1" ;--- Windows 8/Server 2012 --- Case $iBuild = 9200 $sVersionName = $bIsServer ? "Windows Server 2012" : "Windows 8" ;--- Windows 8.1/Server 2012 R2 --- Case $iBuild = 9600 $sVersionName = $bIsServer ? "Windows Server 2012 R2" : "Windows 8.1" ;--- Windows 10 Versions --- Case $iBuild = 10240 $sVersionName = "Windows 10 Version 1507 (RTM)" Case $iBuild = 10586 $sVersionName = "Windows 10 Version 1511 (November Update)" Case $iBuild = 14393 $sVersionName = $bIsServer ? "Windows Server 2016" : "Windows 10 Version 1607 (Anniversary Update)" Case $iBuild = 15063 $sVersionName = "Windows 10 Version 1703 (Creators Update)" Case $iBuild = 16299 $sVersionName = "Windows 10 Version 1709 (Fall Creators Update)" Case $iBuild = 17134 $sVersionName = "Windows 10 Version 1803 (April 2018 Update)" Case $iBuild = 17763 $sVersionName = $bIsServer ? "Windows Server 2019" : "Windows 10 Version 1809 (October 2018 Update)" Case $iBuild = 18362 $sVersionName = "Windows 10 Version 1903 (May 2019 Update)" Case $iBuild = 18363 $sVersionName = "Windows 10 Version 1909 (November 2019 Update)" Case $iBuild >= 19041 And $iBuild <= 19045 ; Use DisplayVersion or ReleaseId for precise identification If $sDisplayVersion <> "" Then Switch $sDisplayVersion Case "20H2" $sVersionName = "Windows 10 Version 20H2 (October 2020 Update)" Case "21H1" $sVersionName = "Windows 10 Version 21H1 (May 2021 Update)" Case "21H2" $sVersionName = "Windows 10 Version 21H2 (November 2021 Update)" Case "22H2" $sVersionName = "Windows 10 Version 22H2 (2022 Update)" Case Else $sVersionName = "Windows 10 Version 2004 (May 2020 Update)" EndSwitch ElseIf $sReleaseId <> "" Then Switch $sReleaseId Case "2009" $sVersionName = "Windows 10 Version 20H2 (October 2020 Update)" Case Else $sVersionName = "Windows 10 Version 2004 (May 2020 Update)" EndSwitch Else $sVersionName = "Windows 10 Version 2004 (May 2020 Update)" EndIf ;--- Windows Server 2022 --- Case $iBuild = 20348 $sVersionName = "Windows Server 2022" ;--- Windows 11 Versions --- Case $iBuild = 22000 $sVersionName = "Windows 11 Version 21H2" Case $iBuild >= 22621 And $iBuild <= 22631 ; Use DisplayVersion for precise identification If $sDisplayVersion <> "" Then Switch $sDisplayVersion Case "22H2" $sVersionName = "Windows 11 Version 22H2 (2022 Update)" Case "23H2" $sVersionName = "Windows 11 Version 23H2 (2023 Update)" Case Else $sVersionName = "Windows 11 Version 22H2 (2022 Update)" EndSwitch Else $sVersionName = "Windows 11 Version 22H2 (2022 Update)" EndIf Case $iBuild >= 26000 And $iBuild <= 26100 $sVersionName = $bIsServer ? "Windows Server 2025" : "Windows 11 Version 24H2 (2024 Update)" ;--- Future/Insider Builds --- Case $iBuild > 26100 $sVersionName = $bIsServer ? "Windows Server (Insider Preview)" : "Windows 11 (Insider Preview)" ;--- Unknown --- Case Else ; Try to determine by product name if available If StringInStr($sProductName, "Windows 11") Then $sVersionName = "Windows 11" ElseIf StringInStr($sProductName, "Windows 10") Then $sVersionName = "Windows 10" ElseIf StringInStr($sProductName, "Server") Then $sVersionName = "Windows Server" Else $sVersionName = "Unknown Windows Version" EndIf EndSelect ; Add build number and UBR if available Local $sBuildDetails = " (Build " & $iBuild If $sUBR <> "" And $sUBR <> "0" Then $sBuildDetails &= "." & $sUBR $sBuildDetails &= ")" Return $sVersionName & $sBuildDetails EndFunc ;==>__GetVersionNameByBuild Func __GetEditionType($sProductName, $sEditionID = '') ; Prefer EditionID when provided Local $sLowerID = StringLower($sEditionID) If $sLowerID <> '' Then Switch True ; China-specific Case StringInStr($sLowerID, 'corecountryspecific') Or StringInStr($sLowerID, 'corechina') Return 'Home China' Case StringInStr($sLowerID, 'professionalcountryspecific') Or StringInStr($sLowerID, 'professionalchina') Return 'Pro China' Case StringInStr($sLowerID, 'enterprisegovchina') Return 'Enterprise Government China' Case StringInStr($sLowerID, 'enterpriseg') Or StringInStr($sLowerID, 'enterprisegovernment') Return 'Enterprise G' ; Europe N editions Case StringInStr($sLowerID, 'coren') Return 'Home N' Case StringInStr($sLowerID, 'professionaln') Return 'Pro N' Case StringInStr($sLowerID, 'enterprisen') Return 'Enterprise N' Case StringInStr($sLowerID, 'educationn') Return 'Education N' ; Korea KN editions Case StringInStr($sLowerID, 'corekn') Return 'Home KN' Case StringInStr($sLowerID, 'professionalkn') Return 'Pro KN' Case StringInStr($sLowerID, 'enterprisekn') Return 'Enterprise KN' ; LTSC / LTSB Case StringInStr($sLowerID, 'enterprises') Or _ StringInStr($sLowerID, 'enterpriseltsc') Or _ StringInStr($sLowerID, 'enterpriseltsb') Return 'Enterprise LTSC' ; IoT editions Case StringInStr($sLowerID, 'iotenterprise') Return 'IoT Enterprise' Case StringInStr($sLowerID, 'iotcore') Return 'IoT Core' ; Mobile editions Case StringInStr($sLowerID, 'mobileenterprise') Return 'Mobile Enterprise' Case StringInStr($sLowerID, 'mobile') Return 'Mobile' ; Other special SKUs Case StringInStr($sLowerID, 'mixedreality') Return 'Mixed Reality' Case StringInStr($sLowerID, 'proeducation') Return 'Pro Education' Case StringInStr($sLowerID, 'proforworkstations') Return 'Pro for Workstations' ; Generic EditionID patterns Case StringInStr($sLowerID, 'enterprise') Return 'Enterprise' Case StringInStr($sLowerID, 'professional') Return 'Professional' Case StringInStr($sLowerID, 'standard') Return 'Standard' Case StringInStr($sLowerID, 'datacenter') Return 'Datacenter' Case StringInStr($sLowerID, 'essentials') Return 'Essentials' Case StringInStr($sLowerID, 'starter') Return 'Starter' Case StringInStr($sLowerID, 'home') Return 'Home' EndSwitch EndIf ; Fallback to product name if EditionID was empty or unrecognized Local $sLower = StringLower($sProductName) ; === Windows Server Editions === If StringInStr($sLower, "server") Then ; Windows Server 2022/2025 If StringInStr($sLower, "2022") Or StringInStr($sLower, "2025") Then If StringInStr($sLower, "azure") Then Return "Server Datacenter: Azure Edition" If StringInStr($sLower, "datacenter") Then Return "Server Datacenter" If StringInStr($sLower, "standard") Then Return "Server Standard" If StringInStr($sLower, "essentials") Then Return "Server Essentials" Return "Server" EndIf ; Windows Server 2019 If StringInStr($sLower, "2019") Then If StringInStr($sLower, "essentials") Then Return "Server Essentials" If StringInStr($sLower, "datacenter") Then Return "Server Datacenter" If StringInStr($sLower, "standard") Then Return "Server Standard" Return "Server 2019" EndIf ; Windows Server 2016 If StringInStr($sLower, "2016") Then If StringInStr($sLower, "essentials") Then Return "Server Essentials" If StringInStr($sLower, "datacenter") Then Return "Server Datacenter" If StringInStr($sLower, "standard") Then Return "Server Standard" If StringInStr($sLower, "foundation") Then Return "Server Foundation" Return "Server 2016" EndIf ; Generic Server If StringInStr($sLower, "essentials") Then Return "Server Essentials" If StringInStr($sLower, "datacenter") Then Return "Server Datacenter" If StringInStr($sLower, "standard") Then Return "Server Standard" Return "Server" EndIf ; === Windows Client Editions === ; Windows 11 Editions If StringInStr($sLower, "windows 11") Then If StringInStr($sLower, "mixed reality") Then Return "Mixed Reality" If StringInStr($sLower, "iot enterprise") Then Return "IoT Enterprise" If StringInStr($sLower, "pro education") Then Return "Pro Education" If StringInStr($sLower, "pro for workstations") Then Return "Pro for Workstations" If StringInStr($sLower, "enterprise") Then Return "Enterprise" If StringInStr($sLower, "education") Then Return "Education" If StringInStr($sLower, "professional") Or StringInStr($sLower, "pro") Then Return "Pro" If StringInStr($sLower, "home") Then Return "Home" Return "Windows 11" EndIf ; Windows 10 Editions If StringInStr($sLower, "windows 10") Then If StringInStr($sLower, "mobile enterprise") Then Return "Mobile Enterprise" If StringInStr($sLower, "mobile") Then Return "Mobile" If StringInStr($sLower, "iot enterprise") Then Return "IoT Enterprise" If StringInStr($sLower, "iot core") Then Return "IoT Core" If StringInStr($sLower, "pro education") Then Return "Pro Education" If StringInStr($sLower, "pro for workstations") Then Return "Pro for Workstations" If StringInStr($sLower, "enterprise") Then Return "Enterprise" If StringInStr($sLower, "education") Then Return "Education" If StringInStr($sLower, "professional") Or StringInStr($sLower, "pro") Then Return "Pro" If StringInStr($sLower, "home") Then Return "Home" Return "Windows 10" EndIf ; Windows 8.1 Editions If StringInStr($sLower, "windows 8.1") Or StringInStr($sLower, "8.1") Then If StringInStr($sLower, "rt") Then Return "RT" If StringInStr($sLower, "single language") Then Return "Single Language" If StringInStr($sLower, "pro") And StringInStr($sLower, "media center") Then Return "Pro with Media Center" If StringInStr($sLower, "enterprise") Then Return "Enterprise" If StringInStr($sLower, "professional") Or StringInStr($sLower, "pro") Then Return "Pro" If StringInStr($sLower, "core") Or StringInStr($sLower, "windows 8.1") Then Return "Core" Return "Windows 8.1" EndIf ; Windows 8 Editions If StringInStr($sLower, "windows 8") Then If StringInStr($sLower, "rt") Then Return "RT" If StringInStr($sLower, "enterprise") Then Return "Enterprise" If StringInStr($sLower, "professional") Or StringInStr($sLower, "pro") Then Return "Pro" If StringInStr($sLower, "core") Or StringInStr($sLower, "windows 8") Then Return "Core" Return "Windows 8" EndIf ; Windows 7 Editions If StringInStr($sLower, "windows 7") Or StringInStr($sLower, "7") Then If StringInStr($sLower, "starter") Then Return "Starter" If StringInStr($sLower, "home basic") Then Return "Home Basic" If StringInStr($sLower, "home premium") Then Return "Home Premium" If StringInStr($sLower, "ultimate") Then Return "Ultimate" If StringInStr($sLower, "enterprise") Then Return "Enterprise" If StringInStr($sLower, "professional") Then Return "Professional" Return "Windows 7" EndIf ; === Generic Edition Detection (for older versions or fallback) === ; Special editions If StringInStr($sLower, "embedded") Then Return "Embedded" If StringInStr($sLower, "starter") Then Return "Starter" If StringInStr($sLower, "ultimate") Then Return "Ultimate" If StringInStr($sLower, "mixed reality") Then Return "Mixed Reality" ; IoT editions If StringInStr($sLower, "iot enterprise") Then Return "IoT Enterprise" If StringInStr($sLower, "iot core") Then Return "IoT Core" If StringInStr($sLower, "iot") Then Return "IoT" ; Mobile editions If StringInStr($sLower, "mobile enterprise") Then Return "Mobile Enterprise" If StringInStr($sLower, "mobile") Then Return "Mobile" ; RT editions If StringInStr($sLower, "rt") Then Return "RT" ; Workstation editions If StringInStr($sLower, "pro for workstations") Then Return "Pro for Workstations" If StringInStr($sLower, "workstation") Then Return "Workstation" ; Education editions If StringInStr($sLower, "pro education") Then Return "Pro Education" If StringInStr($sLower, "education") Then Return "Education" ; Enterprise editions If StringInStr($sLower, "enterprise") Then Return "Enterprise" ; Professional editions If StringInStr($sLower, "professional") Or StringInStr($sLower, "pro") Then ; Check for specific Pro variants If StringInStr($sLower, "media center") Then Return "Pro with Media Center" Return "Professional" EndIf ; Home editions If StringInStr($sLower, "home premium") Then Return "Home Premium" If StringInStr($sLower, "home basic") Then Return "Home Basic" If StringInStr($sLower, "home") Then Return "Home" ; Core/Standard editions If StringInStr($sLower, "core") Then Return "Core" If StringInStr($sLower, "single language") Then Return "Single Language" ; Legacy editions If StringInStr($sLower, "millennium") Then Return "Millennium Edition" If StringInStr($sLower, "second edition") Then Return "Second Edition" ; Default fallback If $sProductName <> "" Then Return "Standard" Return "Unknown" EndFunc ;==>__GetEditionType Func __LoadOfflineHive($sHiveFile, $sTempKey) If Not FileExists($sHiveFile) Then Return False ; Enable backup privilege __EnableBackupPrivilege() Local $aRet = DllCall("advapi32.dll", "long", "RegLoadKeyW", _ "handle", $HKEY_LOCAL_MACHINE, _ "wstr", $sTempKey, _ "wstr", $sHiveFile) Return (IsArray($aRet) And $aRet[0] = $ERROR_SUCCESS) EndFunc ;==>__LoadOfflineHive Func __UnloadOfflineHive($sTempKey) Local $aRet = DllCall("advapi32.dll", "long", "RegUnloadKeyW", _ "handle", $HKEY_LOCAL_MACHINE, _ "wstr", $sTempKey) Return (IsArray($aRet) And $aRet[0] = $ERROR_SUCCESS) EndFunc ;==>__UnloadOfflineHive Func __EnableBackupPrivilege() Local $hToken, $aRet ; Open process token $aRet = DllCall("advapi32.dll", "bool", "OpenProcessToken", _ "handle", DllCall("kernel32.dll", "handle", "GetCurrentProcess")[0], _ "dword", 0x00000020, _ ; TOKEN_ADJUST_PRIVILEGES "handle*", 0) If @error Or Not $aRet[0] Then Return False $hToken = $aRet[3] ; Lookup privilege value Local $tLUID = DllStructCreate("int64") $aRet = DllCall("advapi32.dll", "bool", "LookupPrivilegeValueW", _ "ptr", 0, _ "wstr", "SeBackupPrivilege", _ "ptr", DllStructGetPtr($tLUID)) If @error Or Not $aRet[0] Then DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $hToken) Return False EndIf ; Adjust token privileges Local $tPriv = DllStructCreate("dword;int64;dword") DllStructSetData($tPriv, 1, 1) ; PrivilegeCount DllStructSetData($tPriv, 2, DllStructGetData($tLUID, 1)) ; LUID DllStructSetData($tPriv, 3, 0x00000002) ; SE_PRIVILEGE_ENABLED DllCall("advapi32.dll", "bool", "AdjustTokenPrivileges", _ "handle", $hToken, _ "bool", False, _ "ptr", DllStructGetPtr($tPriv), _ "dword", 0, _ "ptr", 0, _ "ptr", 0) DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $hToken) Return True EndFunc ;==>__EnableBackupPrivilege Func __NormalizePath($sPath) Local Const $MAX_PATH = 260 Local $tBuffer = DllStructCreate("wchar[" & $MAX_PATH & "]") Local $aRet = DllCall("kernel32.dll", "dword", "GetFullPathNameW", _ "wstr", $sPath, _ "dword", $MAX_PATH, _ "ptr", DllStructGetPtr($tBuffer), _ "ptr", 0) If @error Or $aRet[0] = 0 Then Return StringReplace($sPath, "/", "\") Local $sNormalized = DllStructGetData($tBuffer, 1) Return StringRegExpReplace($sNormalized, "\\+$", "") ; Remove trailing backslashes EndFunc ;==>__NormalizePath Func __IsValidWindowsPath($sPath) ; Check if the path exists and contains essential Windows files/directories If Not FileExists($sPath) Then Return False ; Check for essential Windows directories Local $aEssentialDirs = [ _ $sPath & "\System32", _ $sPath & "\System32\Config" _ ] For $sDir In $aEssentialDirs If Not FileExists($sDir) Then Return False Next ; Check for essential Windows files Local $aEssentialFiles = [ _ $sPath & "\System32\kernel32.dll", _ $sPath & "\System32\ntdll.dll" _ ] Local $iFoundFiles = 0 For $sFile In $aEssentialFiles If FileExists($sFile) Then $iFoundFiles += 1 Next ; At least one essential file should exist Return ($iFoundFiles > 0) EndFunc ;==>__IsValidWindowsPath ; =============================================================================================================================== ; TEST SECTION - Enhanced testing with better error handling ; =============================================================================================================================== #Region TEST If Not @Compiled Then _TEST() Func _TEST() ConsoleWrite("!=== Enhanced Windows Version Detector Test ===" & @CRLF & @CRLF) ; Test 1: Current system detection ConsoleWrite("> 1. Testing current system detection:" & @CRLF) Local $aResult = _DetectWindowsVersion() If @error Then ConsoleWrite("! ERROR: " & @error & "- Failed to detect current system" & @CRLF) Else ConsoleWrite("- ProductName: " & $aResult[0] & @CRLF) ConsoleWrite("- BuildNumber: " & $aResult[1] & @CRLF) ConsoleWrite("- VersionName: " & $aResult[2] & @CRLF) ConsoleWrite("- Edition: " & $aResult[3] & @CRLF) ConsoleWrite("- SystemType: " & $aResult[4] & @CRLF) ; Display EditionID if available If UBound($aResult) > 5 And $aResult[5] <> "" Then ConsoleWrite("- EditionID: " & $aResult[5] & @CRLF) EndIf EndIf ConsoleWrite(@CRLF) ; Test 2: Short OS version ConsoleWrite("> 2. Testing short OS version:" & @CRLF) Local $sOS = _OSVersion() ConsoleWrite("- OS Version: " & $sOS & @CRLF) ConsoleWrite(@CRLF) ; Test 3: Detailed OS version ConsoleWrite("> 3. Testing detailed OS version:" & @CRLF) Local $aDetailed = _GetOSVersionDetailed() If @error Then ConsoleWrite("! ERROR: " & @error & "- Failed to get detailed info" & @CRLF) Else ConsoleWrite("- Short Version: " & $aDetailed[0] & @CRLF) ConsoleWrite("- Build with UBR: " & $aDetailed[1] & @CRLF) ConsoleWrite("- Display Version: " & $aDetailed[2] & @CRLF) ConsoleWrite("- Product Name: " & $aDetailed[3] & @CRLF) ConsoleWrite("- Edition Type: " & $aDetailed[4] & @CRLF) ConsoleWrite("- System Type: " & $aDetailed[5] & @CRLF) EndIf ConsoleWrite(@CRLF) ; Test 4: Special Edition Detection Test ConsoleWrite("> 4. Testing special edition detection capabilities:" & @CRLF) ; Test various EditionID scenarios Local $aTestCases = [ _ ["Windows 10 Home", "CoreCountrySpecific", "Home China"], _ ["Windows 11 Home", "CoreCountrySpecific", "Home China"], _ ["Windows 10 Pro", "ProfessionalCountrySpecific", "Pro China"], _ ["Windows 10 Home N", "CoreN", "Home N"], _ ["Windows 11 Pro N", "ProfessionalN", "Pro N"], _ ["Windows 10 Home KN", "CoreKN", "Home KN"], _ ["Windows 10 Enterprise G", "EnterpriseG", "Enterprise G"], _ ["Windows 10 Enterprise LTSC", "EnterpriseS", "Enterprise LTSC"], _ ["Windows 10 IoT Enterprise", "IoTEnterprise", "IoT Enterprise"] _ ] For $i = 0 To UBound($aTestCases) - 1 Local $sProductName = $aTestCases[$i][0] Local $sEditionID = $aTestCases[$i][1] Local $sExpected = $aTestCases[$i][2] Local $sResult = __GetEditionType($sProductName, $sEditionID) Local $sStatus = ($sResult = $sExpected) ? "âś“ PASS" : "âś— FAIL" ConsoleWrite("+ " & $sStatus & " | ProductName: " & $sProductName & " | EditionID: " & $sEditionID & " | Result: " & $sResult & @CRLF) Next ConsoleWrite(@CRLF) ; Test 5: Offline system detection (if another Windows installation exists) ConsoleWrite("> 5. Testing offline system detection:" & @CRLF) Local $aOfflinePaths = ["C:\Windows", "D:\Windows", "E:\Windows"] ; Common offline paths Local $bFoundOffline = False For $sOfflinePath In $aOfflinePaths If FileExists($sOfflinePath) And $sOfflinePath <> @WindowsDir Then ConsoleWrite("- Testing path: " & $sOfflinePath & @CRLF) Local $aOfflineResult = _DetectWindowsVersion($sOfflinePath) If @error Then ConsoleWrite("! ERROR: " & @error & "- Failed to detect offline system at " & $sOfflinePath & @CRLF) Else ConsoleWrite("- Offline ProductName: " & $aOfflineResult[0] & @CRLF) ConsoleWrite("- Offline BuildNumber: " & $aOfflineResult[1] & @CRLF) ConsoleWrite("- Offline VersionName: " & $aOfflineResult[2] & @CRLF) ConsoleWrite("- Offline Edition: " & $aOfflineResult[3] & @CRLF) ConsoleWrite("- Offline SystemType: " & $aOfflineResult[4] & @CRLF) ; Display EditionID if available If UBound($aOfflineResult) > 5 And $aOfflineResult[5] <> "" Then ConsoleWrite("- Offline EditionID: " & $aOfflineResult[5] & @CRLF) EndIf EndIf ConsoleWrite(@CRLF) $bFoundOffline = True ExitLoop ; Test only the first found offline Windows EndIf Next If Not $bFoundOffline Then ConsoleWrite("- No offline Windows installations found for testing." & @CRLF & @CRLF) EndIf ; Test 6: Error handling - invalid path ConsoleWrite("> 6. Testing error handling with invalid path:" & @CRLF) Local $aInvalidResult = _DetectWindowsVersion("C:\NonExistentPath") ConsoleWrite("- Error code: " & @error & @CRLF) ConsoleWrite("- Result array size: " & UBound($aInvalidResult) & @CRLF) ConsoleWrite(@CRLF) ; Test 7: China-specific detection demonstration ConsoleWrite("> 7. China-specific Windows detection capabilities:" & @CRLF) ConsoleWrite("- âś“ CoreCountrySpecific EditionID → Home China" & @CRLF) ConsoleWrite("- âś“ ProfessionalCountrySpecific EditionID → Pro China" & @CRLF) ConsoleWrite("- âś“ EnterpriseG EditionID → Enterprise G (Government)" & @CRLF) ConsoleWrite("- âś“ EnterpriseGovChina EditionID → Enterprise Government China" & @CRLF) ConsoleWrite("- âś“ Supports N editions (Europe) and KN editions (Korea)" & @CRLF) ConsoleWrite("- âś“ Enhanced regional market detection" & @CRLF) ConsoleWrite(@CRLF) ConsoleWrite("!=== Test Complete ===" & @CRLF) EndFunc ;==>_TEST #EndRegion TEST RT: !=== Enhanced Windows Version Detector Test === > 1. Testing current system detection: - ProductName: Windows 10 Enterprise LTSC 2021 - BuildNumber: 19045|22H2|2009|6.3|6216 - VersionName: Windows 10 Version 22H2 (2022 Update) (Build 19045.6216) - Edition: Enterprise LTSC - SystemType: Live - EditionID: EnterpriseS > 2. Testing short OS version: - OS Version: WIN_10 > 3. Testing detailed OS version: - Short Version: WIN_10 - Build with UBR: 19045.6216 - Display Version: 22H2 - Product Name: Windows 10 Enterprise LTSC 2021 - Edition Type: Enterprise LTSC - System Type: Live > 4. Testing special edition detection capabilities: + âś“ PASS | ProductName: Windows 10 Home | EditionID: CoreCountrySpecific | Result: Home China + âś“ PASS | ProductName: Windows 11 Home | EditionID: CoreCountrySpecific | Result: Home China + âś“ PASS | ProductName: Windows 10 Pro | EditionID: ProfessionalCountrySpecific | Result: Pro China + âś“ PASS | ProductName: Windows 10 Home N | EditionID: CoreN | Result: Home N + âś“ PASS | ProductName: Windows 11 Pro N | EditionID: ProfessionalN | Result: Pro N + âś“ PASS | ProductName: Windows 10 Home KN | EditionID: CoreKN | Result: Home KN + âś“ PASS | ProductName: Windows 10 Enterprise G | EditionID: EnterpriseG | Result: Enterprise G + âś“ PASS | ProductName: Windows 10 Enterprise LTSC | EditionID: EnterpriseS | Result: Enterprise LTSC + âś“ PASS | ProductName: Windows 10 IoT Enterprise | EditionID: IoTEnterprise | Result: IoT Enterprise > 5. Testing offline system detection: - Testing path: C:\Offline\Windows - Offline ProductName: Microsoft® Windows® Operating System - Offline BuildNumber: 19041||||3636 - Offline VersionName: Windows 10 Version 2004 (May 2020 Update) (Build 19041.3636) - Offline Edition: Standard - Offline SystemType: Offline > 6. Testing error handling with invalid path: - Error code: 3 - Result array size: 6 > 7. China-specific Windows detection capabilities: - âś“ CoreCountrySpecific EditionID ? Home China - âś“ ProfessionalCountrySpecific EditionID ? Pro China - âś“ EnterpriseG EditionID ? Enterprise G (Government) - âś“ EnterpriseGovChina EditionID ? Enterprise Government China - âś“ Supports N editions (Europe) and KN editions (Korea) - âś“ Enhanced regional market detection !=== Test Complete === UDF v1: #include-once ; #INDEX# ======================================================================================================================= ; Title .........: Windows Version Detector V1 ; AutoIt Version : 3.3.16.1+ ; Description ...: Functions for accurately detecting Windows version for current or offline systems ; Author(s) .....: Dao Van Trong - TRONG.PRO ; Dll(s) ........: kernel32.dll, advapi32.dll ; =============================================================================================================================== ; #CURRENT# ===================================================================================================================== ; _DetectWindowsVersion ; _OSVersion ; _GetOSVersionDetailed ; =============================================================================================================================== ; #INTERNAL_USE_ONLY# =========================================================================================================== ; __IsSystemLive ; __IsFileLocked ; __GetLiveSystemInfo ; __GetOfflineSystemInfo ; __ReadVersionFromFiles ; __GetVersionNameByBuild ; __GetEditionType ; __LoadOfflineHive ; __UnloadOfflineHive ; __EnableBackupPrivilege ; __NormalizePath ; =============================================================================================================================== ; #CONSTANTS# =================================================================================================================== #include <FileConstants.au3> #include <APIRegConstants.au3> #include <APIErrorsConstants.au3> ;~ Global Const $HKEY_LOCAL_MACHINE = 0x80000002 ;~ Global Const $ERROR_SUCCESS = 0 ;~ Global Const $GENERIC_READ = 0x80000000 ;~ Global Const $FILE_SHARE_READ = 0x00000001 ;~ Global Const $OPEN_EXISTING = 3 ; =============================================================================================================================== ; #FUNCTION# ==================================================================================================================== ; Name ..........: _DetectWindowsVersion ; Description ...: Detects Windows version information for current or offline systems ; Syntax ........: _DetectWindowsVersion([$sWindowsPath = @WindowsDir]) ; Parameters ....: $sWindowsPath - [optional] Path to Windows directory. Default is @WindowsDir. ; Return values .: Success - Array with Windows information: ; |[0] - Product Name (e.g., "Windows 11 Pro") ; |[1] - Build Number with metadata (e.g., "22621|23H2|2009|10.0|2787") ; |[2] - Version Name (e.g., "Windows 11 Version 23H2 (2023 Update) (Build 22621.2787)") ; |[3] - Edition Type (e.g., "Professional", "Server", "Home") ; |[4] - System Type ("Live" or "Offline") ; Failure - Empty array, @error set: ; |1 - Failed to get live system info ; |2 - Failed to get offline system info ; Author ........: Dao Van Trong - TRONG.PRO ; Remarks .......: This function automatically detects whether the target system is live or offline ; Related .......: _OSVersion, _GetOSVersionDetailed ; =============================================================================================================================== Func _DetectWindowsVersion($sWindowsPath = @WindowsDir) Local $aResult[5] = ["", "", "", "", ""] ; [0] = ProductName, [1] = BuildNumber, [2] = VersionName, [3] = Edition, [4] = SystemType (Live/Offline) ; Normalize path $sWindowsPath = __NormalizePath($sWindowsPath) ; Determine if system is live or offline Local $bIsLive = __IsSystemLive($sWindowsPath) $aResult[4] = $bIsLive ? "Live" : "Offline" ; Get Windows information based on system type If $bIsLive Then If Not __GetLiveSystemInfo($aResult) Then Return SetError(1, 0, $aResult) EndIf Else If Not __GetOfflineSystemInfo($sWindowsPath, $aResult) Then Return SetError(2, 0, $aResult) EndIf EndIf ; Determine version name from build number $aResult[2] = __GetVersionNameByBuild($aResult[1], $aResult[0]) ; Determine edition type $aResult[3] = __GetEditionType($aResult[0]) Return $aResult EndFunc ;==>_DetectWindowsVersion ; #FUNCTION# ==================================================================================================================== ; Name ..........: _OSVersion ; Description ...: Returns short OS version string similar to @OSVersion macro ; Syntax ........: _OSVersion([$sWindowsPath = @WindowsDir]) ; Parameters ....: $sWindowsPath - [optional] Path to Windows directory. Default is @WindowsDir. ; Return values .: Success - Short version string: ; |Windows Client: "WIN_11", "WIN_10", "WIN_81", "WIN_8", "WIN_7", "WIN_VISTA", "WIN_XP", "WIN_XPe" ; |Windows Server: "WIN_2025", "WIN_2022", "WIN_2019", "WIN_2016", "WIN_2012R2", "WIN_2012", "WIN_2008R2", "WIN_2008", "WIN_2003" ; |Legacy: "WIN_NT4", "WIN_2000", "WIN_ME", "WIN_98SE", "WIN_98", "WIN_95" ; Failure - "UNKNOWN" ; Author ........: Dao Van Trong - TRONG.PRO ; Remarks .......: This function provides a quick way to identify OS version for conditional logic ; Related .......: _DetectWindowsVersion, _GetOSVersionDetailed ; =============================================================================================================================== Func _OSVersion($sWindowsPath = @WindowsDir) Local $aInfo = _DetectWindowsVersion($sWindowsPath) If @error Then Return "UNKNOWN" ; Parse build info Local $aBuildParts = StringSplit($aInfo[1], "|") Local $iBuild = Int($aBuildParts[1]) Local $sProductName = $aInfo[0] Local $bIsServer = (StringInStr($sProductName, "Server") > 0) ; Return appropriate short version string Select ;--- Windows NT Classic --- Case $iBuild = 528 Return "WIN_NT31" Case $iBuild = 807 Return "WIN_NT35" Case $iBuild = 1057 Return "WIN_NT351" Case $iBuild = 1381 Return "WIN_NT4" ;--- Windows 9x/ME --- Case $iBuild >= 950 And $iBuild <= 1111 Return "WIN_95" Case $iBuild = 1998 Return "WIN_98" Case $iBuild = 2222 Return "WIN_98SE" Case $iBuild = 3000 Return "WIN_ME" ;--- Windows 2000/XP/Server 2003 --- Case $iBuild = 2195 Return $bIsServer ? "WIN_2000SRV" : "WIN_2000" Case $iBuild = 2600 ; Check if it's Windows XP Embedded If StringInStr($sProductName, "Embedded") Then Return "WIN_XPe" Else Return "WIN_XP" EndIf Case $iBuild = 3790 Return $bIsServer ? "WIN_2003" : "WIN_XP64" ;--- Windows Vista/Server 2008 --- Case $iBuild >= 6000 And $iBuild <= 6003 Return $bIsServer ? "WIN_2008" : "WIN_VISTA" ;--- Windows 7/Server 2008 R2 --- Case $iBuild = 7600 Or $iBuild = 7601 Return $bIsServer ? "WIN_2008R2" : "WIN_7" ;--- Windows 8/Server 2012 --- Case $iBuild = 9200 Return $bIsServer ? "WIN_2012" : "WIN_8" ;--- Windows 8.1/Server 2012 R2 --- Case $iBuild = 9600 Return $bIsServer ? "WIN_2012R2" : "WIN_81" ;--- Windows 10/Server 2016 --- Case $iBuild >= 10240 And $iBuild <= 19045 If $bIsServer Then ; Determine server version by build If $iBuild = 14393 Then Return "WIN_2016" ElseIf $iBuild = 17763 Then Return "WIN_2019" Else Return "WIN_2016" ; Default for Windows 10 era servers EndIf Else Return "WIN_10" EndIf ;--- Windows Server 2022 --- Case $iBuild = 20348 Return "WIN_2022" ;--- Windows 11/Server 2025 --- Case $iBuild >= 22000 And $iBuild <= 26100 If $bIsServer Then If $iBuild = 26100 Then Return "WIN_2025" Else Return "WIN_2022" ; Fallback for unknown server builds EndIf Else Return "WIN_11" EndIf ;--- Future versions --- Case $iBuild > 26100 Return $bIsServer ? "WIN_SRV_FUTURE" : "WIN_FUTURE" ;--- Unknown --- Case Else Return "UNKNOWN" EndSelect EndFunc ;==>_OSVersion ; #FUNCTION# ==================================================================================================================== ; Name ..........: _GetOSVersionDetailed ; Description ...: Returns detailed OS version information in a structured array format ; Syntax ........: _GetOSVersionDetailed([$sWindowsPath = @WindowsDir]) ; Parameters ....: $sWindowsPath - [optional] Path to Windows directory. Default is @WindowsDir. ; Return values .: Success - Array with detailed version information: ; |[0] - Short Version (e.g., "WIN_11", "WIN_10", "WIN_2022") ; |[1] - Build Number with UBR (e.g., "22621.2787", "19045.3448") ; |[2] - Display Version (e.g., "23H2", "22H2", "21H2" or "N/A") ; |[3] - Product Name (e.g., "Windows 11 Pro", "Windows Server 2022 Datacenter") ; |[4] - Edition Type (e.g., "Professional", "Server", "Home", "Enterprise") ; |[5] - System Type ("Live" or "Offline") ; Failure - Array with error values, @error set: ; |Same error codes as _DetectWindowsVersion ; Author ........: Dao Van Trong - TRONG.PRO ; Remarks .......: This function provides comprehensive version information for detailed analysis ; Related .......: _DetectWindowsVersion, _OSVersion ; =============================================================================================================================== Func _GetOSVersionDetailed($sWindowsPath = @WindowsDir) Local $aInfo = _DetectWindowsVersion($sWindowsPath) If @error Then Local $aResult[6] = ["UNKNOWN", "0", "N/A", "Unknown", "Unknown", "Error"] Return SetError(@error, 0, $aResult) EndIf ; Parse build info Local $aBuildParts = StringSplit($aInfo[1], "|") Local $iBuild = Int($aBuildParts[1]) Local $sDisplayVersion = ($aBuildParts[0] >= 2) ? $aBuildParts[2] : "" Local $sUBR = ($aBuildParts[0] >= 5) ? $aBuildParts[5] : "" ; Create detailed result array Local $aResult[6] $aResult[0] = _OSVersion($sWindowsPath) ; Short version $aResult[1] = $iBuild & ($sUBR <> "" And $sUBR <> "0" ? "." & $sUBR : "") ; Full build with UBR $aResult[2] = $sDisplayVersion <> "" ? $sDisplayVersion : "N/A" ; Display version like 23H2 $aResult[3] = $aInfo[0] ; Product name $aResult[4] = $aInfo[3] ; Edition type $aResult[5] = $aInfo[4] ; System type (Live/Offline) Return $aResult EndFunc ;==>_GetOSVersionDetailed ; #INTERNAL_USE_ONLY# =========================================================================================================== Func __IsSystemLive($sWindowsPath) ; Check if we're analyzing the current running system Local $sCurrentWin = __NormalizePath(@WindowsDir) If $sWindowsPath = $sCurrentWin Then Return True ; Check if critical system files are locked (indicating live system) Local $aSystemFiles = [ _ $sWindowsPath & "\System32\Config\SYSTEM", _ $sWindowsPath & "\System32\Config\SOFTWARE", _ $sWindowsPath & "\System32\ntoskrnl.exe" _ ] For $sFile In $aSystemFiles If __IsFileLocked($sFile) Then Return True Next Return False EndFunc ;==>__IsSystemLive Func __IsFileLocked($sFilePath) If Not FileExists($sFilePath) Then Return False ; Try to open file exclusively - if fails, it's locked Local $hFile = DllCall("kernel32.dll", "handle", "CreateFileW", _ "wstr", $sFilePath, _ "dword", $GENERIC_READ, _ "dword", 0, _ ; No sharing "ptr", 0, _ "dword", $OPEN_EXISTING, _ "dword", 0, _ "handle", 0) If @error Or $hFile[0] = -1 Then Return True DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $hFile[0]) Return False EndFunc ;==>__IsFileLocked Func __GetLiveSystemInfo(ByRef $aResult) ; Read from live registry Local $sRegPath = "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" $aResult[0] = RegRead($sRegPath, "ProductName") If @error Then $aResult[0] = RegRead($sRegPath, "EditionID") ; Fallback $aResult[1] = RegRead($sRegPath, "CurrentBuildNumber") If @error Then $aResult[1] = RegRead($sRegPath, "CurrentBuild") ; Get additional version info for precise identification Local $sDisplayVersion = RegRead($sRegPath, "DisplayVersion") ; 22H2, 23H2, etc. Local $sReleaseId = RegRead($sRegPath, "ReleaseId") ; 1903, 1909, 2004, etc. Local $sCurrentVersion = RegRead($sRegPath, "CurrentVersion") ; 6.3, 10.0, etc. Local $sUBR = RegRead($sRegPath, "UBR") ; Update Build Revision ; Store additional info for precise version detection If $sDisplayVersion <> "" Then $aResult[1] &= "|" & $sDisplayVersion If $sReleaseId <> "" Then $aResult[1] &= "|" & $sReleaseId If $sCurrentVersion <> "" Then $aResult[1] &= "|" & $sCurrentVersion If $sUBR <> "" Then $aResult[1] &= "|" & $sUBR Return ($aResult[0] <> "" And StringLen($aResult[1]) > 0) EndFunc ;==>__GetLiveSystemInfo Func __GetOfflineSystemInfo($sWindowsPath, ByRef $aResult) ; Try to load offline registry hive Local $sTempHive = "TEMP_SW_" & Random(1000, 9999, 1) Local $sHivePath = $sWindowsPath & "\System32\Config\SOFTWARE" If __LoadOfflineHive($sHivePath, $sTempHive) Then Local $sRegPath = "HKLM\" & $sTempHive & "\Microsoft\Windows NT\CurrentVersion" $aResult[0] = RegRead($sRegPath, "ProductName") If @error Then $aResult[0] = RegRead($sRegPath, "EditionID") $aResult[1] = RegRead($sRegPath, "CurrentBuildNumber") If @error Then $aResult[1] = RegRead($sRegPath, "CurrentBuild") ; Get additional version info for precise identification Local $sDisplayVersion = RegRead($sRegPath, "DisplayVersion") Local $sReleaseId = RegRead($sRegPath, "ReleaseId") Local $sCurrentVersion = RegRead($sRegPath, "CurrentVersion") Local $sUBR = RegRead($sRegPath, "UBR") ; Store additional info for precise version detection If $sDisplayVersion <> "" Then $aResult[1] &= "|" & $sDisplayVersion If $sReleaseId <> "" Then $aResult[1] &= "|" & $sReleaseId If $sCurrentVersion <> "" Then $aResult[1] &= "|" & $sCurrentVersion If $sUBR <> "" Then $aResult[1] &= "|" & $sUBR __UnloadOfflineHive($sTempHive) ; If registry read successful, return If $aResult[0] <> "" And StringLen($aResult[1]) > 0 Then Return True EndIf ; Fallback to file version reading Return __ReadVersionFromFiles($sWindowsPath, $aResult) EndFunc ;==>__GetOfflineSystemInfo Func __ReadVersionFromFiles($sWindowsPath, ByRef $aResult) ; Priority order for version files Local $aFiles = [ _ $sWindowsPath & "\System32\kernel32.dll", _ $sWindowsPath & "\System32\ntoskrnl.exe", _ $sWindowsPath & "\explorer.exe", _ $sWindowsPath & "\System32\user32.dll" _ ] For $sFile In $aFiles If FileExists($sFile) Then ; Get product name If $aResult[0] = "" Then $aResult[0] = FileGetVersion($sFile, "ProductName") If @error Then $aResult[0] = FileGetVersion($sFile, "FileDescription") EndIf ; Get version info Local $sVersion = FileGetVersion($sFile) If Not @error And $sVersion <> "" Then Local $aVer = StringSplit($sVersion, ".") If $aVer[0] >= 3 Then $aResult[1] = $aVer[3] ; Build number is the 3rd component Return True EndIf EndIf EndIf Next Return False EndFunc ;==>__ReadVersionFromFiles Func __GetVersionNameByBuild($sBuildData, $sProductName = "") ; Parse build info and additional data If Not StringInStr($sBuildData, "|") Then Return SetError(-1, 0, '') Local $aBuildParts = StringSplit($sBuildData, "|") Local $iBuild = Int($aBuildParts[1]) Local $sDisplayVersion = ($aBuildParts[0] >= 2) ? $aBuildParts[2] : "" Local $sReleaseId = ($aBuildParts[0] >= 3) ? $aBuildParts[3] : "" Local $sCurrentVersion = ($aBuildParts[0] >= 4) ? $aBuildParts[4] : "" Local $sUBR = ($aBuildParts[0] >= 5) ? $aBuildParts[5] : "" Local $bIsServer = (StringInStr($sProductName, "Server") > 0) Local $sVersionName = "" ; Precise version detection based on build number and additional info Select ;--- Windows NT Classic --- Case $iBuild = 528 $sVersionName = "Windows NT 3.1" Case $iBuild = 807 $sVersionName = "Windows NT 3.5" Case $iBuild = 1057 $sVersionName = "Windows NT 3.51" Case $iBuild = 1381 $sVersionName = "Windows NT 4.0" ;--- Windows 9x/ME --- Case $iBuild >= 950 And $iBuild <= 1111 $sVersionName = ($iBuild <= 1110) ? "Windows 95" : "Windows 95 OSR2" Case $iBuild = 1998 $sVersionName = "Windows 98" Case $iBuild = 2222 $sVersionName = "Windows 98 Second Edition" Case $iBuild = 3000 $sVersionName = "Windows Millennium Edition" ;--- Windows 2000/XP/Server 2003 --- Case $iBuild = 2195 $sVersionName = $bIsServer ? "Windows 2000 Server" : "Windows 2000 Professional" Case $iBuild = 2600 $sVersionName = "Windows XP" Case $iBuild = 3790 $sVersionName = $bIsServer ? "Windows Server 2003 R2" : "Windows XP Professional x64 Edition" ;--- Windows Vista/Server 2008 --- Case $iBuild = 6000 $sVersionName = $bIsServer ? "Windows Server 2008" : "Windows Vista" Case $iBuild = 6001 $sVersionName = $bIsServer ? "Windows Server 2008 SP1" : "Windows Vista SP1" Case $iBuild = 6002 Or $iBuild = 6003 $sVersionName = $bIsServer ? "Windows Server 2008 SP2" : "Windows Vista SP2" ;--- Windows 7/Server 2008 R2 --- Case $iBuild = 7600 $sVersionName = $bIsServer ? "Windows Server 2008 R2" : "Windows 7" Case $iBuild = 7601 $sVersionName = $bIsServer ? "Windows Server 2008 R2 SP1" : "Windows 7 SP1" ;--- Windows 8/Server 2012 --- Case $iBuild = 9200 $sVersionName = $bIsServer ? "Windows Server 2012" : "Windows 8" ;--- Windows 8.1/Server 2012 R2 --- Case $iBuild = 9600 $sVersionName = $bIsServer ? "Windows Server 2012 R2" : "Windows 8.1" ;--- Windows 10 Versions --- Case $iBuild = 10240 $sVersionName = "Windows 10 Version 1507 (RTM)" Case $iBuild = 10586 $sVersionName = "Windows 10 Version 1511 (November Update)" Case $iBuild = 14393 $sVersionName = $bIsServer ? "Windows Server 2016" : "Windows 10 Version 1607 (Anniversary Update)" Case $iBuild = 15063 $sVersionName = "Windows 10 Version 1703 (Creators Update)" Case $iBuild = 16299 $sVersionName = "Windows 10 Version 1709 (Fall Creators Update)" Case $iBuild = 17134 $sVersionName = "Windows 10 Version 1803 (April 2018 Update)" Case $iBuild = 17763 $sVersionName = $bIsServer ? "Windows Server 2019" : "Windows 10 Version 1809 (October 2018 Update)" Case $iBuild = 18362 $sVersionName = "Windows 10 Version 1903 (May 2019 Update)" Case $iBuild = 18363 $sVersionName = "Windows 10 Version 1909 (November 2019 Update)" Case $iBuild >= 19041 And $iBuild <= 19045 ; Use DisplayVersion or ReleaseId for precise identification If $sDisplayVersion <> "" Then Switch $sDisplayVersion Case "20H2" $sVersionName = "Windows 10 Version 20H2 (October 2020 Update)" Case "21H1" $sVersionName = "Windows 10 Version 21H1 (May 2021 Update)" Case "21H2" $sVersionName = "Windows 10 Version 21H2 (November 2021 Update)" Case "22H2" $sVersionName = "Windows 10 Version 22H2 (2022 Update)" Case Else $sVersionName = "Windows 10 Version 2004 (May 2020 Update)" EndSwitch ElseIf $sReleaseId <> "" Then Switch $sReleaseId Case "2009" $sVersionName = "Windows 10 Version 20H2 (October 2020 Update)" Case Else $sVersionName = "Windows 10 Version 2004 (May 2020 Update)" EndSwitch Else $sVersionName = "Windows 10 Version 2004 (May 2020 Update)" EndIf ;--- Windows Server 2022 --- Case $iBuild = 20348 $sVersionName = "Windows Server 2022" ;--- Windows 11 Versions --- Case $iBuild = 22000 $sVersionName = "Windows 11 Version 21H2" Case $iBuild >= 22621 And $iBuild <= 22631 ; Use DisplayVersion for precise identification If $sDisplayVersion <> "" Then Switch $sDisplayVersion Case "22H2" $sVersionName = "Windows 11 Version 22H2 (2022 Update)" Case "23H2" $sVersionName = "Windows 11 Version 23H2 (2023 Update)" Case Else $sVersionName = "Windows 11 Version 22H2 (2022 Update)" EndSwitch Else $sVersionName = "Windows 11 Version 22H2 (2022 Update)" EndIf Case $iBuild = 26100 $sVersionName = $bIsServer ? "Windows Server 2025" : "Windows 11 Version 24H2 (2024 Update)" ;--- Future/Insider Builds --- Case $iBuild > 26100 $sVersionName = $bIsServer ? "Windows Server (Insider Preview)" : "Windows 11 (Insider Preview)" ;--- Unknown --- Case Else $sVersionName = "Unknown Windows Version" EndSelect ; Add build number and UBR if available Local $sBuildDetails = " (Build " & $iBuild If $sUBR <> "" And $sUBR <> "0" Then $sBuildDetails &= "." & $sUBR $sBuildDetails &= ")" Return $sVersionName & $sBuildDetails EndFunc ;==>__GetVersionNameByBuild Func __GetEditionType($sProductName) Local $sLower = StringLower($sProductName) ; === Windows Server Editions === If StringInStr($sLower, "server") Then ; Windows Server 2022 If StringInStr($sLower, "2022") Then If StringInStr($sLower, "azure") Then Return "Server Datacenter: Azure Edition" If StringInStr($sLower, "datacenter") Then Return "Server Datacenter" If StringInStr($sLower, "standard") Then Return "Server Standard" Return "Server 2022" EndIf ; Windows Server 2019 If StringInStr($sLower, "2019") Then If StringInStr($sLower, "essentials") Then Return "Server Essentials" If StringInStr($sLower, "datacenter") Then Return "Server Datacenter" If StringInStr($sLower, "standard") Then Return "Server Standard" Return "Server 2019" EndIf ; Windows Server 2016 If StringInStr($sLower, "2016") Then If StringInStr($sLower, "essentials") Then Return "Server Essentials" If StringInStr($sLower, "datacenter") Then Return "Server Datacenter" If StringInStr($sLower, "standard") Then Return "Server Standard" If StringInStr($sLower, "foundation") Then Return "Server Foundation" Return "Server 2016" EndIf ; Generic Server If StringInStr($sLower, "essentials") Then Return "Server Essentials" If StringInStr($sLower, "datacenter") Then Return "Server Datacenter" If StringInStr($sLower, "standard") Then Return "Server Standard" Return "Server" EndIf ; === Windows Client Editions === ; Windows 11 Editions If StringInStr($sLower, "windows 11") Then If StringInStr($sLower, "mixed reality") Then Return "Mixed Reality" If StringInStr($sLower, "iot enterprise") Then Return "IoT Enterprise" If StringInStr($sLower, "pro education") Then Return "Pro Education" If StringInStr($sLower, "pro for workstations") Then Return "Pro for Workstations" If StringInStr($sLower, "enterprise") Then Return "Enterprise" If StringInStr($sLower, "education") Then Return "Education" If StringInStr($sLower, "professional") Or StringInStr($sLower, "pro") Then Return "Pro" If StringInStr($sLower, "home") Then Return "Home" Return "Windows 11" EndIf ; Windows 10 Editions If StringInStr($sLower, "windows 10") Then If StringInStr($sLower, "mobile enterprise") Then Return "Mobile Enterprise" If StringInStr($sLower, "mobile") Then Return "Mobile" If StringInStr($sLower, "iot enterprise") Then Return "IoT Enterprise" If StringInStr($sLower, "iot core") Then Return "IoT Core" If StringInStr($sLower, "pro education") Then Return "Pro Education" If StringInStr($sLower, "pro for workstations") Then Return "Pro for Workstations" If StringInStr($sLower, "enterprise") Then Return "Enterprise" If StringInStr($sLower, "education") Then Return "Education" If StringInStr($sLower, "professional") Or StringInStr($sLower, "pro") Then Return "Pro" If StringInStr($sLower, "home") Then Return "Home" Return "Windows 10" EndIf ; Windows 8.1 Editions If StringInStr($sLower, "windows 8.1") Or StringInStr($sLower, "8.1") Then If StringInStr($sLower, "rt") Then Return "RT" If StringInStr($sLower, "single language") Then Return "Single Language" If StringInStr($sLower, "pro") And StringInStr($sLower, "media center") Then Return "Pro with Media Center" If StringInStr($sLower, "enterprise") Then Return "Enterprise" If StringInStr($sLower, "professional") Or StringInStr($sLower, "pro") Then Return "Pro" If StringInStr($sLower, "core") Or StringInStr($sLower, "windows 8.1") Then Return "Core" Return "Windows 8.1" EndIf ; Windows 8 Editions If StringInStr($sLower, "windows 8") Then If StringInStr($sLower, "rt") Then Return "RT" If StringInStr($sLower, "enterprise") Then Return "Enterprise" If StringInStr($sLower, "professional") Or StringInStr($sLower, "pro") Then Return "Pro" If StringInStr($sLower, "core") Or StringInStr($sLower, "windows 8") Then Return "Core" Return "Windows 8" EndIf ; Windows 7 Editions If StringInStr($sLower, "windows 7") Or StringInStr($sLower, "7") Then If StringInStr($sLower, "starter") Then Return "Starter" If StringInStr($sLower, "home basic") Then Return "Home Basic" If StringInStr($sLower, "home premium") Then Return "Home Premium" If StringInStr($sLower, "ultimate") Then Return "Ultimate" If StringInStr($sLower, "enterprise") Then Return "Enterprise" If StringInStr($sLower, "professional") Then Return "Professional" Return "Windows 7" EndIf ; === Generic Edition Detection (for older versions or fallback) === ; Special editions If StringInStr($sLower, "embedded") Then Return "Embedded" If StringInStr($sLower, "starter") Then Return "Starter" If StringInStr($sLower, "ultimate") Then Return "Ultimate" If StringInStr($sLower, "mixed reality") Then Return "Mixed Reality" ; IoT editions If StringInStr($sLower, "iot enterprise") Then Return "IoT Enterprise" If StringInStr($sLower, "iot core") Then Return "IoT Core" If StringInStr($sLower, "iot") Then Return "IoT" ; Mobile editions If StringInStr($sLower, "mobile enterprise") Then Return "Mobile Enterprise" If StringInStr($sLower, "mobile") Then Return "Mobile" ; RT editions If StringInStr($sLower, "rt") Then Return "RT" ; Workstation editions If StringInStr($sLower, "pro for workstations") Then Return "Pro for Workstations" If StringInStr($sLower, "workstation") Then Return "Workstation" ; Education editions If StringInStr($sLower, "pro education") Then Return "Pro Education" If StringInStr($sLower, "education") Then Return "Education" ; Enterprise editions If StringInStr($sLower, "enterprise") Then Return "Enterprise" ; Professional editions If StringInStr($sLower, "professional") Or StringInStr($sLower, "pro") Then ; Check for specific Pro variants If StringInStr($sLower, "media center") Then Return "Pro with Media Center" Return "Professional" EndIf ; Home editions If StringInStr($sLower, "home premium") Then Return "Home Premium" If StringInStr($sLower, "home basic") Then Return "Home Basic" If StringInStr($sLower, "home") Then Return "Home" ; Core/Standard editions If StringInStr($sLower, "core") Then Return "Core" If StringInStr($sLower, "single language") Then Return "Single Language" ; Legacy editions If StringInStr($sLower, "millennium") Then Return "Millennium Edition" If StringInStr($sLower, "second edition") Then Return "Second Edition" ; Default fallback If $sProductName <> "" Then Return "Standard" Return "Unknown" EndFunc ;==>__GetEditionType Func __LoadOfflineHive($sHiveFile, $sTempKey) If Not FileExists($sHiveFile) Then Return False ; Enable backup privilege __EnableBackupPrivilege() Local $aRet = DllCall("advapi32.dll", "long", "RegLoadKeyW", _ "handle", $HKEY_LOCAL_MACHINE, _ "wstr", $sTempKey, _ "wstr", $sHiveFile) Return (IsArray($aRet) And $aRet[0] = $ERROR_SUCCESS) EndFunc ;==>__LoadOfflineHive Func __UnloadOfflineHive($sTempKey) Local $aRet = DllCall("advapi32.dll", "long", "RegUnloadKeyW", _ "handle", $HKEY_LOCAL_MACHINE, _ "wstr", $sTempKey) Return (IsArray($aRet) And $aRet[0] = $ERROR_SUCCESS) EndFunc ;==>__UnloadOfflineHive Func __EnableBackupPrivilege() Local $hToken, $aRet ; Open process token $aRet = DllCall("advapi32.dll", "bool", "OpenProcessToken", _ "handle", DllCall("kernel32.dll", "handle", "GetCurrentProcess")[0], _ "dword", 0x00000020, _ ; TOKEN_ADJUST_PRIVILEGES "handle*", 0) If @error Or Not $aRet[0] Then Return False $hToken = $aRet[3] ; Lookup privilege value Local $tLUID = DllStructCreate("int64") $aRet = DllCall("advapi32.dll", "bool", "LookupPrivilegeValueW", _ "ptr", 0, _ "wstr", "SeBackupPrivilege", _ "ptr", DllStructGetPtr($tLUID)) If @error Or Not $aRet[0] Then DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $hToken) Return False EndIf ; Adjust token privileges Local $tPriv = DllStructCreate("dword;int64;dword") DllStructSetData($tPriv, 1, 1) ; PrivilegeCount DllStructSetData($tPriv, 2, DllStructGetData($tLUID, 1)) ; LUID DllStructSetData($tPriv, 3, 0x00000002) ; SE_PRIVILEGE_ENABLED DllCall("advapi32.dll", "bool", "AdjustTokenPrivileges", _ "handle", $hToken, _ "bool", False, _ "ptr", DllStructGetPtr($tPriv), _ "dword", 0, _ "ptr", 0, _ "ptr", 0) DllCall("kernel32.dll", "bool", "CloseHandle", "handle", $hToken) Return True EndFunc ;==>__EnableBackupPrivilege Func __NormalizePath($sPath) Local Const $MAX_PATH = 260 Local $tBuffer = DllStructCreate("wchar[" & $MAX_PATH & "]") Local $aRet = DllCall("kernel32.dll", "dword", "GetFullPathNameW", _ "wstr", $sPath, _ "dword", $MAX_PATH, _ "ptr", DllStructGetPtr($tBuffer), _ "ptr", 0) If @error Or $aRet[0] = 0 Then Return StringReplace($sPath, "/", "\") Local $sNormalized = DllStructGetData($tBuffer, 1) Return StringRegExpReplace($sNormalized, "\\+$", "") ; Remove trailing backslashes EndFunc ;==>__NormalizePath #Region TEST ;#include "WindowsVersionDetector.au3" ; Basic usage Local $sOS = _OSVersion() If $sOS = "WIN_10" Then ; Windows 10 logic ConsoleWrite("!_OSVersion: " & $sOS & @CRLF) ElseIf $sOS = "WIN_11" Then ; Windows 11 logic ConsoleWrite("!_OSVersion: " & $sOS & @CRLF) EndIf ; Offline system Local $aOffline = _GetOSVersionDetailed("X:\Windows") ConsoleWrite("!Offline OS: " & $aOffline[0] & " Build: " & $aOffline[1] & @CRLF) ; Details Local $aDetailed = _GetOSVersionDetailed() ConsoleWrite("+Current OS: " & $aDetailed[0] & " Build: " & $aDetailed[1] & @CRLF) Local $aResult = _DetectWindowsVersion("C:\Windows") ConsoleWrite('- ProductName: ' & $aResult[0] & @CRLF & '- BuildNumber: ' & $aResult[1] & @CRLF & '- VersionName: ' & $aResult[2] & @CRLF & '- Edition: ' & $aResult[3] & @CRLF & '- SystemType: ' & $aResult[4] & @CRLF) #EndRegion TEST RT: !_OSVersion: WIN_10 !Offline OS: UNKNOWN Build: 0 +Current OS: WIN_10 Build: 19045.6216 - ProductName: Windows 10 Enterprise LTSC 2021 - BuildNumber: 19045|22H2|2009|6.3|6216 - VersionName: Windows 10 Version 22H2 (2022 Update) (Build 19045.6216) - Edition: Enterprise - SystemType: Live
    3 points
  2. ..I see..., I screwed up. It was never gonna do a thing. Ok, here is the patch. look for the hints and amend with the changes: ... Global $g__Context_Autoit = 0, $g__iShowMenuRet = 0 ; <<<<<<< look for this line ... If $g__iShowMenuRet Then $Msg = $g__iShowMenuRet $g__iShowMenuRet = 0 EndIf If $g__Context_Autoit Then ; <<<<<<< look for this line ... Case $g_ctrl_CloneGui[0] ; <<<<<<< look for this line GUICtrlSetState($g_ctrl_CloneGui[0], $GUI_DISABLE) $g__iShowMenuRet = ShowMenu($hForm, $g_ctrl_CloneGui[0], $g_ctrl_CloneGui[2]) GUICtrlSetState($g_ctrl_CloneGui[0], $GUI_ENABLE) ... ShowMenu() used to return value to .... wait for it ..., no one/no thing ...so it now has a new and shiny $g__iShowMenuRet = ShowMenu() to receive the user choice. I'll upload the changes next week
    1 point
  3. lol, thanks I'm busy with time sensitive matters. I have a script that needs to be done before tomorrow, and 2 more before Friday. So I'll look at it right after am done with those 3 scripts
    1 point
  4. MattyD

    WinRT - WinUI3

    Okeydoke no problems - thanks for the compliment So Before we get to back to grid, here's a bonus post on the button code as I didn't really explain this earlier. Local $pInner Local $pButton_Fact = _WinRT_GetActivationFactory("Microsoft.UI.Xaml.Controls.Button", $sIID_IButtonFactory) Local $pButton = IButtonFactory_CreateInstance($pButton_Fact, 0, $pInner) Firstly a quick explanation on parameters for the factory (0, and $pInner) - these are the "baseInterface" and "innerInterface" respectively... The factory for all controls look like this because they are "Composable objects", and on the subject Microsoft tells us: Runtime classes optionally support composition—the ability for multiple class instances to be combined into what appears to be a single object from the outside. WinRT uses composition as a form of runtime class inheritance. so there you go - composition is a technique to smoosh two classes together. If we keep going through the article we find: ... take the example that Button composes Control, which in turn composes UIElement.... ... Building on the earlier Button -> Control -> UIElement example, the button class would be activating by calling one of its composition factory methods and passing null for the outer parameter. Button would in turn activate a Control instance, passing a reference to itself as the outer parameter.... So from all that, I'm reading we shouldn't specify the "baseinterface" for a control. I assume there could technically be other controls based on a "button" - these would internally pass themselves as a button's baseInterface on creation (if they actually exist!). The "InnerIntnerface" param on the other hand is outputted from the factory - so this is why we define the $pInner variable. I'm still not 100% across all this to be fair, but its probably all we need to know at this point in time... Anyway, with the factory out of the way, we'll get on to setting the button text. Some example XAML looks like this: <Button x:Name="btn1" Content="Push Me" Click="OnClick"/> And the first few interfaces of the control look like this: Class: Microsoft.UI.Xaml.Controls.Button {216C183D-D07A-5AA5-B8A4-0300A2683E87} - IButton {65714269-2473-5327-A652-0EA6BCE7F403} - IButtonBase {07E81761-11B2-52AE-8F8B-4D53D2B5900A} - IContentControl IContentControl looks like the right place for the "Content" property, so lets inspect that. - We can see the following methods look promising. Method : get_Content Fn : value = get_Content() P0 : value type: System.Object Method : put_Content Fn : put_Content(In value) P0 : value type: void P1 : value type: System.Object But System.Object could be anything... so how do we know what object to provide? Shall we do a IContentControl_GetContent and see what object lives there? Its a good idea in theory, but we just get a null ptr because we haven't set anything yet!. Unfortunately the Microsoft IContentControl documentation only gives you a bunch of XML examples, so that's no use either. So after getting nowhere on the internet, I knew "Windows.Foundation" was probably a good place to have a dig around. I basically just thumbed through the explorer until I found something that looked right. The PropertyValue object looks very close to a variant struct - and that seemed like a good fit for a generic sounding thing called IContentControl::put_Content. Give that a go and... _WinRT_SwitchInterface($pButton, $sIID_IContentControl) Local $pProp_Fact = _WinRT_GetActivationFactory("Windows.Foundation.PropertyValue", $sIID_IPropertyValueStatics) Local $pProp = IPropertyValueStatics_CreateString($pProp_Fact, "Push Me") IContentControl_SetContent($pButton, $pProp) yep, like magic it worked. Creating the event handler was actually a much easier task. We've created delegates before with other controls so that process was simple enough by now. If anyone needs to catch up - the underlying principal to creating these are probably best explained here. The only difference from that example is that the WinRT delegates are a bit more constrained. They'll take 2 parameters (apart from itself), a pointer to the "sender" object, and one called "arguments". ;Setup the delegate for handling button pushes. Global $__hQueryInterface = DllCallbackRegister("__QueryInterface", "long", "ptr;ptr;ptr") Global $__hAddRef = DllCallbackRegister("__AddRef", "long", "ptr") Global $__hRelease = DllCallbackRegister("__Release", "long", "ptr") Global $tBtnClick_VTab = DllStructCreate("ptr pFunc[4]") $tBtnClick_VTab.pFunc(1) = DllCallbackGetPtr($__hQueryInterface) $tBtnClick_VTab.pFunc(2) = DllCallbackGetPtr($__hAddRef) $tBtnClick_VTab.pFunc(3) = DllCallbackGetPtr($__hRelease) Global $hBtnClick = DllCallbackRegister("BtnClick", "none", "ptr;ptr;ptr") $tBtnClick_VTab.pFunc(4) = DllCallbackGetPtr($hBtnClick) Global $tBtnClick = DllStructCreate("ptr pVTab;int iRefCnt") $tBtnClick.pVTab = DllStructGetPtr($tBtnClick_VTab) $tBtnClick.iRefCnt = 1 Global $pBtnClick = DllStructGetPtr($tBtnClick) Func BtnClick($pThis, $pSender, $pArgs) #forceref $pThis, $pSender, $pArgs Local $pSrc = IRoutedEventArgs_GetOriginalSource($pArgs) ; $pSrc is the same as $pSender. MsgBox(0, "TestWin", "You are pushing my buttons.") EndFunc Func __QueryInterface($pThis, $pIID, $ppObj) Local $hResult = $S_OK If Not $ppObj Then $hResult = $E_POINTER ElseIf _WinAPI_StringFromGUID($pIID) = $sIID_IUnknown Then DllStructSetData(DllStructCreate("ptr", $ppObj), 1, $pThis) __AddRef($pThis) Else $hResult = $E_NOINTERFACE EndIf Return $hResult EndFunc Func __AddRef($pThis) Local $tThis = DllStructCreate("ptr pVTab;int iRefCnt", $pThis) $tThis.iRefCnt += 1 Return $tThis.iRefCnt EndFunc Func __Release($pThis) Local $tThis = DllStructCreate("ptr pVTab;int iRefCnt", $pThis) $tThis.iRefCnt -= 1 Return $tThis.iRefCnt EndFunc Looking though IButtonBase, it is very clear how to register our delegate. For reference, All add_* and remove_* functions are related to registering delegates! Method : add_Click Fn : token = add_Click(In handler) P0 : token type: Windows.Foundation.EventRegistrationToken P1 : handler type: Microsoft.UI.Xaml.RoutedEventHandler Method : remove_Click Fn : remove_Click(In token) P0 : token type: void P1 : token type: Windows.Foundation.EventRegistrationToken And that translates to: _WinRT_SwitchInterface($pButton, $sIID_IButtonBase) IButtonBase_AddHdlrClick($pButton, $pBtnClick) Finally, by inspecting RoutedEventHandler we can see that the "arguments" param will be an Microsoft.UI.Xaml.RoutedEventArgs object which looks like this. TypeDef : Microsoft.UI.Xaml.RoutedEventArgs Extends : System.Object Composable : Microsoft.UI.Xaml.IRoutedEventArgsFactory (Default) Interface : Microsoft.UI.Xaml.IRoutedEventArgs Property : OriginalSource Method : .ctor Fn : .ctor() P0 : type: void Method : get_OriginalSource Fn : value = get_OriginalSource() P0 : value type: System.Object
    1 point
  5. Heres a function for searching for a bitmap within another bitmap. The heart of it is written assembly (source included) and working pretty quick I feel. I have included an example which is pretty basic and should be easily enough for anyone to get the concept. You will be given a small blue window that will take a screencapture of that size: It will then take a full screenshot and highlight all locations that it found Please let me know if you have any issues or questions. Thanks! Update 8/5/2019: Rewrote for fasmg. Added full source with everything needed to modify BmpSearch_8-5-2019.7z BmpSearch.zip GAMERS - Asking for help with ANY kind of game automation is against the forum rules. DON'T DO IT.
    1 point
  6. Code below searches a certain bitmap (all supported formats of GDI+) picture in another picture or on screen (after saved as bitmap) not depending on 3rd party dll's Pentium 4 of 3 GHz takes * about 4.5 seconds on a 1280*1024 desktop to find a 260*260 bitmap. Partial match 0.5 seconds * about 1 seconds on a 1280*1024 desktop to find a 20*17 bitmap. Partial match 0.5 seconds Some references I used to come up with this solution * code to get a smaller bitmap: http://www.autoitscript.com/forum/index.ph...=pixelchecksum) * Pixelchecksum seems not to do the full job but in combination with below it can become very fast: http://www.autoitscript.com/forum/index.ph...l=pixelchecksum * A pixel by pixel searcher http://www.autoitscript.com/forum/index.ph...&hl=compare * UDF Get or Read Pixel from Memory * GetDIBits Not fully finished but all comments/enhancements are appreciated * Save to file or directly on special window description SCREEN, [ACTIVE] or name of picture file * Searching is done on a line by line basis instead of pixel by pixel * Bitmap to search in example program should be fully visible to work for first 3 examples as it are screenshots taken. Same applies for 3 in AUTOIT homepage * Speed depends on the size of the bitmap you are searching for but is about linear and < 5 seconds * Some doubts on left and top returned (sometimes seems to be off by 1 or several pixels) * Transparency (when search letters on a different background) http://www.winprog.org/tutorial/transparency.html * Match quicker by doing a bitblt with srcinvert when first line is found (instead of checking line by line) * $BMP1 and $BMP2 to be HBITMAP handle as input instead of filenames (will make searching within partial screen Sample to work * Install pics.zip into %temp% / Save BMP attachments for example to @tempdir (%temp%) for example to work Sample code #Region includes Opt('MustDeclareVars', 1) ;#include <GUIConstants.au3> #include <GDIPlus.au3> #Include <ScreenCapture.au3> #include <string.au3> #include <WinAPI.au3> #include <WindowsConstants.au3> #EndRegion ;~ If debugging output is wanted to understand and analyse the algorithm put this on true/false ;~ Const $bDebug=True Const $bDebug=False #region example ;~ Const $cMatchLinePercentage=0.97 ;Minimum matching, put on 1 if exact matching is needed on all lines Const $cMatchLinePercentage=0.99 ;Minimum matching, put on 1 if exact matching is needed on all lines ;~ Constants for type of picture matching const $c24RGBFullMatch=1 ;Load as 24 bits and full match const $c24RGBPartialMatch=2 ;Load as 24 bits and partial match const $c16RGBFullMatch=3 ;Load as 16 bits and full match const $c16RGBPartialMatch=4 ;Load as 16 bits and partial match ; ** Example start ** ; Screen samples Global Const $Bitmap1Filename = @scriptDir & "\FULLSCREEN.BMP" Global Const $Bitmap2Filename = @scriptDir & "\CALCULATOR.BMP" Global Const $Bitmap3Filename = @scriptDir & "\BACKSPACE.BMP" ;BMP samples Global Const $Bitmap4Filename = @scriptDir & "\7WDS_BW.BMP" Global Const $Bitmap5Filename = @scriptDir & "\SEVEN_BW.BMP" Global Const $Bitmap6Filename = @scriptDir & "\CAT_BW.BMP" Global Const $Bitmap7Filename = @scriptDir & "\AUTOIT3.bmp";Make sure the homepage of autoit with 3 is visible local $calcHWND, $begin, $pos, $aWinPos, $aWinCSize, $start local $pBitmap, $BitmapData, $pixelFormat Opt("WinTitleMatchMode", 4);Matching windows by advanced options ; Initialize GDI+ library _GDIPlus_Startup () ;Calculator, make sure its started $calcHWND = WinGetHandle("[REGEXPCLASS:.*Calc.*]") If $calcHWND = "" Then Run("calc.exe") Sleep(2000) $calcHWND = WinGetHandle("[REGEXPCLASS:.*Calc.*]") EndIf If $calcHWND = "" Then consolewrite("Using active window which is 99% sure equal to the calculator window" & @LF ) $calcHWND = WinGetHandle("[ACTIVE]") EndIf consolewrite($calcHWND) ;Capture the calculator screen $begin = TimerInit() winactivate($calcHWND) Sleep(1000) _ScreenCapture_CaptureWnd($Bitmap2Filename, $calcHWND,0,0,-1,-1,False) ConsoleWrite("Saving calculator window " & TimerDiff($begin) & " milliseconds " & @LF) winmove("[ACTIVE]","",223,341) ;~ Get the base information of the calc window $aWinPos = WinGetPos($calchWnd) $aWinCSize = WinGetClientSize($calchWnd) Sleep(500) ;Capture an area within the calculator area backspace $begin = TimerInit() $pos=controlgetpos("","","[CLASS:Button; TEXT:BACKSPACE]") ;~ $pos=controlgetpos("","","[CLASS:Button; INSTANCE:2]") if @error=1 Then consolewrite("Not recognizing so we are on a windows 10 environment most likely, fix by providing manual controls" & @CRLF) consolewrite("Read frequently asked questions in Wiki number 31 and use simplespy" & @CRLF) local $pos[4] ;~ Backspace button <394;693;75;74> ;~ Button 2 <315;927;75;74> $pos[0]=394 $pos[1]=693 $pos[2]=75 $pos[3]=74 ;~ Func _UIA_DrawRect($tLeft, $tRight, $tTop, $tBottom, $color = 0xFF, $PenWidth = 4) _UIA_DrawRect($pos[0],$pos[0]+$pos[2],$pos[1],$pos[1]+$pos[2]) ;~ Fix quick and dirty as simplespy gives screencoordinates not relative to window $pos[0]=394 - $aWinpos[0] $pos[1]=693 - $aWinpos[1] EndIf ;Q&D calculation of offsets to capture _ScreenCapture_Capture($Bitmap3Filename, ($aWinPos[0]+$pos[0]) + ($aWinPos[2] - $aWinCSize[0])-3, ($awinpos[1]+$pos[1])+ ($aWinPos[3]-$aWinCSize[1])-3, $aWinPos[0]+$pos[0]+$pos[2]+3, ($awinpos[1]+$pos[1])+ ($aWinPos[3]-$aWinCSize[1])+$pos[3]-3,false) ConsoleWrite("Saving backspacebutton " & TimerDiff($begin) & " milliseconds " & @LF) ; Capture full screen $begin = TimerInit() _ScreenCapture_Capture($Bitmap1Filename,0,0,-1,-1,False) ConsoleWrite("Saving full screen took " & TimerDiff($begin) & " milliseconds " & @LF) Sleep(500) func filegetname($f) local $i $i=stringinstr($f,"\",false,-1) if $i > 0 Then return stringmid($f,$i+1) Else return $f EndIf EndFunc ;Do the actual find Func FindTester($BMP1, $BMP2, $Bool) local $tResult $start = TimerInit() $tResult=findBMP($BMP1,$BMP2, $Bool) ConsoleWrite($tResult & " " & FileGetName($BMP2) & " in " & FileGetName($BMP1) & " ** matchtype " & $Bool & " time elapsed: " & TimerDiff($start) & " milliseconds" & @LF) EndFunc if not $bDebug=True Then ; Not very usefull to find full screen and tricky as screen is most likely changed (clock, output from application etc.) ; findTester("SCREEN",$Bitmap1Filename,TRUE);Find the full screen itself ; findTester($Bitmap1Filename,$Bitmap1Filename,$c24RGBFullMatch);Find the full screen itself 31 seconds findTester($Bitmap1Filename,$Bitmap1Filename,$c24RGBPartialMatch);Find the full screen itself 2.8529088255245 seconds ; findTester($Bitmap1Filename,$Bitmap1Filename,$c16RGBFullMatch);Find the full screen itself 21 seconds findTester($Bitmap1Filename,$Bitmap1Filename,$c16RGBPartialMatch);Find the full screen itself 1 seconds ; Be aware that overlapping windows, moving things on screen can make it difficult to find on full screen findTester("SCREEN",$Bitmap2Filename,$c24RGBFullMatch);Find the full calculatorscreen findTester("SCREEN",$Bitmap2Filename,$c24RGBPArtialMatch);Find the full calculatorscreen with partial match findTester("SCREEN",$Bitmap2Filename,$c16RGBFullMatch);Find the full calculatorscreen findTester("SCREEN",$Bitmap2Filename,$c16RGBPArtialMatch);Find the full calculatorscreen with partial match findTester($Bitmap1Filename,$Bitmap2Filename,$c16RGBFullMatch);Find the full calculatorscreen findTester($Bitmap1Filename,$Bitmap2Filename,$c16RGBPartialMatch);Find the full calculatorscreen with partial match findTester($Bitmap2Filename,$Bitmap3Filename,$c24RGBFullMatch);Find the backspace button findTester($Bitmap2Filename,$Bitmap3Filename,$c24RGBPartialMatch);Find the backspace button with partial match findTester($Bitmap2Filename,$Bitmap3Filename,$c16RGBFullMatch);Find the backspace button findTester($Bitmap2Filename,$Bitmap3Filename,$c16RGBPartialMatch);Find the backspace button with partial match winactivate($calcHWND);Make sure calculator is active on the screen findTester("[ACTIVE]",$Bitmap3Filename,$c24RGBFullMatch);Find the backspace button findTester("[ACTIVE]",$Bitmap3Filename,$c24RGBPartialMatch);Find the backspace button with partial match findTester("[ACTIVE]",$Bitmap3Filename,$c16RGBFullMatch);Find the backspace button on active screen findTester("[ACTIVE]",$Bitmap3Filename,$c16RGBPartialMatch);Find the backspace button with partial match findTester("SCREEN",$Bitmap3Filename,$c24RGBFullMatch);Find the backspace button findTester("SCREEN",$Bitmap3Filename,$c24RGBPartialMatch);Find the backspace button with partial match findTester("SCREEN",$Bitmap3Filename,$c16RGBFullMatch);Find the backspace button findTester("SCREEN",$Bitmap3Filename,$c16RGBPartialMatch);Find the backspace button with partial match findTester($Bitmap4Filename,$Bitmap5Filename,$c16RGBFullMatch);Find the seven findTester($Bitmap4Filename,$Bitmap5Filename,$c16RGBPartialMatch);Find the seven with partial match findTester($Bitmap4Filename,$Bitmap6Filename,$c16RGBFullMatch);Find the cat findTester($Bitmap4Filename,$Bitmap6Filename,$c16RGBPartialMatch);Find the cat with partial match findTester($Bitmap1Filename,$Bitmap7Filename,$c24RGBFullMatch);Find the 3 of Autoit Homepage findTester($Bitmap1Filename,$Bitmap7Filename,$c24RGBPartialMatch);Find the 3 of Autoit Homepage findTester($Bitmap1Filename,$Bitmap7Filename,$c16RGBFullMatch);Find the 3 of Autoit Homepage findTester($Bitmap1Filename,$Bitmap7Filename,$c16RGBPartialMatch);Find the 3 of Autoit Homepage Else ;~ findTester($Bitmap4Filename,$Bitmap6Filename,$c24RGBFullMatch);Find the cat ;~ findTester($Bitmap1Filename,$Bitmap2Filename,$c24RGBFullMatch);Find the full calculatorscreen findTester("SCREEN",$Bitmap2Filename,$c24RGBFullMatch);Find the full calculatorscreen EndIf _GDIPlus_Shutdown() ;** Example end ** #EndRegion #Region actual functions ;=============================================================================== ; Function Name: findBMP ; Description: Finds a bitmap (.BMP) in another BMP file (other formats PNG and TIFF work also other formats less usefull GIF, JPEG, Exif, WMF, and EMF should work) ; Syntax: findBMP($BMP1, $BMP2, $MatchType=TRUE) ; ; Parameter(s): $BMP1 = Filename of bitmap to search in ; $BMP2 = Filename of bitmap to search for ; $MatchType = c24RGBFullMatch, c24RGBPartialMatch, c16RGBFullMatch, c16RGBPartialMatch ; ; Return Value(s): On Success: = Returns Array List ; On Failure: = @error 1 (Control was found but there was an error with the DLLCall) ; ; Author(s): JunkEW ; ; Note(s): ; * Its never an exact match even with TRUE as last few bits are disposed in algorithm and lines below ; are not checked under assumption that those are 99.99% of the time correct ; * locking bits overview http://www.bobpowell.net/lockingbits.htm ; ToDo: ; * Transparency (when search letters on a different background) http://www.winprog.org/tutorial/transparency.html ; * Match quicker by doing a bitblt with srcinvert when first line is found (instead of checking line by line) ; * $BMP1 and $BMP2 to be HBITMAP handle as input instead of filenames (will make searching within partial screen easier) ; Example(s): ; ;=============================================================================== Func findBMP($BMP1, $BMP2, $MatchType=$c24RGBFullMatch) Dim $fLine[1];Line number of found line(s), redimmed when second picture size is known Dim $BMP1Data="", $BMP1Width=0, $BMP1Height=0, $BMP1LineWidth=0; Dim $BMP2Data="", $BMP2Width=0, $BMP2Height=0, $BMP2LineWidth=0 Dim $foundAt = "", $matchPossible=FALSE, $matchedLines=0, $foundAtLeft=-1, $foundAtTop=-1 Dim $bestMatchLine=-1, $HighestMatchingLines=-1; For knowing when no match is found where best area is Dim $iPos=1; dim $imgBytes, $avData; local $iFuzzyDist, $searchFor, $iAbove, $bMatchPossible, $aboveLine local $j, $imgBits local $tDebug if ($MatchType=$c24RGBFullMatch) or ($matchtype=$c24RGBPartialMatch) then $imgBytes=3 Else $imgBytes=2 endif ; Load the bitmap to search in getImage($BMP1, $BMP1Data, $BMP1Width, $BMP1Height, $BMP1LineWidth, $imgBytes) $BMP1Data = BinaryToString($BMP1Data) ; Load the bitmap to find getImage($BMP2, $BMP2Data, $BMP2Width, $BMP2Height, $BMP2LineWidth, $imgBytes) ;Make it strings to be able to use string functions for searching $BMP2Data = BinaryToString($BMP2Data) if $bDebug=True Then ;~ START debugging ;Get some debugging information FileDelete(@ScriptDir & "\BMP1DATA.TXT") FileDelete(@ScriptDir & "\BMP2DATA.TXT") FileDelete(@ScriptDir & "\COMPAREDLINES.TXT") consolewrite($BMP1Width & @crlf) consolewrite($BMP1Height & @crlf) consolewrite($BMP1LineWidth & @crlf) consolewrite(stringlen($BMP1Data) & @crlf) consolewrite(".{" & $BMP1LineWidth & "}"& @CRLF) consolewrite(".{" & $BMP2LineWidth & "}"& @CRLF) $tDebug=StringRegExpReplace(_stringtohex($BMP1Data),"(.{" & $BMP1LineWidth*2 & "})","$1" & @CRLF) consolewrite(@error & @extended & @CRLF) FileWrite (@ScriptDir & "\BMP1DATA.TXT" , $tdebug) $tDebug=StringRegExpReplace(_stringtohex($BMP2Data),"(.{" & $BMP2LineWidth*2 & "})","$1" & @CRLF) consolewrite(@error & @extended & @CRLF) FileWrite (@ScriptDir & "\BMP2DATA.TXT" , $tdebug) ;~ END debugging EndIf ;For reference of line where in BMP2FindIn a line of BMP2Find was found If $BMP2Height = 0 Then SetError(1,0,0) Return False EndIf ReDim $fline[$BMP2Height] ;If exact match check every 1 line else do it more fuzzy (as most likely other lines are unique) if ($MatchType=$c24RGBFullMatch) or ($matchtype=$c16RGBFullMatch) Then $iFuzzyDist = 1 Else ;Check fuzzy every 10% of lines $iFuzzyDist = ceiling(($bmp2height * 0.1)) endIf $begin = TimerInit() ;Look for each line of the bitmap if it exists in the bitmap to find in ;~ Split bitmap to search in lines ;~ $avData=stringregexp($BMP2Data, ".{1," & $BMP2lineWidth & "}+", 3) dim $searchForRegEx $searchForRegEx = StringMid($BMP2Data, 1 + (0 * $BMP2lineWidth), ($BMP2lineWidth - $imgBytes)) $searchForRegEx = $searchForRegEx & ".*" & StringMid($BMP2Data, 1 + ($BMP2Height * $BMP2lineWidth), ($BMP2lineWidth - $imgBytes)) Local $aArray=stringregexp($BMP1Data,$searchForRegEx,3) if @error=0 Then consolewrite("Regex found " & TimerDiff($begin) & " ms " & @crlf) ;~ For $i = 0 To UBound($aArray) - 1 ;~ consolewrite("RegExp Test with Option 3 - " & $i & $aArray[$i]) ;~ Next endif For $i = 0 To $BMP2Height - 1 ;Minus imgbytes as last bits are padded with unpredictable bytes (24 bits image assumption) or 2 when 16 bits $searchFor = StringMid($BMP2Data, 1 + ($i * $BMP2lineWidth), ($BMP2lineWidth - $imgBytes)) ;~ $searchfor=stringleft($avData[$i],$BMP2lineWidth - $imgBytes) $iPos = StringInStr($BMP1Data, $searchFor, 2, 1, $iPos) ; $iPos = StringInStr($BMP1Data, $searchFor) ;Look for all lines above if there is also a match ;Not doing it for the lines below as speed is more important and risk of mismatch on lines below is small $iAbove=1 if $iPos > 0 then $bMatchPossible=True $matchedLines=1;As first found line is matched we start counting ;Location of the match $foundAtTop = Int($iPos / $BMP1lineWidth) -$i $foundAtLeft = int(mod($iPos,$bmp1linewidth) / $imgBytes) Else ;~ No 1st line found so far nothing to start matching $bMatchPossible=false exitloop endif while (($i+$iAbove) <= ($BMP2Height -1)) and ($bMatchPossible=True) $searchFor = StringMid($BMP2Data, 1 + (($i + $iAbove) * $BMP2lineWidth), ($BMP2lineWidth - $imgBytes)) ;~ $searchfor = stringleft($avData[$i+$iAbove],$BMP2lineWidth - $imgBytes) $aboveLine = stringmid($BMP1Data,$iPos + ($iAbove * $BMP1LineWidth), ($BMP2LineWidth - $imgBytes)) if $bDebug=True Then ;~ START debugging $tDebug=StringRegExpReplace(_stringtohex($searchfor),"(.{8})","$1_") FileWrite (@ScriptDir & "\COMPAREDLINES.TXT" , $tDebug & @crlf) $tDebug=StringRegExpReplace(_stringtohex($aboveline),"(.{8})","$1_") FileWrite (@ScriptDir & "\COMPAREDLINES.TXT" , $tDebug & @crlf) If $aboveLine <> $searchFor Then FileWrite (@ScriptDir & "\COMPAREDLINES.TXT" , "** MISMATCH ** of line " & (1+$iAbove) & " in 2nd bitmap at line " & ($foundAtTop + $i) & @crlf) endIf ;~ END debugging EndIf if comparePicLine($aboveline,$searchfor) = false then $bMatchPossible=False ;To remember the area with the best match if $matchedLines >= $HighestMatchingLines Then $HighestMatchingLines = $matchedLines ;Best guess of location ;~ $foundAtTop = $fline[$i] + $i - $BMP2Height $foundAtTop = Int($iPos / $BMP1lineWidth);+ $i - $BMP2Height $bestMatchLine = Int($iPos / $BMP1lineWidth) EndIf ExitLoop EndIf $matchedLines=$matchedLines + 1 $iAbove=$iAbove+$iFuzzyDist WEnd ;If bMatchPossible is still true most likely we have found the bitmap if $bmatchPossible = True then ;~ ConsoleWrite("Could match top: " & $foundAtTop & " left: " & $foundAtLeft & " in " & TimerDiff($begin) / 1000 & " seconds" & @LF) ; MouseMove($foundatleft,$foundatTop) exitloop else ;~ consolewrite("i not matched " & $ipos & " " & $matchedlines & @crlf ) EndIf Next ;For some debugging of time ; if $bMatchPossible = True Then ; ConsoleWrite("Searching took " & TimerDiff($begin) / 1000 & " seconds " & @LF) ; Else ; ConsoleWrite("NOT FOUND Searching took " & TimerDiff($begin) / 1000 & " seconds" & @LF) ; endif ;Return an error if not found else return an array with all information if $bMatchPossible = False Then SetError(1, 0, 0) endif ; return stringsplit($bMatchPossible & ";" & $matchedLines & ";" & $foundAtLeft & ";" & $foundAtTop & ";" & $bmp2width & ";" & $BMP2Height & ";" & $HighestMatchingLines & ";" & $bestMatchLine,";") return $bMatchPossible & ";" & $matchedLines & ";" & $foundAtLeft & ";" & $foundAtTop & ";" & $bmp2width & ";" & $BMP2Height & ";" & $HighestMatchingLines & ";" & $bestMatchLine EndFunc;==>findBMP func comparePicLine($s1,$s2) local $sLen, $iMatched if $s1=$s2 Then return True Else $iMatched=0 $sLen=stringlen($s1) for $tJ=1 to $slen if stringmid($s1,$tJ,1)=stringmid($s2,$tJ,1) Then $iMatched=$imatched+1 Else if $bDebug=True Then ;~ START debugging FileWrite (@ScriptDir & "\COMPAREDLINES.TXT" , "Debug mismatch pixel " & $tj & "=" & int($tj/4) & @crlf) endif endif Next if ($iMatched / $sLen ) > $cMatchLinePercentage then return true Else return false endif EndIf endfunc Func GetImage($BMPFile, byref $BMPDataStart, byref $Width, byRef $Height, byref $Stride, $imgBytes=3) local $Scan0, $pixelData, $hbScreen, $pBitmap, $pBitmapCap, $handle ; Load the bitmap to search in If $BMPFile="SCREEN" Then $hbScreen=_ScreenCapture_Capture("",0,0,-1,-1,False) $pBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hbScreen); returns memory bitmap Else ;try to get a handle $handle = WinGetHandle($BMPFile) If @error Then ;Assume its an unknown handle so correct filename should be given $pBitmap = _GDIPlus_BitmapCreateFromFile($BMPFile) Else $hbScreen=_ScreenCapture_CaptureWnd("",$handle,0,0,-1,-1,False) $pBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hbScreen); returns memory bitmap EndIf EndIf ;Get $tagGDIPBITMAPDATA structure ;~ ConsoleWrite("Bitmap Width: " & _GDIPlus_ImageGetWidth($pBitmap) & @CRLF ) ;~ ConsoleWrite("Bitmap Height: " & _GDIPlus_ImageGetHeight($pBitmap) & @CRLF) ;~ 24 bits (3 bytes) or 16 bits (2 bytes) comparison if ($imgBytes=2) then $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF16RGB555) ;~ $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF32ARGB) Else $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF24RGB) ;~ $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF32ARGB) endIf If @ERROR Then MsgBox(0,"","Error locking region " & @error) $Stride = DllStructGetData($BitmapData, "Stride");Stride - Offset, in bytes, between consecutive scan lines of the bitmap. If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up. $Width = DllStructGetData($BitmapData, "Width");Image width - Number of pixels in one scan line of the bitmap. $Height = DllStructGetData($BitmapData, "Height");Image height - Number of scan lines in the bitmap. $PixelFormat = DllStructGetData($BitmapData, "PixelFormat");Pixel format - Integer that specifies the pixel format of the bitmap $Scan0 = DllStructGetData($BitmapData, "Scan0");Scan0 - Pointer to the first (index 0) scan line of the bitmap. $pixelData = DllStructCreate("ubyte lData[" & (abs($Stride) * $Height-1) & "]", $Scan0) $BMPDataStart = $BMPDataStart & DllStructGetData($pixeldata,"lData") _GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData) _GDIPlus_ImageDispose ($pBitmap) _WinAPI_DeleteObject ($pBitmap) EndFunc;==>GetImage ; Draw rectangle on screen. Func _UIA_DrawRect($tLeft, $tRight, $tTop, $tBottom, $color = 0xFF, $PenWidth = 4) Local $hDC, $hPen, $obj_orig, $x1, $x2, $y1, $y2 $x1 = $tLeft $x2 = $tRight $y1 = $tTop $y2 = $tBottom $hDC = _WinAPI_GetWindowDC(0) ; DC of entire screen (desktop) $hPen = _WinAPI_CreatePen($PS_SOLID, $PenWidth, $color) $obj_orig = _WinAPI_SelectObject($hDC, $hPen) _WinAPI_DrawLine($hDC, $x1, $y1, $x2, $y1) ; horizontal to right _WinAPI_DrawLine($hDC, $x2, $y1, $x2, $y2) ; vertical down on right _WinAPI_DrawLine($hDC, $x2, $y2, $x1, $y2) ; horizontal to left right _WinAPI_DrawLine($hDC, $x1, $y2, $x1, $y1) ; vertical up on left ; clear resources _WinAPI_SelectObject($hDC, $obj_orig) _WinAPI_DeleteObject($hPen) _WinAPI_ReleaseDC(0, $hDC) EndFunc ;==>_UIA_DrawRect #EndRegion Edit: Speed and partial match enhancements. Search is within a split second especially when partial match is turned on Edit2: Cleaned up code in a nicer way, returns best match results even if find is false Edit 24 march 2008: Becomes dependent on beta v3.2.11.5 (beta) ;$iPos = StringInStr($BMP1Data, $searchFor) $iPos = StringInStr($BMP1Data, $searchFor, 2, 1, $iPos+1) Edit 13th june 2008 updated for new autoit version Edit 31st august 2008 added some reference links Edit 04 sept 2008 completely rewritten using GDI plus functions and speedenhancements by using different 24 bits and 16 bits pictures Edit 23 nov 2008 small additional check on bmp2height beeing 0 Edit 1st dec 2008 fixed top/left, added [active] find on active screen, work with window special description Edit 20st feb 2009 fixed Stringinstr to use additional parameters, now matchin b/w pictures better $iPos = StringInStr($BMP1Data, $searchFor, 2, 1, $iPos) Edit jan 5th 2013 full updated all working under Windows 7 / 64 bits (not tested under windows XP but no reason to be broken) edit jan 20st 2018 checked with windows 10, quickfix as calculator is not recognized by AU3Inf, basic algorithm still works fine see also works with BMP, PNG and TIFF added threshold for small bits off on a single line (unclear why GDI writes those pixels/shade variation) speed I assume is no issue for nowadays CPU power pics.zip
    1 point
×
×
  • Create New...