Files
USB_installer/legacy-au3/usb-install.au3
T

1191 lines
48 KiB
AutoIt
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
; PARAVIS FLASH - разметка USB-диска (однокнопочный вариант)
; 1) FAT32 16 ГБ (PARAVIS)
; 2) FAT 32 МБ (VTOYEFI, под Ventoy)
; 3) exFAT остальное минус 3 МБ (DATA)
; В конце диска остаются 3 МБ нераспределёнными.
; ВНИМАНИЕ: все данные на выбранном диске уничтожаются.
#RequireAdmin
#NoTrayIcon
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <MsgBoxConstants.au3>
#include <FileConstants.au3>
#include <ComboConstants.au3>
#include <StaticConstants.au3>
Opt("GUIOnEventMode", 1)
Opt("MustDeclareVars", 1)
Global Const $MB = 1048576
Global Const $P1_MB = 16384
Global Const $P2_MB = 32
Global Const $TAIL_MB = 3
Global $gTestDisk = -1 ; служебный параметр /testdisk=N (только для автотеста)
Global $gWimPath = "" ; путь к архиву (параметр /wim=..., иначе рядом с exe)
Global $gRun = StringFormat("%04d%02d%02d_%02d%02d%02d", @YEAR, @MON, @MDAY, @HOUR, @MIN, @SEC) ; метка запуска для логов
Global $gFailInfo = "" ; на каком шаге и на какой раздел шла запись
Global $gLastOut = "" ; последний вывод внешней команды (для разбора ошибок)
; Служебные режимы (не для обычного запуска):
; /selftest - записать диагностику в %TEMP%\paravis_selftest.txt и выйти
; /testdisk=N - работать с диском N, не требуя выбора в списке (автотест на VHD)
If $CmdLine[0] > 0 Then
Local $ai, $arg, $argl
For $ai = 1 To $CmdLine[0]
$arg = $CmdLine[$ai]
$argl = StringLower($arg)
If $argl = "/selftest" Then
Local $hSf = FileOpen(@TempDir & "\paravis_selftest.txt", $FO_OVERWRITE + $FO_ANSI)
FileWriteLine($hSf, "windows=" & @WindowsDir)
FileWriteLine($hSf, "system_disks=" & _SystemDiskIds())
FileWriteLine($hSf, "admin=" & IsAdmin())
FileWriteLine($hSf, "wim=" & @ScriptDir & "\Data.pf")
FileWriteLine($hSf, "wim_exists=" & FileExists(@ScriptDir & "\Data.pf"))
FileClose($hSf)
Exit
EndIf
If StringLeft($argl, 8) = "/verify=" Then
; Диагностика проверки раскладки на уже готовом диске (только чтение).
Local $vd = Number(StringTrimLeft($arg, 8))
Local $vok = False
Local $vtxt = _VerifyLayout($vd, $vok)
Local $vf = FileOpen(@TempDir & "\paravis_verify.txt", $FO_OVERWRITE + $FO_ANSI)
FileWriteLine($vf, "disk=" & $vd & " ok=" & $vok)
FileWriteLine($vf, $vtxt)
FileClose($vf)
Exit
EndIf
If StringLeft($argl, 10) = "/testdisk=" Then $gTestDisk = Number(StringTrimLeft($arg, 10))
If StringLeft($argl, 5) = "/wim=" Then $gWimPath = StringTrimLeft($arg, 5)
Next
EndIf
Global $hStatus, $hGo, $hProg, $hWork, $hHint
Global $gBusy = False ; идёт разметка/запись
Global $gCanInstall = False ; можно ли устанавливать на выбранный диск
Global $gModal = False ; открыто модальное окно
Global $aDiskNum[0] ; номера дисков в порядке выпадающего списка
Global $aDiskSize[0] ; их размеры на момент заполнения списка
Global $aDiskName[0] ; названия моделей
Global $aDiskCap[0] ; объёмы для показа
Global $gSelIdx = 0 ; выбранный индекс
Global $gSig = "-" ; подпись текущего состава устройств (на старте всегда отличается)
Global $hPopup = 0 ; окно раскрытого списка
Global $hItemName[0] ; пункты: название
Global $hItemCap[0] ; пункты: объём
Global $aItemBg[0] ; последние цвета пунктов
Global $gDropGuard = 0 ; защита от закрытия сразу после открытия
Global $gNeedScan = False ; пришло сообщение о смене устройств
Global $gDevTick = 0 ; когда пришло это сообщение
Global $gCancel = False ; пользователь нажал «ОТМЕНИТЬ»
Global $gCancelDown = False
Global $gCancelBg = -1
Global $gApplied = 0 ; сколько образов уже распаковано
Global $gPhase = 0 ; 0 — простой, 1 — разметка, 2 — копирование файлов
Global $gRunPid = 0 ; процесс текущей команды (для отмены копирования)
Global $gInstalled = False ; на выбранном диске уже стоит сборка (маркеры на месте)
Global $gClickGuard = 0 ; защита от «залипшего» клика после закрытия окна
; --- оформление
Global Const $CLR_BG = 0xF7F8FA
Global Const $CLR_HEAD = 0x1E2A44
Global Const $CLR_DIM = 0x6B7280
Global $hGUI = GUICreate("Paravis installer", 376, 183, -1, -1, $WS_POPUP, $WS_EX_APPWINDOW)
GUISetBkColor($CLR_BG, $hGUI)
GUISetOnEvent($GUI_EVENT_CLOSE, "_Quit")
GUISetFont(9, 400, 0, "Tahoma", $hGUI)
; шапка
Global $hHead = GUICtrlCreateLabel("", 0, 0, 376, 42)
GUICtrlSetBkColor($hHead, $CLR_HEAD)
Global $hTitle = GUICtrlCreateLabel("Paravis Flash", 16, 2, 300, 24)
GUICtrlSetFont($hTitle, 15, 700, 0, "Tahoma")
GUICtrlSetColor($hTitle, 0xFFFFFF)
GUICtrlSetBkColor($hTitle, $CLR_HEAD)
Global $hSub = GUICtrlCreateLabel("Запись загрузочной флешки", 18, 26, 300, 14)
GUICtrlSetColor($hSub, 0xC7D0E0)
GUICtrlSetBkColor($hSub, $CLR_HEAD)
Global $hMin = GUICtrlCreateLabel("—", 306, 2, 30, 18, BitOR($SS_CENTER, $SS_CENTERIMAGE, $SS_NOTIFY))
GUICtrlSetFont($hMin, 10, 400, 0, "Tahoma")
GUICtrlSetColor($hMin, 0xC7D0E0)
GUICtrlSetBkColor($hMin, $CLR_HEAD)
Global $hCloseBtn = GUICtrlCreateLabel("✕", 338, 2, 30, 18, BitOR($SS_CENTER, $SS_CENTERIMAGE, $SS_NOTIFY))
GUICtrlSetFont($hCloseBtn, 9, 400, 0, "Tahoma")
GUICtrlSetColor($hCloseBtn, 0xC7D0E0)
GUICtrlSetBkColor($hCloseBtn, $CLR_HEAD)
; карточка выбора
Local $hCard = GUICtrlCreateLabel("", 8, 45, 360, 46)
GUICtrlSetBkColor($hCard, 0xFFFFFF)
Global $hLbl = GUICtrlCreateLabel("Выбран накопитель:", 16, 45, 340, 15)
GUICtrlSetColor($hLbl, $CLR_DIM)
GUICtrlSetBkColor($hLbl, 0xFFFFFF)
Global $hComboFrame = GUICtrlCreateLabel("", 15, 61, 342, 28)
GUICtrlSetBkColor($hComboFrame, 0xD5DAE3)
Global $hDrop = GUICtrlCreateLabel("", 16, 62, 340, 26)
GUICtrlSetBkColor($hDrop, 0xFFFFFF)
Global $hDropName = GUICtrlCreateLabel("", 21, 62, 240, 26, BitOR($SS_LEFT, $SS_CENTERIMAGE, $SS_NOTIFY))
GUICtrlSetFont($hDropName, 9, 400, 0, "Tahoma")
GUICtrlSetColor($hDropName, 0x1F2430)
GUICtrlSetBkColor($hDropName, 0xFFFFFF)
Global $hDropSize = GUICtrlCreateLabel("", 262, 62, 74, 26, BitOR($SS_RIGHT, $SS_CENTERIMAGE, $SS_NOTIFY))
GUICtrlSetFont($hDropSize, 9, 400, 0, "Tahoma")
GUICtrlSetColor($hDropSize, 0x1F2430)
GUICtrlSetBkColor($hDropSize, 0xFFFFFF)
GUICtrlSetFont($hDrop, 9, 400, 0, "Tahoma")
GUICtrlSetColor($hDrop, 0x1F2430)
GUICtrlSetBkColor($hDrop, 0xFFFFFF)
Global $hArrow = GUICtrlCreateLabel("▼", 336, 62, 16, 26, BitOR($SS_CENTER, $SS_CENTERIMAGE, $SS_NOTIFY))
GUICtrlSetFont($hArrow, 8, 400, 0, "Tahoma")
GUICtrlSetColor($hArrow, 0x6B7280)
GUICtrlSetBkColor($hArrow, 0xFFFFFF)
GUICtrlSetOnEvent($hDrop, "_DropToggle")
GUICtrlSetOnEvent($hArrow, "_DropToggle")
$hStatus = GUICtrlCreateLabel("", 14, 95, 348, 15)
GUICtrlSetColor($hStatus, $CLR_DIM)
GUICtrlSetBkColor($hStatus, $CLR_BG)
$hProg = GUICtrlCreateProgress(14, 139, 348, 5)
GUICtrlSetState($hProg, $GUI_HIDE)
; текст установки — на месте красного предупреждения
$hWork = GUICtrlCreateLabel("", 14, 115, 348, 18, $SS_CENTER)
GUICtrlSetFont($hWork, 9, 700, 0, "Tahoma")
GUICtrlSetColor($hWork, 0x1F2430)
GUICtrlSetBkColor($hWork, $CLR_BG)
GUICtrlSetState($hWork, $GUI_HIDE)
$hGo = GUICtrlCreateLabel("УСТАНОВИТЬ", 14, 147, 348, 30, BitOR($SS_CENTER, $SS_CENTERIMAGE, $SS_NOTIFY))
GUICtrlSetFont($hGo, 10, 700, 0, "Tahoma")
GUICtrlSetColor($hGo, 0xFFFFFF)
GUICtrlSetBkColor($hGo, 0x2563EB)
; нажатие на кнопку обрабатывается опросом (_GoPoll), а не событием AutoIt
$hHint = GUICtrlCreateLabel("Все данные на выбранном диске будут уничтожены", 14, 115, 348, 18, $SS_CENTER)
GUICtrlSetColor($hHint, 0xC0392B)
GUICtrlSetFont($hHint, 9, 700, 0, "Tahoma")
GUICtrlSetBkColor($hHint, $CLR_BG)
GUISetState(@SW_SHOW, $hGUI)
AdlibRegister("_BtnPoll", 40)
AdlibRegister("_AutoRefresh", 250)
GUIRegisterMsg(0x0219, "_OnDeviceChange")
_Refresh()
While 1
Sleep(50)
WEnd
Func _Quit()
Exit
EndFunc
Func _Refresh()
_ScanDisks()
EndFunc
Func _AutoRefresh()
; Список перечитывается только когда система сообщила о смене устройств.
; Постоянного опроса нет — окно не подтормаживает при перетаскивании и наведении.
If Not $gNeedScan Then Return
If $gBusy Or $gModal Or $hPopup <> 0 Then Return
If TimerDiff($gDevTick) < 500 Then Return
$gNeedScan = False
_ScanDisks()
EndFunc
Func _OnDeviceChange($hWnd, $iMsg, $wParam, $lParam)
; WM_DEVICECHANGE: подключили/отключили накопитель
If $wParam = 0x8000 Or $wParam = 0x8004 Or $wParam = 0x0007 Then
If Not $gNeedScan Then
$gNeedScan = True
$gDevTick = TimerInit()
EndIf
EndIf
Return $GUI_RUNDEFMSG
EndFunc
Func _ScanDisks()
; Перечитывает USB-диски. Если состав не изменился — ничего не трогает.
Local $sig = "", $n = 0
Local $nums[0], $sizes[0], $names[0], $caps[0]
Local $oStorage = ObjGet("winmgmts:{impersonationLevel=impersonate}!\\.\root\Microsoft\Windows\Storage")
If IsObj($oStorage) Then
Local $col = $oStorage.ExecQuery("SELECT * FROM MSFT_Disk")
If IsObj($col) Then
For $d In $col
If Number($d.BusType) = 7 Then
_PushDisk($nums, $sizes, $names, $caps, $n, Number($d.Number), Number($d.Size), $d.FriendlyName)
$sig = $sig & Number($d.Number) & ":" & Number($d.Size) & "|"
EndIf
Next
EndIf
EndIf
If $n = 0 Then
; страховка: если провайдер хранилища недоступен
Local $oWMI = ObjGet("winmgmts:\\.\root\cimv2")
If IsObj($oWMI) Then
Local $col2 = $oWMI.ExecQuery("SELECT * FROM Win32_DiskDrive WHERE InterfaceType='USB' OR PNPDeviceID LIKE 'USBSTOR%' OR MediaType='External hard disk media' OR MediaType='Removable Media'")
If IsObj($col2) Then
For $d In $col2
Local $num = Number(StringRegExpReplace($d.DeviceID, ".*PHYSICALDRIVE(\d+).*", "$1"))
_PushDisk($nums, $sizes, $names, $caps, $n, $num, Number($d.Size), $d.Model)
$sig = $sig & $num & ":" & Number($d.Size) & "|"
Next
EndIf
EndIf
EndIf
If $sig = $gSig Then Return ; состав тот же — экран не меняем
$gSig = $sig
; запоминаем выбранный диск, чтобы не сбить выбор
Local $curNum = -1
If UBound($aDiskNum) > 0 And $gSelIdx >= 0 And $gSelIdx < UBound($aDiskNum) Then $curNum = $aDiskNum[$gSelIdx]
$aDiskNum = $nums
$aDiskSize = $sizes
$aDiskName = $names
$aDiskCap = $caps
If $n = 0 Then
GUICtrlSetData($hDropName, "")
GUICtrlSetData($hDropSize, "")
GUICtrlSetData($hLbl, "Подходящие накопители отсутствуют!")
GUICtrlSetData($hStatus, "Найдено: 0")
$gSelIdx = 0
$gInstalled = False
_ArrowState()
_RefreshGo()
Return
EndIf
Local $found = 0, $i
For $i = 0 To UBound($aDiskNum) - 1
If $aDiskNum[$i] = $curNum Then
$found = $i
ExitLoop
EndIf
Next
$gSelIdx = $found
_ShowSelected()
_RefreshGo()
GUICtrlSetData($hLbl, "Выбран накопитель:")
GUICtrlSetData($hStatus, "Найдено: " & UBound($aDiskNum))
_ArrowState()
EndFunc
Func _PushDisk(ByRef $nums, ByRef $sizes, ByRef $names, ByRef $caps, ByRef $n, $num, $size, $model)
ReDim $nums[$n + 1]
ReDim $sizes[$n + 1]
ReDim $names[$n + 1]
ReDim $caps[$n + 1]
$nums[$n] = $num
$sizes[$n] = $size
Local $m = StringStripWS(String($model), 3)
If $m = "" Then $m = "(без названия)"
$names[$n] = $m
$caps[$n] = _NominalSize($size)
$n = $n + 1
EndFunc
Func _ShowSelected()
If UBound($aDiskName) = 0 Then Return
GUICtrlSetData($hDropName, $aDiskName[$gSelIdx])
GUICtrlSetData($hDropSize, $aDiskCap[$gSelIdx])
_CheckInstalled()
EndFunc
Func _NominalGb($bytes)
; «Магазинный» объём в ГБ (64, а не 60)
Local $gb = Number($bytes) / 1000000000
Local $list[23] = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1000, 1500, 2000, 3000, 4000, 6000, 8000, 10000, 12000, 14000, 16000, 18000, 20000]
Local $i
For $i = 0 To UBound($list) - 1
If $gb <= $list[$i] * 1.02 Then Return $list[$i]
Next
Return $gb
EndFunc
Func _NominalSize($bytes)
Local $n = _NominalGb($bytes)
If $n >= 1000 Then Return StringFormat("%g ТБ", $n / 1000)
Return StringFormat("%d ГБ", $n)
EndFunc
Func _SelectedIndex()
If UBound($aDiskNum) = 0 Then Return -1
If $gSelIdx < 0 Or $gSelIdx >= UBound($aDiskNum) Then Return -1
Return $gSelIdx
EndFunc
; --- свой выпадающий список
Func _DropToggle()
If $gBusy Or $gModal Then Return
If UBound($aDiskName) = 0 Then Return
If $hPopup <> 0 Then
_DropClose()
Else
_DropOpen()
EndIf
EndFunc
Func _DropOpen()
Local $n = UBound($aDiskName)
If $n = 0 Then Return
Local $itemH = 24
Local $p = WinGetPos($hGUI)
Local $c = WinGetClientSize($hGUI)
If Not IsArray($p) Or Not IsArray($c) Then Return
Local $borderX = ($p[2] - $c[0]) / 2
Local $titleH = $p[3] - $c[1] - $borderX
; рамка — как у остальных элементов: 1 px светло-серым, без системной рамки
$hPopup = GUICreate("", 342, $n * $itemH + 2, $p[0] + $borderX + 15, $p[1] + $titleH + 89, $WS_POPUP, $WS_EX_TOOLWINDOW, $hGUI)
GUISetBkColor(0xD5DAE3, $hPopup)
Local $hPanel = GUICtrlCreateLabel("", 1, 1, 340, $n * $itemH)
GUICtrlSetBkColor($hPanel, 0xFFFFFF)
ReDim $hItemName[$n]
ReDim $hItemCap[$n]
ReDim $aItemBg[$n]
Local $i
For $i = 0 To $n - 1
Local $y = 1 + $i * $itemH
$hItemName[$i] = GUICtrlCreateLabel(" " & $aDiskName[$i], 1, $y, 248, $itemH, BitOR($SS_LEFT, $SS_CENTERIMAGE, $SS_NOTIFY))
GUICtrlSetFont($hItemName[$i], 9, 400, 0, "Tahoma")
GUICtrlSetColor($hItemName[$i], 0x1F2430)
GUICtrlSetBkColor($hItemName[$i], 0xFFFFFF)
GUICtrlSetOnEvent($hItemName[$i], "_DropPick")
$hItemCap[$i] = GUICtrlCreateLabel($aDiskCap[$i] & " ", 249, $y, 92, $itemH, BitOR($SS_RIGHT, $SS_CENTERIMAGE, $SS_NOTIFY))
GUICtrlSetFont($hItemCap[$i], 9, 400, 0, "Tahoma")
GUICtrlSetColor($hItemCap[$i], 0x6B7280)
GUICtrlSetBkColor($hItemCap[$i], 0xFFFFFF)
GUICtrlSetOnEvent($hItemCap[$i], "_DropPick")
$aItemBg[$i] = -1
Next
$gDropGuard = TimerInit()
GUISetState(@SW_SHOW, $hPopup)
EndFunc
Func _DropClose()
If $hPopup <> 0 Then
GUIDelete($hPopup)
$hPopup = 0
EndIf
EndFunc
Func _DropPick()
Local $i
For $i = 0 To UBound($aDiskName) - 1
If @GUI_CtrlId = $hItemName[$i] Or @GUI_CtrlId = $hItemCap[$i] Then
$gSelIdx = $i
_ShowSelected()
_RefreshGo()
ExitLoop
EndIf
Next
_DropClose()
EndFunc
Func _DiskSize($num)
Local $sz = 0
Local $oStorage = ObjGet("winmgmts:{impersonationLevel=impersonate}!\\.\root\Microsoft\Windows\Storage")
If IsObj($oStorage) Then
Local $col = $oStorage.ExecQuery("SELECT * FROM MSFT_Disk")
If IsObj($col) Then
For $d In $col
If Number($d.Number) = $num Then $sz = Number($d.Size)
Next
EndIf
EndIf
If $sz = 0 Then
Local $oWMI = ObjGet("winmgmts:\\.\root\cimv2")
If IsObj($oWMI) Then
Local $col2 = $oWMI.ExecQuery("SELECT * FROM Win32_DiskDrive WHERE Index=" & $num)
If IsObj($col2) Then
For $d In $col2
$sz = Number($d.Size)
Next
EndIf
EndIf
EndIf
Return $sz
EndFunc
Func _SystemDiskIds()
; Возвращает строку вида "|0|" со номерами дисков, на которых лежит Windows.
Local $ids = ""
Local $oWMI = ObjGet("winmgmts:\\.\root\cimv2")
If Not IsObj($oWMI) Then Return ""
Local $sys = StringLeft(@WindowsDir, 2)
If StringRight($sys, 1) <> ":" Then Return ""
Local $col = $oWMI.ExecQuery("ASSOCIATORS OF {Win32_LogicalDisk.DeviceID='" & $sys & "'} WHERE AssocClass=Win32_LogicalDiskToPartition")
If Not IsObj($col) Then Return ""
For $part In $col
Local $m = StringRegExp(String($part.DeviceID), "(?i)Disk #(\d+)", 1)
If IsArray($m) Then $ids = $ids & "|" & Number($m[0]) & "|"
Next
Return $ids
EndFunc
Func _Apply()
If $gBusy Then Return
If Not $gCanInstall And $gTestDisk < 0 Then Return
Local $disk = $gTestDisk
Local $expectSize = 0
If $disk < 0 Then
Local $idx = _SelectedIndex()
If $idx < 0 Then
_Msg(BitOR($MB_ICONWARNING, $MB_OK), "Устройство не выбрано", "Выберите флешку в списке.")
Return
EndIf
$disk = $aDiskNum[$idx]
$expectSize = $aDiskSize[$idx]
EndIf
; Страховка: никогда не размечаем диск, на котором стоит Windows.
Local $sysIds = _SystemDiskIds()
If StringInStr($sysIds, "|" & $disk & "|") > 0 Then
_Msg(BitOR($MB_ICONERROR, $MB_OK), "Это системный диск", _
"Диск " & $disk & " содержит Windows. Разметка отменена - выберите другой диск.")
Return
EndIf
Local $sizeBytes = _DiskSize($disk)
If $sizeBytes <= 0 Then
_Msg(BitOR($MB_ICONERROR, $MB_OK), "Ошибка", "Не удалось определить размер диска " & $disk & ".")
Return
EndIf
; Сверка: диск должен быть тем же, что был в списке (защита от сдвига индексов)
If $expectSize > 0 And Abs($sizeBytes - $expectSize) > ($expectSize / 100) Then
_Msg(BitOR($MB_ICONERROR, $MB_OK), "Диск не совпадает", _
"Размер выбранного диска (" & Round($sizeBytes / 1073741824, 1) & " ГБ) не совпадает с тем, " & _
"что был в списке (" & Round($expectSize / 1073741824, 1) & " ГБ)." & @CRLF & @CRLF & _
"Нажмите «Обновить» и выберите устройство заново.")
Return
EndIf
Local $totalMB = Int($sizeBytes / $MB)
Local $p3 = $totalMB - 1 - $P1_MB - $P2_MB - $TAIL_MB
If $p3 < 512 Then
_Msg(BitOR($MB_ICONERROR, $MB_OK), "Слишком маленький диск", _
"Нужен диск не меньше " & ($P1_MB + $P2_MB + $TAIL_MB + 512) & " МБ." & @CRLF & _
"Сейчас: " & $totalMB & " МБ.")
Return
EndIf
Local $msg = "Диск " & $disk & " (" & StringFormat("%.1f", $sizeBytes / 1073741824) & " ГБ)" & @CRLF & @CRLF & _
"Будет создано:" & @CRLF & _
" 1) FAT32 16 ГБ метка PARAVIS" & @CRLF & _
" 2) FAT 32 МБ метка VTOYEFI" & @CRLF & _
" 3) exFAT " & StringFormat("%.1f", $p3 / 1024) & " ГБ метка DATA" & @CRLF & @CRLF & _
"Затем на разделы будут распакованы файлы из архива Data.pf" & @CRLF & _
"(1 -> PARAVIS, 2 -> VTOYEFI, 3 -> DATA)." & @CRLF & @CRLF & _
"ВНИМАНИЕ! ВСЕ ДАННЫЕ НА ЭТОМ ДИСКЕ БУДУТ УНИЧТОЖЕНЫ." & @CRLF & _
"Продолжить?"
GUICtrlSetData($hGo, "Ожидание подтверждения...")
GUICtrlSetBkColor($hGo, 0xAEB6C4)
GUICtrlSetColor($hGo, 0xEDEFF3)
If _Msg(BitOR($MB_ICONWARNING, $MB_YESNO, $MB_DEFBUTTON2), "Подтверждение", $msg) <> $IDYES Then
_GoCaption()
_RefreshGo()
Return
EndIf
_BusySet(True)
$gPhase = 1
; Если диск был offline (например, после тестов ВМ) — включаем его перед разметкой.
; На уже online-диске diskpart вернёт ошибку, но это не помешает основному скрипту.
Local $onlineTmp = @TempDir & "\paravis_" & $gRun & "_online.txt"
Local $hOn = FileOpen($onlineTmp, $FO_OVERWRITE + $FO_ANSI)
If $hOn <> -1 Then
FileWrite($hOn, "select disk " & $disk & @CRLF & "online disk" & @CRLF & "exit" & @CRLF)
FileClose($hOn)
RunWait(@ComSpec & ' /c diskpart.exe /s "' & $onlineTmp & '" > nul 2>&1', "", @SW_HIDE)
FileDelete($onlineTmp)
EndIf
Local $script = "select disk " & $disk & @CRLF & _
"clean" & @CRLF & _
"convert mbr" & @CRLF & _
"create partition primary size=" & $P1_MB & @CRLF & _
"format fs=fat32 quick label=""PARAVIS""" & @CRLF & _
"active" & @CRLF & "assign" & @CRLF & _
"create partition primary size=" & $P2_MB & @CRLF & _
"format fs=fat quick label=""VTOYEFI""" & @CRLF & _
"assign" & @CRLF & _
"create partition primary size=" & $p3 & @CRLF & _
"format fs=exfat quick label=""DATA""" & @CRLF & _
"assign" & @CRLF & "exit" & @CRLF
Local $tmp = @TempDir & "\paravis_" & $gRun & "_diskpart.txt"
Local $hFile = FileOpen($tmp, $FO_OVERWRITE + $FO_ANSI)
If $hFile = -1 Then
_Msg(BitOR($MB_ICONERROR, $MB_OK), "Ошибка", "Не удалось создать файл скрипта.")
Return
EndIf
FileWrite($hFile, $script)
FileClose($hFile)
GUICtrlSetData($hWork, "Разметка диска " & $disk & "…")
Local $logFile = @TempDir & "\paravis_" & $gRun & "_diskpart_log.txt"
; Разметку не прерываем: ждём её конца, но нажатие «ОТМЕНИТЬ» запоминаем
Local $pidDp = Run(@ComSpec & ' /c diskpart.exe /s "' & $tmp & '" > "' & $logFile & '" 2>&1', "", @SW_HIDE)
If $pidDp Then
While ProcessExists($pidDp)
_CancelPoll()
Sleep(50)
WEnd
EndIf
Local $log = ""
If FileExists($logFile) Then $log = FileRead($logFile)
Sleep(1500)
Local $ok = False
Local $check = _VerifyLayout($disk, $ok)
If Not $ok Then
GUICtrlSetData($hWork, "Раскладка не совпала с ожидаемой")
GUICtrlSetState($hProg, $GUI_HIDE)
_Msg(BitOR($MB_ICONERROR, $MB_OK), "Ошибка разметки", _
"РАСКЛАДКА НЕ СОВПАЛА С ОЖИДАЕМОЙ" & @CRLF & @CRLF & $check & @CRLF & @CRLF & _
"Лог: " & $logFile & @CRLF & @CRLF & StringLeft($log, 600))
_BusySet(False)
GUICtrlSetState($hWork, $GUI_HIDE)
GUICtrlSetState($hHint, $GUI_SHOW)
_Refresh()
Return
EndIf
; --- разметка верна, распаковываем образы на разделы
If $gCancel Then
_BusySet(False)
GUICtrlSetState($hWork, $GUI_HIDE)
GUICtrlSetState($hHint, $GUI_SHOW)
_Refresh()
_Msg(BitOR($MB_ICONINFORMATION, $MB_OK), "Отменено", _
"Операция отменена пользователем." & @CRLF & @CRLF & _
"Диск размечен, файлы не скопированы.")
Return
EndIf
Local $wim = _FindWim()
If $wim = "" Then
GUICtrlSetData($hWork, "Размечено, архив Data.pf не найден")
_Msg(BitOR($MB_ICONWARNING, $MB_OK), "Разметка выполнена", _
"Диск размечен, раскладка верна." & @CRLF & @CRLF & $check & @CRLF & @CRLF & _
"Но файл Data.pf не найден рядом с программой - файлы не скопированы.")
_BusySet(False)
GUICtrlSetState($hWork, $GUI_HIDE)
GUICtrlSetState($hHint, $GUI_SHOW)
_Refresh()
Return
EndIf
$gPhase = 2
Local $res = _ApplyImages($disk, $wim)
$gPhase = 0
If $res = 1 Then
_HideVentoy($disk)
GUICtrlSetData($hWork, "Готово")
GUICtrlSetState($hProg, $GUI_HIDE)
; После успешной установки — предупреждение и запуск установщика загрузчиков
_Msg(BitOR($MB_ICONINFORMATION, $MB_OK), "Установка завершена", _
"Установка завершена успешно. Сейчас будет запущен установщик загрузчиков. " & _
"Выберите Legacy и UEFI загрузчики. Если этого не сделать, то будет работать только UEFI.")
Local $p1 = _PartitionLetter($disk, 0)
If $p1 <> "" Then
Local $loader = $p1 & "Change_BootLoader.exe"
If FileExists($loader) Then
Local $pidLoader = Run('"' & $loader & '"')
If $pidLoader <> 0 Then Exit
_Msg(BitOR($MB_ICONWARNING, $MB_OK), "Установщик загрузчиков", _
"Не удалось запустить Change_BootLoader.exe." & @CRLF & @CRLF & _
"Путь: " & $loader)
Else
_Msg(BitOR($MB_ICONWARNING, $MB_OK), "Установщик загрузчиков", _
"Файл Change_BootLoader.exe не найден на разделе PARAVIS." & @CRLF & @CRLF & _
"Путь: " & $loader)
EndIf
Else
_Msg(BitOR($MB_ICONWARNING, $MB_OK), "Установщик загрузчиков", _
"Не удалось определить букву раздела PARAVIS.")
EndIf
ElseIf $res = 2 Then
GUICtrlSetData($hWork, "Ошибка записи")
GUICtrlSetState($hProg, $GUI_HIDE)
_Msg(BitOR($MB_ICONERROR, $MB_OK), "Ошибка копирования файлов", _
"Диск размечен, но скопировать файлы не удалось." & @CRLF & @CRLF & _
"Запустите установку ещё раз.")
ElseIf $res = 3 Then
GUICtrlSetData($hWork, "Отменено")
GUICtrlSetState($hProg, $GUI_HIDE)
_BusySet(False)
GUICtrlSetState($hWork, $GUI_HIDE)
GUICtrlSetState($hHint, $GUI_SHOW)
_Refresh()
_Msg(BitOR($MB_ICONINFORMATION, $MB_OK), "Отменено", _
"Операция отменена пользователем." & @CRLF & @CRLF & _
"Диск размечен, скопировано образов: " & $gApplied & " из 3.")
Return
EndIf
_BusySet(False)
GUICtrlSetState($hWork, $GUI_HIDE)
GUICtrlSetState($hHint, $GUI_SHOW)
_Refresh()
EndFunc
Func _FindWim()
; Архив ищется ТОЛЬКО рядом с exe и только под именем Data.pf.
; Параметр /wim= оставлен как служебный (автотесты), в обычной работе не нужен.
If $gWimPath <> "" Then
If FileExists($gWimPath) Then Return $gWimPath
_Msg(BitOR($MB_ICONWARNING, $MB_OK), "Архив не найден", "Файл не найден:" & @CRLF & $gWimPath)
EndIf
Local $p = @ScriptDir & "\Data.pf"
If FileExists($p) Then Return $p
Return ""
EndFunc
Func _PartitionLetter($disk, $idx)
; Буква раздела с номером $idx (0..2) на диске $disk. Возвращает "X:\" или "".
Local $oWMI = ObjGet("winmgmts:\\.\root\cimv2")
If Not IsObj($oWMI) Then Return ""
Local $parts = $oWMI.ExecQuery("SELECT * FROM Win32_DiskPartition WHERE DiskIndex=" & $disk & " AND Index=" & $idx)
If Not IsObj($parts) Then Return ""
For $p In $parts
Local $ld = $oWMI.ExecQuery("ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" & $p.DeviceID & "'} WHERE AssocClass=Win32_LogicalDiskToPartition")
If IsObj($ld) Then
For $l In $ld
Local $d = String($l.DeviceID)
If StringLen($d) = 2 Then Return $d & "\"
Next
EndIf
Next
Return ""
EndFunc
Func _RunCmd($cmdline, $caption)
; Запускает команду напрямую (без временных .cmd), показывает проценты,
; возвращает код возврата: cmd печатает PF_EXIT=<код> после команды.
Local $full = @ComSpec & ' /v:on /c "' & $cmdline & ' & echo PF_EXIT=!errorlevel!"'
Local $pid = Run($full, "", @SW_HIDE, 2 + 4)
If $pid = 0 Then Return -1
$gRunPid = $pid
Local $out = "", $buf = "", $shown = -1
While 1
$buf = StdoutRead($pid)
If @error Then ExitLoop
If $buf <> "" Then
$out = $out & $buf
Local $m = StringRegExp($out, "(\d{1,3}(?:[.,]\d+)?)\s*%", 3)
If IsArray($m) Then
Local $last = Number($m[UBound($m) - 1])
If $last <> $shown Then
$shown = $last
GUICtrlSetData($hWork, $caption)
GUICtrlSetData($hProg, $last)
EndIf
EndIf
EndIf
If Not ProcessExists($pid) Then ExitLoop
_CancelPoll()
Sleep(50)
WEnd
$gRunPid = 0
$out = $out & StdoutRead($pid)
$gLastOut = $out
Local $code = -1
Local $mm = StringRegExp($out, "PF_EXIT=(\d+)", 3)
If IsArray($mm) Then $code = Number($mm[UBound($mm) - 1])
Return $code
EndFunc
Func _DismApply($wim, $index, $dir, $caption)
; Прямая распаковка образа на раздел через DISM (Windows 8 и новее).
Local $cmd = 'dism.exe /Apply-Image /ImageFile:"' & $wim & '" /Index:' & $index & ' /ApplyDir:' & $dir
Local $rc = _RunCmd($cmd, $caption)
If $rc = 0 Then Return True
GUICtrlSetData($hWork, $caption & ": ОШИБКА (код " & $rc & ")")
Return False
EndFunc
Func _HideVentoy($disk)
; Убираем букву у раздела VTOYEFI (2-й раздел) — в проводнике он не появится.
; Работает по номеру диска и раздела, без привязки к буквам.
Local $tmp = @TempDir & "\paravis_" & $gRun & "_hide.txt"
Local $h = FileOpen($tmp, $FO_OVERWRITE + $FO_ANSI)
If $h = -1 Then Return
FileWrite($h, "select disk " & $disk & @CRLF & _
"select partition 2" & @CRLF & _
"remove" & @CRLF & _
"set id=EF override" & @CRLF & _
"exit" & @CRLF)
FileClose($h)
RunWait(@ComSpec & ' /c diskpart.exe /s "' & $tmp & '" > nul 2>&1', "", @SW_HIDE)
FileDelete($tmp)
EndFunc
Func _ApplyImages($disk, $wim)
; 1 -> PARAVIS, 2 -> VTOYEFI, 3 -> DATA
Local $names[3] = ["PARAVIS", "VTOYEFI", "DATA"]
Local $i
For $i = 0 To 2
If $gCancel Then Return 3
; буква может появиться не мгновенно после assign — ждём до 15 с
Local $dir = ""
Local $try
For $try = 1 To 15
$dir = _PartitionLetter($disk, $i)
If $dir <> "" Then ExitLoop
Sleep(1000)
Next
If $dir = "" Then
_Msg(BitOR($MB_ICONERROR, $MB_OK), "Ошибка", _
"Не удалось определить букву раздела " & ($i + 1) & " (" & $names[$i] & ")." & @CRLF & _
"Проверьте, что раздел подключён (diskpart: assign).")
Return 2
EndIf
If $gCancel Then Return 3
Local $cap = $names[$i] & " → " & $dir
$gFailInfo = "образ " & ($i + 1) & " (" & $names[$i] & ") -> раздел " & $dir
GUICtrlSetData($hProg, 0)
GUICtrlSetState($hProg, $GUI_SHOW)
GUICtrlSetData($hWork, $cap)
Local $r
$r = _DismApply($wim, $i + 1, $dir, $cap)
If Not $r Then
If $gCancel Then Return 3
Return 2
EndIf
$gApplied = $gApplied + 1
If $gCancel Then Return 3
Next
Return 1
EndFunc
Func _VerifyLayout($disk, ByRef $okFlag)
; Проверка результата по WMI (без разбора текста diskpart, не зависит от языка Windows).
$okFlag = False
Local $oWMI = ObjGet("winmgmts:\\.\root\cimv2")
If Not IsObj($oWMI) Then Return "разделов: ? (нет доступа к WMI)"
Local $want = StringSplit("PARAVIS|VTOYEFI|DATA", "|", 2)
Local $rep = ""
Local $n = 0
Local $labels = "|"
Local $sz1 = 0, $sz2 = 0, $sz3 = 0
Local $col = $oWMI.ExecQuery("SELECT * FROM Win32_DiskPartition WHERE DiskIndex=" & $disk)
If IsObj($col) Then
For $p In $col
$n = $n + 1
Local $mbSz = Int(Number($p.Size) / $MB)
If $n = 1 Then $sz1 = $mbSz
If $n = 2 Then $sz2 = $mbSz
If $n = 3 Then $sz3 = $mbSz
Local $line = " раздел " & $n & ": " & $mbSz & " МБ"
Local $lds = $oWMI.ExecQuery("ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" & $p.DeviceID & "'} WHERE AssocClass=Win32_LogicalDiskToPartition")
If IsObj($lds) Then
For $l In $lds
Local $vn = StringStripWS(String($l.VolumeName), 3)
$line = $line & " " & $l.DeviceID & " " & $vn & " " & String($l.FileSystem)
If $vn <> "" Then $labels = $labels & $vn & "|"
Next
EndIf
$rep = $rep & $line & @CRLF
Next
EndIf
Local $miss = ""
For $w In $want
If StringInStr($labels, "|" & $w & "|") = 0 Then $miss = $miss & " " & $w
Next
Local $sizeOk = (Abs($sz1 - $P1_MB) <= 8) And (Abs($sz2 - $P2_MB) <= 8)
Local $head = "разделов: " & $n
If $n <> 3 Then $head = $head & " (ожидалось 3)"
If $miss <> "" Then $rep = $rep & "нет меток:" & $miss & @CRLF
If Not $sizeOk And $n = 3 Then $rep = $rep & "не совпали размеры 1-го и 2-го разделов" & @CRLF
$okFlag = ($n = 3) And ($miss = "") And $sizeOk
Return $head & @CRLF & $rep
EndFunc
Func _BtnPoll()
Static $goCur = -1, $comboCur = -1
If $gModal Then Return
If $gBusy Then
_CancelPoll()
Return
EndIf
Local $ci = GUIGetCursorInfo($hGUI)
If Not IsArray($ci) Then Return
; главная кнопка — клик через опрос
If _GoPoll() Then
_Apply()
Return
EndIf
; --- кнопки шапки: подсветка, нажатие, перетаскивание окна
Static $minCur = -1, $closeCur = -1, $minDown = False, $closeDown = False
Static $drag = False, $dragX = 0, $dragY = 0
Local $minBg = $CLR_HEAD, $closeBg = $CLR_HEAD
If $ci[4] = $hMin Then
If $ci[2] = 1 Then
$minBg = 0x3A4A6B
Else
$minBg = 0x2C3A57
EndIf
EndIf
If $ci[4] = $hCloseBtn Then
If $ci[2] = 1 Then
$closeBg = 0xC0392B
Else
$closeBg = 0xE04B3F
EndIf
EndIf
If $minBg <> $minCur Then
GUICtrlSetBkColor($hMin, $minBg)
$minCur = $minBg
EndIf
If $closeBg <> $closeCur Then
GUICtrlSetBkColor($hCloseBtn, $closeBg)
$closeCur = $closeBg
EndIf
; нажатие засчитывается, если отпустили на той же кнопке
If $ci[4] = $hMin And $ci[2] = 1 Then
$minDown = True
ElseIf $minDown Then
$minDown = False
If $ci[4] = $hMin And $ci[2] = 0 Then GUISetState(@SW_MINIMIZE, $hGUI)
EndIf
If $ci[4] = $hCloseBtn And $ci[2] = 1 Then
$closeDown = True
ElseIf $closeDown Then
$closeDown = False
If $ci[4] = $hCloseBtn And $ci[2] = 0 Then Exit
EndIf
; перетаскивание за шапку (кнопки шапки исключены)
If ($ci[4] = $hHead Or $ci[4] = $hTitle Or $ci[4] = $hSub) And $ci[2] = 1 And Not $drag Then
Local $m = MouseGetPos()
Local $wp = WinGetPos($hGUI)
If IsArray($m) And IsArray($wp) Then
$drag = True
$dragX = $m[0] - $wp[0]
$dragY = $m[1] - $wp[1]
EndIf
EndIf
If $drag Then
If $ci[2] = 0 Then
$drag = False
Else
Local $m2 = MouseGetPos()
If IsArray($m2) Then WinMove($hGUI, "", $m2[0] - $dragX, $m2[1] - $dragY)
EndIf
EndIf
; главная кнопка
Local $go
If (Not $gCanInstall) Or BitAND(GUICtrlGetState($hGo), $GUI_DISABLE) Then
$go = 0xAEB6C4
ElseIf $ci[4] = $hGo And $ci[2] = 1 Then
$go = 0x1B4FD8
ElseIf $ci[4] = $hGo Then
$go = 0x3B82F6
Else
$go = 0x2563EB
EndIf
If $go <> $goCur Then
GUICtrlSetBkColor($hGo, $go)
$goCur = $go
EndIf
; поле списка: раскрытие по нажатию (события надписи не всегда приходят)
Static $dropDown = False
Local $hasDisks = (UBound($aDiskName) > 0)
If $hasDisks And ($ci[4] = $hDrop Or $ci[4] = $hArrow Or $ci[4] = $hDropName Or $ci[4] = $hDropSize) And $ci[2] = 1 And Not $dropDown Then
_DropToggle()
$dropDown = True
EndIf
If $ci[2] = 0 Then $dropDown = False
; поле списка: подсветка рамки
Local $cb
If $hasDisks And ($ci[4] = $hDrop Or $ci[4] = $hArrow Or $ci[4] = $hComboFrame Or $ci[4] = $hDropName Or $ci[4] = $hDropSize) Then
$cb = 0x7FA6F0
Else
$cb = 0xD5DAE3
EndIf
If $cb <> $comboCur Then
GUICtrlSetBkColor($hComboFrame, $cb)
$comboCur = $cb
EndIf
; раскрытый список: подсветка пункта, выбор и закрытие по клику вне
If $hPopup <> 0 Then
Local $ci2 = GUIGetCursorInfo($hPopup)
If IsArray($ci2) Then
Local $k, $bg, $over = -1
For $k = 0 To UBound($hItemName) - 1
$bg = 0xFFFFFF
If $ci2[4] = $hItemName[$k] Or $ci2[4] = $hItemCap[$k] Then
$bg = 0xEEF3FF
$over = $k
EndIf
If $k < UBound($aItemBg) And $aItemBg[$k] <> $bg Then
GUICtrlSetBkColor($hItemName[$k], $bg)
GUICtrlSetBkColor($hItemCap[$k], $bg)
$aItemBg[$k] = $bg
EndIf
Next
If $ci2[2] = 1 And TimerDiff($gDropGuard) > 400 Then
If $over >= 0 Then
; выбрали пункт
$gSelIdx = $over
_ShowSelected()
_RefreshGo()
_DropClose()
ElseIf $ci2[4] = 0 Then
; клик мимо — просто закрыть
_DropClose()
EndIf
EndIf
EndIf
EndIf
EndFunc
Func _DimControls($on)
; Затеняем поле выбора и кнопку «Обновить», когда интерфейс недоступен
If $on Then
GUICtrlSetBkColor($hDrop, 0xF2F3F5)
GUICtrlSetBkColor($hDropName, 0xF2F3F5)
GUICtrlSetColor($hDropName, 0xA0A6B1)
GUICtrlSetBkColor($hDropSize, 0xF2F3F5)
GUICtrlSetColor($hDropSize, 0xB4B9C2)
GUICtrlSetColor($hArrow, 0xC7CBD3)
GUICtrlSetBkColor($hArrow, 0xF2F3F5)
GUICtrlSetBkColor($hComboFrame, 0xE3E6EB)
_DropClose()
Else
GUICtrlSetBkColor($hDrop, 0xFFFFFF)
GUICtrlSetBkColor($hDropName, 0xFFFFFF)
GUICtrlSetColor($hDropName, 0x1F2430)
GUICtrlSetBkColor($hDropSize, 0xFFFFFF)
GUICtrlSetColor($hDropSize, 0x6B7280)
GUICtrlSetBkColor($hArrow, 0xFFFFFF)
_ArrowState()
GUICtrlSetBkColor($hComboFrame, 0xD5DAE3)
EndIf
EndFunc
Func _BusySet($b)
; Идёт установка: предупреждение скрыто, показан ход работ, всё затенено,
; кнопка превращается в «ОТМЕНИТЬ»
$gBusy = $b
If $b Then
$gCancel = False
$gCancelDown = False
$gCancelBg = -1
$gApplied = 0
GUICtrlSetState($hHint, $GUI_HIDE)
GUICtrlSetState($hWork, $GUI_SHOW)
_DimControls(True)
GUICtrlSetData($hGo, "ОТМЕНИТЬ")
GUICtrlSetColor($hGo, 0xFFFFFF)
_CancelLook()
Else
$gCancel = False
$gCancelDown = False
$gPhase = 0
$gRunPid = 0
_CheckInstalled()
_DimControls(False)
_RefreshGo()
EndIf
EndFunc
Func _CancelLook()
; Кнопка отмены: красная (под курсором светлее, нажата темнее),
; после нажатия — серая «Отменяю. Ждите...»
Local $bg, $fg
If $gCancel Then
$bg = 0xAEB6C4
$fg = 0xEDEFF3
Else
$bg = 0xC0392B
$fg = 0xFFFFFF
Local $ci = GUIGetCursorInfo($hGUI)
If IsArray($ci) And $ci[4] = $hGo Then
If $ci[2] = 1 Then
$bg = 0xA93226
Else
$bg = 0xD6453C
EndIf
EndIf
EndIf
If $bg <> $gCancelBg Then
GUICtrlSetBkColor($hGo, $bg)
GUICtrlSetColor($hGo, $fg)
$gCancelBg = $bg
EndIf
EndFunc
Func _GoPoll()
; Клик по главной кнопке через опрос курсора (события AutoIt копятся и срабатывают позже)
Static $down = False
Local $ci = GUIGetCursorInfo($hGUI)
If Not IsArray($ci) Then Return False
If $ci[4] = $hGo And $ci[2] = 1 Then
$down = True
Return False
EndIf
If $down Then
$down = False
If $ci[4] = $hGo And $ci[2] = 0 Then
If TimerDiff($gClickGuard) < 400 Then Return False
Return True
EndIf
EndIf
Return False
EndFunc
Func _RequestCancel()
; Нажали «ОТМЕНИТЬ»: кнопка сразу серая и больше не реагирует на клики.
; Разметку доигрываем до конца, копирование прерываем сразу.
If $gCancel Then Return
$gCancel = True
$gCancelDown = False
GUICtrlSetData($hGo, "Отменяю. Ждите...")
GUICtrlSetBkColor($hGo, 0xAEB6C4)
GUICtrlSetColor($hGo, 0xEDEFF3)
$gCancelBg = 0xAEB6C4
If $gPhase = 2 Then _KillCopy()
EndFunc
Func _KillCopy()
; Прерываем текущую распаковку: гасим всё дерево процессов cmd/dism
If $gRunPid <> 0 Then
Run(@ComSpec & ' /c taskkill /T /F /PID ' & $gRunPid, "", @SW_HIDE)
$gRunPid = 0
EndIf
EndFunc
Func _CancelPoll()
; Нажатие «ОТМЕНИТЬ» во время установки.
; Разметку не прерываем — ждём её конца. Копирование прерываем сразу.
If Not $gBusy Then Return
If $gCancel Then Return
_CancelLook()
If _GoPoll() Then _RequestCancel()
EndFunc
Func _Msg($flags, $title, $text)
; Пока открыто окно — интерфейс затенён и неактивен
Local $prev = $gModal
$gModal = True
_GoState(False)
_DimControls(True)
Local $r = MsgBox($flags, $title, $text)
$gModal = $prev
$gClickGuard = TimerInit()
If Not $gBusy Then
_DimControls(False)
_RefreshGo()
Else
_CancelLook()
EndIf
Return $r
EndFunc
Func _GoState($on)
; Цвет главной кнопки: активна / недоступна (идёт запись или открыт диалог)
If $on Then
GUICtrlSetBkColor($hGo, 0x2563EB)
GUICtrlSetColor($hGo, 0xFFFFFF)
Else
GUICtrlSetBkColor($hGo, 0xAEB6C4)
GUICtrlSetColor($hGo, 0xEDEFF3)
EndIf
EndFunc
Func _ArrowState()
; Треугольник списка: тусклый, если накопителей нет
If UBound($aDiskName) = 0 Then
GUICtrlSetColor($hArrow, 0xC7CBD3)
Else
GUICtrlSetColor($hArrow, 0x6B7280)
EndIf
EndFunc
Func _GoCaption()
; Надпись на главной кнопке: сборка уже стоит — «Переустановить»
If $gBusy Then Return
If $gInstalled Then
GUICtrlSetData($hGo, "Переустановить")
Else
GUICtrlSetData($hGo, "УСТАНОВИТЬ")
EndIf
EndFunc
Func _CheckInstalled()
; Сборка считается установленной, если на выбранном диске есть оба маркера:
; 1-й раздел: \efi\Paravis\Boot\NOT.DEL, 3-й раздел: \ISO\Ventoy\NOT.DEL
$gInstalled = False
If UBound($aDiskNum) > 0 And $gSelIdx >= 0 And $gSelIdx < UBound($aDiskNum) Then
Local $disk = $aDiskNum[$gSelIdx]
Local $p1 = _PartitionLetter($disk, 0)
If $p1 <> "" And FileExists($p1 & "efi\Paravis\Boot\NOT.DEL") Then
Local $p3 = _PartitionLetter($disk, 2)
If $p3 <> "" And FileExists($p3 & "ISO\Ventoy\NOT.DEL") Then $gInstalled = True
EndIf
EndIf
_GoCaption()
EndFunc
Func _CanInstall()
If UBound($aDiskNum) = 0 Then Return False
If $gSelIdx < 0 Or $gSelIdx >= UBound($aDiskSize) Then Return False
; порог: заявленный объём меньше 32 ГБ — установка запрещена
If _NominalGb($aDiskSize[$gSelIdx]) < 32 Then Return False
Return True
EndFunc
Func _RefreshGo()
$gCanInstall = _CanInstall()
_GoCaption()
_GoState($gCanInstall)
EndFunc