1191 lines
48 KiB
AutoIt
1191 lines
48 KiB
AutoIt
; 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 |