# ============================================================== # Ortak yardimcilar (saf) # ============================================================== $script:KT_Surum = "2.2" function Test-KTAscii { <# .SYNOPSIS Saf. Metin yalniz yazdirilabilir ASCII mi (karar 42). #> param([AllowNull()][string]$Text) return ($null -ne $Text -and $Text -cmatch '^[\x20-\x7E]*$') } function ConvertTo-KTAsciiText { <# .SYNOPSIS Saf. ASCII disi karakterleri '?' yapar (sistemden gelen adlar ekrana/loga yazilirken). #> param([AllowNull()][string]$Text) if ($null -eq $Text) { return "" } return [regex]::Replace($Text, '[^\x20-\x7E]', '?') } function Get-KTField { <# .SYNOPSIS Saf. Hashtable veya JSON'dan gelen nesneden alan okur; yoksa $null. #> param($Obj, [string]$Name) if ($null -eq $Obj) { return $null } if ($Obj -is [System.Collections.IDictionary]) { return $Obj[$Name] } $p = $Obj.PSObject.Properties[$Name] if ($p) { return $p.Value } else { return $null } } function Copy-KTAction { <# .SYNOPSIS Saf. Islemin sig kopyasi (hashtable). Hashtable veya JSON'dan gelen nesne kabul eder. #> param([Parameter(Mandatory)] $Action) $c = @{} if ($Action -is [System.Collections.IDictionary]) { foreach ($k in $Action.Keys) { $c[$k] = $Action[$k] } } else { foreach ($p in $Action.PSObject.Properties) { $c[$p.Name] = $p.Value } } return $c } function ConvertTo-KTStateKey { <# .SYNOPSIS Saf. Durum nesnesini karsilastirilabilir tek metne cevirir. JSON'dan okunan durum ile canli okunan durum ayni metni verir (ad sirasi sabit, ordinal). #> param([AllowNull()] $State) function _norm($o) { if ($null -eq $o) { return $null } if ($o -is [string] -or $o -is [bool]) { return $o } # Sayilar tek bicim: JSON'dan Int64, canli okumadan Int32/Int64 gelebilir if ($o -is [ValueType]) { return [string]::Format([Globalization.CultureInfo]::InvariantCulture, "{0}", $o) } if ($o -is [System.Collections.IDictionary] -or $o -is [pscustomobject]) { $names = if ($o -is [System.Collections.IDictionary]) { [string[]]@($o.Keys) } else { [string[]]@($o.PSObject.Properties.Name) } [Array]::Sort($names, [StringComparer]::Ordinal) $d = [ordered]@{} foreach ($n in $names) { $d[$n] = _norm (Get-KTField $o $n) } return $d } if ($o -is [System.Collections.IEnumerable]) { return ,([object[]]@(foreach ($x in $o) { _norm $x })) } return [string]$o } return (ConvertTo-Json -InputObject (_norm $State) -Compress -Depth 30) } # ============================================================== # Kayit defteri islemleri: Get / Test / Set / Undo / Restored / Check # ============================================================== # RegValue : deger yaz @{ Tur = 'RegValue'; Yol = 'HKLM:\...'; Ad = '...'; DegerTuru = 'DWord'; Veri = 38 } # RegValueDelete : deger sil @{ Tur = 'RegValueDelete'; Yol; Ad } # RegKeyDelete : anahtari agaciyla sil @{ Tur = 'RegKeyDelete'; Yol } (tum agac yedeklenir) # Get : SALT OKUNUR. Durumdaki Veri JSON'a uygun kodlanir: # DWord sayi, QWord ondalik metin, Binary base64, MultiString dizi, ExpandString genisletilmemis. # Test : saf; yalniz Get ciktisina bakar. # Set/Undo: yazar. Undo tekrar calistirilabilir. # Yalniz HKLM, HKCU ve HKU:\.DEFAULT (oturum acma ekrani ayarlari, QOL09). HKCR birlesik gorunumdur; gercek yol # (HKLM:\SOFTWARE\Classes vb.) yazilir. HKU altinda baska kullanici (SID) reddedilir; .DEFAULT makine kapsamindadir. # 64-bit gorunum kullanilir (32-bit PowerShell'de de dogru anahtara gider). $script:KT_RegKinds = @("String", "ExpandString", "Binary", "DWord", "MultiString", "QWord") # ---------------- Yol ve kodlama (saf) ---------------- function ConvertTo-KTRegPath { <# .SYNOPSIS Saf. 'HKCU:\A\B' / 'HKEY_LOCAL_MACHINE\A' / 'HKU:\.DEFAULT\A' -> @{ Hive; SubKey }. Diger kokler reddedilir. #> param([Parameter(Mandatory)][string]$Path) $m = [regex]::Match($Path, '^(HKCU|HKLM|HKU|HKEY_CURRENT_USER|HKEY_LOCAL_MACHINE|HKEY_USERS):?\\(.+)$', 'IgnoreCase, CultureInvariant') if (-not $m.Success) { throw "Desteklenmeyen kayit defteri yolu: $Path" } $hive = switch ($m.Groups[1].Value.ToUpperInvariant()) { "HKEY_CURRENT_USER" { "HKCU" } "HKEY_LOCAL_MACHINE" { "HKLM" } "HKEY_USERS" { "HKU" } default { $_ } } $sub = $m.Groups[2].Value.TrimEnd('\') if ($hive -eq "HKU" -and -not [regex]::IsMatch($sub, '^\.DEFAULT(\\|$)', 'IgnoreCase, CultureInvariant')) { throw "HKU altinda yalniz .DEFAULT desteklenir: $Path" } return [pscustomobject]@{ Hive = $hive; SubKey = $sub } } function Get-KTRegHive { <# .SYNOPSIS Saf. ConvertTo-KTRegPath Hive adi -> RegistryHive. #> param([Parameter(Mandatory)][string]$Hive) switch ($Hive) { "HKCU" { return [Microsoft.Win32.RegistryHive]::CurrentUser } "HKU" { return [Microsoft.Win32.RegistryHive]::Users } default { return [Microsoft.Win32.RegistryHive]::LocalMachine } } } function ConvertTo-KTRegData { <# .SYNOPSIS Saf. Kayit defteri degerini JSON'a uygun bicime cevirir. #> param([Parameter(Mandatory)][string]$ValueType, [AllowNull()] $Data) switch ($ValueType) { "DWord" { # GetValue Int32 dondurur (0x80000000 ustu negatif); hep 0..0xFFFFFFFF saklanir $v = [int64]$Data; if ($v -lt 0 -and $v -ge -2147483648) { $v += 4294967296 } if ($v -lt 0 -or $v -gt 4294967295) { throw "DWord araliginin disinda: $Data" } return $v } "QWord" { $ci = [Globalization.CultureInfo]::InvariantCulture if ($Data -is [string]) { return ([uint64]::Parse($Data, $ci)).ToString($ci) } if ($Data -is [uint64]) { return $Data.ToString($ci) } return [BitConverter]::ToUInt64([BitConverter]::GetBytes([int64]$Data), 0).ToString($ci) } "Binary" { return [Convert]::ToBase64String([byte[]]@($Data)) } "MultiString" { return ,([string[]]@($Data)) } "String" { return [string]$Data } "ExpandString" { return [string]$Data } default { throw "Desteklenmeyen deger turu: $ValueType" } } } function ConvertFrom-KTRegData { <# .SYNOPSIS Saf. JSON bicimini RegistryKey.SetValue'nun bekledigi .NET turune cevirir. #> param([Parameter(Mandatory)][string]$ValueType, [AllowNull()] $Data) switch ($ValueType) { "DWord" { return [BitConverter]::ToInt32([BitConverter]::GetBytes([uint32]([uint64]$Data)), 0) } "QWord" { return [BitConverter]::ToInt64([BitConverter]::GetBytes([uint64]::Parse([string]$Data, [Globalization.CultureInfo]::InvariantCulture)), 0) } "Binary" { return ,([Convert]::FromBase64String([string]$Data)) } "MultiString" { return ,([string[]]@($Data)) } "String" { return [string]$Data } "ExpandString" { return [string]$Data } default { throw "Desteklenmeyen deger turu: $ValueType" } } } # ---------------- Dusuk seviye erisim ---------------- function Open-KTRegKey { <# .SYNOPSIS 64-bit gorunumde anahtari acar; yoksa $null. -Writable ile yazilabilir. #> param([Parameter(Mandatory)][string]$Path, [switch]$Writable) $p = ConvertTo-KTRegPath $Path $hive = Get-KTRegHive $p.Hive $base = [Microsoft.Win32.RegistryKey]::OpenBaseKey($hive, [Microsoft.Win32.RegistryView]::Registry64) try { return $base.OpenSubKey($p.SubKey, [bool]$Writable) } finally { $base.Dispose() } } function New-KTRegKey { <# .SYNOPSIS Anahtari (eksik ust anahtarlarla) olusturur, yazilabilir dondurur. #> param([Parameter(Mandatory)][string]$Path) $p = ConvertTo-KTRegPath $Path $hive = Get-KTRegHive $p.Hive $base = [Microsoft.Win32.RegistryKey]::OpenBaseKey($hive, [Microsoft.Win32.RegistryView]::Registry64) try { return $base.CreateSubKey($p.SubKey, $true) } finally { $base.Dispose() } } function Get-KTRegValueName { <# .SYNOPSIS Saf. Islemin deger adi; '@' anahtarin varsayilan degeri (reg.exe gosterimi, kayitta bos ad). #> param([Parameter(Mandatory)] $Action) $a = [string]$Action.Ad if ($a -eq '@') { return '' } return $a } function Find-KTRegValueName { <# .SYNOPSIS Deger adini buyuk/kucuk harf duyarsiz (ordinal) arar; gercek adi veya $null. #> param([Parameter(Mandatory)] $Key, [AllowEmptyString()][string]$Name) foreach ($n in $Key.GetValueNames()) { if ([string]::Equals($n, $Name, [StringComparison]::OrdinalIgnoreCase)) { return $n } } return $null } function Get-KTMissingAncestor { <# .SYNOPSIS Yoldaki ilk var olmayan anahtar (en ustteki); hepsi varsa $null. #> param([Parameter(Mandatory)][string]$Path) $p = ConvertTo-KTRegPath $Path $parts = $p.SubKey -split '\\' for ($i = 1; $i -le $parts.Count; $i++) { $sub = "$($p.Hive):\" + ($parts[0..($i - 1)] -join '\') $k = Open-KTRegKey $sub if (-not $k) { return $sub } $k.Dispose() } return $null } function Remove-KTEmptyKeyChain { <# .SYNOPSIS Anahtardan baslayip StopAt'e kadar (dahil) yukari dogru BOS anahtarlari siler. Kok altindaki ilk duzey silinmez. #> param([Parameter(Mandatory)][string]$Path, [Parameter(Mandatory)][string]$StopAt) $cur = $Path.TrimEnd('\') $stop = $StopAt.TrimEnd('\') while ($true) { $parent = $cur.Substring(0, $cur.LastIndexOf('\')) if ($parent -notmatch '\\') { return } $k = Open-KTRegKey $cur if ($k) { $empty = ($k.ValueCount -eq 0 -and $k.SubKeyCount -eq 0); $k.Dispose() if (-not $empty) { return } $pk = Open-KTRegKey $parent -Writable try { $pk.DeleteSubKey($cur.Substring($cur.LastIndexOf('\') + 1), $false) } finally { $pk.Dispose() } } if ([string]::Equals($cur, $stop, [StringComparison]::OrdinalIgnoreCase)) { return } $cur = $parent } } # ---------------- RegValue ---------------- function Get-KTRegValueState { <# .SYNOPSIS Salt okunur. Durum: KeyExists, MissingFrom, Exists, ValueType, Data. #> param([Parameter(Mandatory)] $Action) $state = [ordered]@{ KeyExists = $false; MissingFrom = $null; Exists = $false; ValueType = $null; Data = $null } $k = Open-KTRegKey $Action.Yol if (-not $k) { $state.MissingFrom = Get-KTMissingAncestor $Action.Yol; return [pscustomobject]$state } try { $state.KeyExists = $true $real = Find-KTRegValueName $k (Get-KTRegValueName $Action) if ($null -ne $real) { $kind = $k.GetValueKind($real).ToString() if ($script:KT_RegKinds -notcontains $kind) { throw "Desteklenmeyen deger turu ($kind): $($Action.Yol)\$($Action.Ad)" } $state.Exists = $true; $state.ValueType = $kind $state.Data = ConvertTo-KTRegData $kind $k.GetValue($real, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames) } } finally { $k.Dispose() } return [pscustomobject]$state } function Test-KTRegValueState { <# .SYNOPSIS Saf. Tur ve veri hedefle ayniysa Applied, degilse NotApplied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if (-not $State.Exists -or $State.ValueType -ne $Action.DegerTuru) { return "NotApplied" } $want = ConvertTo-KTStateKey (ConvertTo-KTRegData $Action.DegerTuru $Action.Veri) if ((ConvertTo-KTStateKey $State.Data) -eq $want) { return "Applied" } else { return "NotApplied" } } function Set-KTRegValue { <# .SYNOPSIS Anahtari (yoksa) olusturup degeri yazar. Ayni adli deger farkli turdeyse tur da degisir. #> param([Parameter(Mandatory)] $Action) $k = New-KTRegKey $Action.Yol try { $real = Find-KTRegValueName $k (Get-KTRegValueName $Action) $name = if ($null -ne $real) { $real } else { Get-KTRegValueName $Action } $k.SetValue($name, (ConvertFrom-KTRegData $Action.DegerTuru (ConvertTo-KTRegData $Action.DegerTuru $Action.Veri)), [Microsoft.Win32.RegistryValueKind]$Action.DegerTuru) } finally { $k.Dispose() } } function Undo-KTRegValue { <# .SYNOPSIS Onceki durumu kurar: deger vardi -> eski tur/veri; yoktu -> sil; anahtar yoktu -> bos kalan anahtarlari sil. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if ($Before.Exists) { $k = New-KTRegKey $Action.Yol try { $real = Find-KTRegValueName $k (Get-KTRegValueName $Action) $name = if ($null -ne $real) { $real } else { Get-KTRegValueName $Action } $k.SetValue($name, (ConvertFrom-KTRegData $Before.ValueType $Before.Data), [Microsoft.Win32.RegistryValueKind]$Before.ValueType) } finally { $k.Dispose() } return } $k = Open-KTRegKey $Action.Yol -Writable if ($k) { try { $real = Find-KTRegValueName $k (Get-KTRegValueName $Action); if ($null -ne $real) { $k.DeleteValue($real, $false) } } finally { $k.Dispose() } } if (-not $Before.KeyExists -and $Before.MissingFrom) { Remove-KTEmptyKeyChain -Path $Action.Yol -StopAt $Before.MissingFrom } } function Test-KTRegRestored { <# .SYNOPSIS Saf. Geri almadan sonra deger oncekiyle ayni mi (var/yok, tur, veri). Anahtarin varligi karsilastirilmaz: ayni anahtar zincirine yazan birden fazla islemde bos anahtarlar ancak son islem geri alinca silinebilir; ara adimda anahtar durabilir (21.10.2026 Windows testi). #> param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) $pick = { param($s) [ordered]@{ Exists = [bool]$s.Exists; ValueType = $s.ValueType; Data = $s.Data } } return ((ConvertTo-KTStateKey (& $pick $Now)) -eq (ConvertTo-KTStateKey (& $pick $Before))) } function Test-KTRegValueAction { <# .SYNOPSIS Saf. Tanimdaki RegValue isleminin alanlarini denetler; hata metinleri listesi. #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() $yol = [string]$Action.Yol if ($yol.StartsWith("{EkranKarti}")) { $yol = "HKLM:\SYSTEM\Class" + $yol.Substring("{EkranKarti}".Length) # sinif anahtari sistemden gelir $ur = Get-KTField $Action KartUretici if ($ur -and @("NVIDIA", "AMD", "Intel") -cnotcontains $ur) { $err.Add("RegValue: KartUretici NVIDIA, AMD veya Intel olmali") } } try { ConvertTo-KTRegPath $yol | Out-Null } catch { $err.Add($_.Exception.Message) } if ([string]::IsNullOrEmpty([string]$Action.Ad)) { $err.Add("RegValue: Ad zorunlu") } if ($script:KT_RegKinds -notcontains [string]$Action.DegerTuru) { $err.Add("RegValue: gecersiz DegerTuru: $($Action.DegerTuru)") } elseif ($null -eq $Action.Veri) { $err.Add("RegValue: Veri zorunlu") } elseif ($Action.Veri -is [string] -and $script:KT_VeriKaynaklari.ContainsKey([string]$Action.Veri)) { } # sistemden doldurulur else { try { ConvertTo-KTRegData $Action.DegerTuru $Action.Veri | Out-Null } catch { $err.Add("RegValue: $($_.Exception.Message)") } } return $err.ToArray() } # ---------------- RegValueDelete ---------------- # Durum ve geri alma RegValue ile ayni (Get-KTRegValueState, Undo-KTRegValue, Test-KTRegRestored). function Test-KTRegValueDeleteState { <# .SYNOPSIS Saf. Deger yoksa Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if ($State.Exists) { return "NotApplied" } else { return "Applied" } } function Set-KTRegValueDelete { <# .SYNOPSIS Degeri siler (ad buyuk/kucuk harf duyarsiz). Anahtar veya deger yoksa bir sey yapmaz. #> param([Parameter(Mandatory)] $Action) $k = Open-KTRegKey $Action.Yol -Writable if (-not $k) { return } try { $real = Find-KTRegValueName $k (Get-KTRegValueName $Action); if ($null -ne $real) { $k.DeleteValue($real, $false) } } finally { $k.Dispose() } } function Test-KTRegValueDeleteAction { <# .SYNOPSIS Saf. Tanim alanlari: Yol, Ad. Yol {EkranKarti} ile baslayabilir (KartUretici ile; RegValue gibi). #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() $yol = [string]$Action.Yol if ($yol.StartsWith("{EkranKarti}")) { $yol = "HKLM:\SYSTEM\Class" + $yol.Substring("{EkranKarti}".Length) # sinif anahtari sistemden gelir $ur = Get-KTField $Action KartUretici if ($ur -and @("NVIDIA", "AMD", "Intel") -cnotcontains $ur) { $err.Add("RegValueDelete: KartUretici NVIDIA, AMD veya Intel olmali") } } try { ConvertTo-KTRegPath $yol | Out-Null } catch { $err.Add($_.Exception.Message) } if ([string]::IsNullOrEmpty([string]$Action.Ad)) { $err.Add("RegValueDelete: Ad zorunlu") } return $err.ToArray() } # ---------------- RegKeyDelete ---------------- function Read-KTRegTree { <# .SYNOPSIS Acik anahtari tum degerleri ve alt anahtarlariyla okur (JSON'a uygun, ad sirasi sabit). #> param([Parameter(Mandatory)] $Key) $valNames = [string[]]@($Key.GetValueNames()); [Array]::Sort($valNames, [StringComparer]::OrdinalIgnoreCase) $subNames = [string[]]@($Key.GetSubKeyNames()); [Array]::Sort($subNames, [StringComparer]::OrdinalIgnoreCase) $vals = foreach ($n in $valNames) { $kind = $Key.GetValueKind($n).ToString() # Yedeklenemeyen tur varsa okuma hata verir; motor bu durumda silmez (Okunamadi) if ($script:KT_RegKinds -notcontains $kind) { throw "Desteklenmeyen deger turu ($kind): $($Key.Name)\$n" } [pscustomobject]@{ Name = $n; ValueType = $kind Data = (ConvertTo-KTRegData $kind $Key.GetValue($n, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)) } } $subs = foreach ($s in $subNames) { $sk = $Key.OpenSubKey($s, $false) try { $t = Read-KTRegTree $sk; $t.Name = $s; $t } finally { $sk.Dispose() } } # Sozluk degil nesne: sozlukte .Values, sozlugun kendi Values koleksiyonuyla karisir return [pscustomobject]@{ Name = ""; Values = @($vals); SubKeys = @($subs) } } function Write-KTRegTree { <# .SYNOPSIS Agaci yazilabilir anahtara yazar. Var olan fazla degerleri silmez (birlestirir). #> param([Parameter(Mandatory)] $Key, [Parameter(Mandatory)] $Tree) foreach ($v in @($Tree.Values)) { if ($null -eq $v) { continue } $Key.SetValue([string]$v.Name, (ConvertFrom-KTRegData $v.ValueType $v.Data), [Microsoft.Win32.RegistryValueKind]$v.ValueType) } foreach ($s in @($Tree.SubKeys)) { if ($null -eq $s) { continue } $sk = $Key.CreateSubKey([string]$s.Name, $true) try { Write-KTRegTree $sk $s } finally { $sk.Dispose() } } } function Get-KTRegKeyState { <# .SYNOPSIS Salt okunur. Durum: Exists ve tum agac (Tree). #> param([Parameter(Mandatory)] $Action) $k = Open-KTRegKey $Action.Yol if (-not $k) { return [pscustomobject]@{ Exists = $false; Tree = $null } } try { return [pscustomobject]@{ Exists = $true; Tree = (Read-KTRegTree $k) } } finally { $k.Dispose() } } function Test-KTRegKeyState { <# .SYNOPSIS Saf. Anahtar yoksa Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if ($State.Exists) { return "NotApplied" } else { return "Applied" } } function Set-KTRegKeyDelete { <# .SYNOPSIS Anahtari tum alt agaciyla siler. Yoksa bir sey yapmaz. #> param([Parameter(Mandatory)] $Action) $p = ConvertTo-KTRegPath $Action.Yol $i = $p.SubKey.LastIndexOf('\') $pk = Open-KTRegKey ("$($p.Hive):\" + $p.SubKey.Substring(0, $i)) -Writable if (-not $pk) { return } try { $pk.DeleteSubKeyTree($p.SubKey.Substring($i + 1), $false) } finally { $pk.Dispose() } } function Undo-KTRegKeyDelete { <# .SYNOPSIS Anahtar vardiysa agaci yedekten kurar. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if (-not $Before.Exists) { return } $k = New-KTRegKey $Action.Yol try { Write-KTRegTree $k $Before.Tree } finally { $k.Dispose() } } function Test-KTRegKeyRestored { <# .SYNOPSIS Saf. Anahtar varligi ve agacin tamami oncekiyle ayni mi. #> param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) $pick = { param($s) [ordered]@{ Exists = [bool]$s.Exists; Tree = $s.Tree } } return ((ConvertTo-KTStateKey (& $pick $Now)) -eq (ConvertTo-KTStateKey (& $pick $Before))) } function Test-KTRegKeyDeleteAction { <# .SYNOPSIS Saf. Tanim alanlari: Yol. Koke yakin anahtar silinemez: kok altinda en az 3 duzey (ornek HKCU:\Software\Microsoft\X). Yanlis yazilmis bir yol SOFTWARE gibi bir agaci silmesin. #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() try { $p = ConvertTo-KTRegPath ([string]$Action.Yol) $en = if ($p.Hive -eq "HKU") { 4 } else { 3 } # HKU: .DEFAULT duzeyi sayilmaz if (@($p.SubKey -split '\\' | Where-Object { $_ }).Count -lt $en) { $err.Add("RegKeyDelete: koke cok yakin anahtar silinemez: $($Action.Yol)") } } catch { $err.Add($_.Exception.Message) } return $err.ToArray() } # ---------------- RegValue: ag kartina gore genisletme ---------------- # Yol icinde {AgKarti} gecerse islem sistem analizindeki her fiziksel ag kartina acilir, {AgKarti} kartin # arayuz GUID'i olur (ornek NetBT\Parameters\Interfaces\Tcpip_{AgKarti}). KartTuru (Ethernet / Wi-Fi) ile suzulebilir. # Yedekten gelen somut islemde {AgKarti} kalmaz, oldugu gibi doner. # Yol {EkranKarti} ile baslarsa islem her ekran kartinin gercek sinif anahtarina acilir (karar 11; analiz GpuKartlari.ClassKey); # KartUretici (NVIDIA / AMD / Intel) ile suzulebilir. Sinif anahtari okunamayan kart atlanir. # Veri '{RamKB}' ise sistem analizindeki toplam RAM (KB) yazilir (CPU04); bilgi yoksa islem atlanir. $script:KT_VeriKaynaklari = @{ "{RamKB}" = "RamKB" } function Expand-KTRegValue { <# .SYNOPSIS Saf. Veri kaynagi ({RamKB}) sistemden doldurulur; {AgKarti} yoksa islem oldugu gibi, varsa her uygun kart icin GUID'li kopya. Gerekli sistem bilgisi yoksa bos liste (islem atlanir). #> param([Parameter(Mandatory)] $Action, $Sistem = $null) $veri = Get-KTField $Action Veri if ($veri -is [string] -and $script:KT_VeriKaynaklari.ContainsKey($veri)) { $deger = Get-KTField $Sistem $script:KT_VeriKaynaklari[$veri] if (-not $deger -or [int64]$deger -le 0) { return @() } $Action = Copy-KTAction $Action $Action.Veri = [int64]$deger } if (([string](Get-KTField $Action Yol)).StartsWith("{EkranKarti}")) { return (Expand-KTRegValueGpu -Action $Action -Sistem $Sistem) } if (-not ([string](Get-KTField $Action Yol)).Contains("{AgKarti}")) { return $Action } $out = [System.Collections.Generic.List[object]]::new() $tur = [string](Get-KTField $Action KartTuru) foreach ($k in @(Get-KTField $Sistem AgKartlari)) { if ($null -eq $k) { continue } if ($tur -and [string](Get-KTField $k Tur) -ne $tur) { continue } $c = Copy-KTAction $Action $c.Yol = ([string]$c.Yol).Replace("{AgKarti}", (ConvertTo-KTInterfaceGuid ([string](Get-KTField $k Guid)))) $c.KartAdi = [string](Get-KTField $k Ad) $out.Add($c) } # Virgulsuz: cagiran @(...) ile sarar (OGRENILENLER 4) return $out.ToArray() } function Expand-KTRegValueGpu { <# .SYNOPSIS Saf. {EkranKarti} yollu islemi uygun her ekran kartinin sinif anahtarina acar; uygun kart yoksa bos liste. #> param([Parameter(Mandatory)] $Action, $Sistem = $null) $out = [System.Collections.Generic.List[object]]::new() $ur = [string](Get-KTField $Action KartUretici) foreach ($k in @(Get-KTField $Sistem GpuKartlari)) { if ($null -eq $k) { continue } $key = [string](Get-KTField $k ClassKey) if (-not $key) { continue } if ($ur -and [string](Get-KTField $k Uretici) -ne $ur) { continue } $c = Copy-KTAction $Action $c.Yol = $key + ([string]$c.Yol).Substring("{EkranKarti}".Length) $c.KartAdi = [string](Get-KTField $k Ad) $out.Add($c) } return $out.ToArray() } # ============================================================== # Ayarlar sayfasi gizleme islemi (AyarSayfasi): Get / Test / Set / Undo / Restored / Check # ============================================================== # Islem: @{ Tur = 'AyarSayfasi'; Sayfa = 'home' } # Ilke: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer SettingsPageVisibility (REG_SZ), # "hide:" + noktali virgulle ayrilmis sayfa kimlikleri (ms-settings: oneki olmadan). Birden fazla tweak ayni dizeyi # paylasir (ornek Y20): her islem yalniz kendi sayfasini ekler/cikarir, digerlerine dokunmaz. # "showonly:" bicimindeki dizeye dokunulmaz (baska bir yonetim; islem hata verir). # Durum: @{ Exists (deger var); Raw (dize); Pages (gizli sayfalar); Mode ('hide' / 'showonly' / ''); Gizli (bu islemin sayfasi gizli mi) } # Dis erisim ince sarmalayicilarda (Get-KTPageVisibilityRaw, Set-KTPageVisibilityRaw); testler bunlari degistirir. $script:KT_SayfaYolu = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer' $script:KT_SayfaDeger = 'SettingsPageVisibility' function ConvertFrom-KTPageVisibility { <# .SYNOPSIS Saf. Dizeyi @{ Mode; Pages } bicimine ayirir. Bos veya yoksa Mode ''. #> param([AllowNull()][AllowEmptyString()][string]$Raw) $s = ([string]$Raw).Trim() if (-not $s) { return [pscustomobject]@{ Mode = ""; Pages = @() } } $i = $s.IndexOf(':') $mode = if ($i -gt 0) { $s.Substring(0, $i).Trim().ToLowerInvariant() } else { "?" } $rest = if ($i -ge 0) { $s.Substring($i + 1) } else { $s } $pages = @($rest.Split(';') | ForEach-Object { $_.Trim().ToLowerInvariant() } | Where-Object { $_ }) return [pscustomobject]@{ Mode = $mode; Pages = $pages } } function ConvertTo-KTPageVisibility { <# .SYNOPSIS Saf. Sayfa listesinden "hide:a;b" dizesi; liste bossa $null (deger silinir). #> param([string[]]$Pages) $p = @($Pages | Where-Object { $_ }) if ($p.Count -eq 0) { return $null } return "hide:" + ($p -join ";") } # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTPageVisibilityRaw { <# .SYNOPSIS Salt okunur. Deger yoksa $null. #> $p = Get-ItemProperty -LiteralPath $script:KT_SayfaYolu -Name $script:KT_SayfaDeger -ErrorAction SilentlyContinue if ($null -eq $p) { return $null } return [string]$p.($script:KT_SayfaDeger) } function Set-KTPageVisibilityRaw { <# .SYNOPSIS Dizeyi yazar; $null ise degeri siler. #> param([AllowNull()][string]$Value) if ($null -eq $Value) { Remove-ItemProperty -LiteralPath $script:KT_SayfaYolu -Name $script:KT_SayfaDeger -ErrorAction SilentlyContinue; return } if (-not (Test-Path -LiteralPath $script:KT_SayfaYolu)) { New-Item -Path $script:KT_SayfaYolu -Force | Out-Null } New-ItemProperty -LiteralPath $script:KT_SayfaYolu -Name $script:KT_SayfaDeger -PropertyType String -Value $Value -Force | Out-Null } # ---------------- Get / Test / Set / Undo ---------------- function Get-KTPageState { <# .SYNOPSIS Salt okunur. #> param([Parameter(Mandatory)] $Action) $raw = Get-KTPageVisibilityRaw $p = ConvertFrom-KTPageVisibility $raw $gizli = $p.Mode -eq "hide" -and @($p.Pages) -contains ([string]$Action.Sayfa).ToLowerInvariant() return [pscustomobject]@{ Exists = ($null -ne $raw); Raw = $raw; Pages = @($p.Pages); Mode = $p.Mode; Gizli = [bool]$gizli } } function Test-KTPageState { <# .SYNOPSIS Saf. Sayfa gizli listede ise Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if ($State.Gizli) { return "Applied" } else { return "NotApplied" } } function Set-KTPage { <# .SYNOPSIS Sayfayi gizli listeye ekler; diger sayfalar korunur. "showonly:" veya bilinmeyen bicimde hata. #> param([Parameter(Mandatory)] $Action) $cur = Get-KTPageState $Action if ($cur.Mode -and $cur.Mode -ne "hide") { throw "SettingsPageVisibility baska bicimde ($($cur.Raw)); dokunulmadi" } $sayfa = ([string]$Action.Sayfa).ToLowerInvariant() if (@($cur.Pages) -contains $sayfa) { return } Set-KTPageVisibilityRaw (ConvertTo-KTPageVisibility (@($cur.Pages) + $sayfa)) } function Undo-KTPage { <# .SYNOPSIS Sayfa onceden gizli degildiyse listeden cikarir; liste bosalir ve deger onceden yoksa deger silinir. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) $sayfa = ([string]$Action.Sayfa).ToLowerInvariant() if ($Before.Gizli) { return } $cur = Get-KTPageState $Action if ($cur.Mode -and $cur.Mode -ne "hide") { throw "SettingsPageVisibility baska bicimde ($($cur.Raw)); dokunulmadi" } $kalan = @($cur.Pages | Where-Object { $_ -ne $sayfa }) if ($kalan.Count -eq 0 -and $Before.Exists) { Set-KTPageVisibilityRaw ([string]$Before.Raw); return } Set-KTPageVisibilityRaw (ConvertTo-KTPageVisibility $kalan) } function Test-KTPageRestored { <# .SYNOPSIS Saf. Sayfanin gizli olup olmamasi onceki durumla ayni ise basarili (diger sayfalara bakilmaz). #> param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) return ([bool]$Before.Gizli -eq [bool]$Now.Gizli) } function Test-KTPageAction { <# .SYNOPSIS Saf. Tanim alanlari: Sayfa (ms-settings: oneki ve ; : bosluk icermez). #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() if ([string]::IsNullOrEmpty([string]$Action.Sayfa) -or [string]$Action.Sayfa -match '[;:\s]') { $err.Add("AyarSayfasi: Sayfa zorunlu, ';' ':' bosluk icermez") } return $err.ToArray() } # ============================================================== # Kayit dizesindeki tek alan (DizeAlani): Get / Test / Set / Undo / Restored / Check # ============================================================== # Islem: @{ Tur = 'DizeAlani'; Yol = 'HKCU:\Software\Microsoft\DirectX\UserGpuPreferences'; Ad = 'DirectXUserGlobalSettings'; # Alan = 'SwapEffectUpgradeEnable'; Deger = '1' } # Deger REG_SZ, "Alan=Deger;" ciftlerinden olusur (ornek: "VRROptimizeEnable=0;SwapEffectUpgradeEnable=1;"). # Birden fazla tweak ayni dizeyi paylasir (GPU08, GPU09): her islem yalniz kendi alanini yazar/geri alir, digerleri korunur. # Yazma sirasi korunur; yeni alan sona eklenir; dize ';' ile biter. # Durum: @{ Exists (deger var); Raw (dize); Alan (bu alanin degeri veya $null) } # Dis erisim ince sarmalayicilarda (Get-KTDizeRaw, Set-KTDizeRaw); testler bunlari degistirir. function ConvertFrom-KTDize { <# .SYNOPSIS Saf. Dizeyi sirali @( @{ Ad; Deger } ) listesine ayirir; bos parcalar atlanir. #> param([AllowNull()][AllowEmptyString()][string]$Raw) $out = [System.Collections.Generic.List[object]]::new() foreach ($p in ([string]$Raw).Split(';')) { $t = $p.Trim() if (-not $t) { continue } $i = $t.IndexOf('=') if ($i -lt 0) { $out.Add([pscustomobject]@{ Ad = $t; Deger = $null }) } else { $out.Add([pscustomobject]@{ Ad = $t.Substring(0, $i).Trim(); Deger = $t.Substring($i + 1).Trim() }) } } return $out.ToArray() } function ConvertTo-KTDize { <# .SYNOPSIS Saf. Alan listesinden "a=1;b=2;" dizesi; liste bossa $null (deger silinir). #> param($Alanlar) $p = @($Alanlar | Where-Object { $_ } | ForEach-Object { if ($null -eq $_.Deger) { $_.Ad } else { "$($_.Ad)=$($_.Deger)" } }) if ($p.Count -eq 0) { return $null } return ($p -join ";") + ";" } function Set-KTDizeField { <# .SYNOPSIS Saf. Dizede alani verilen degere ayarlar ($null: alani cikarir); diger alanlar ve sira korunur. #> param([AllowNull()][AllowEmptyString()][string]$Raw, [Parameter(Mandatory)][string]$Alan, $Deger) # tipsiz: [string] $null degerini "" yapar $list = [System.Collections.Generic.List[object]]::new() $bulundu = $false foreach ($a in @(ConvertFrom-KTDize $Raw)) { if ([string]::Equals($a.Ad, $Alan, [StringComparison]::OrdinalIgnoreCase)) { if ($bulundu -or $null -eq $Deger) { continue } $list.Add([pscustomobject]@{ Ad = $a.Ad; Deger = [string]$Deger }); $bulundu = $true } else { $list.Add($a) } } if (-not $bulundu -and $null -ne $Deger) { $list.Add([pscustomobject]@{ Ad = $Alan; Deger = [string]$Deger }) } return (ConvertTo-KTDize $list.ToArray()) } function Get-KTDizeField { <# .SYNOPSIS Saf. Alanin degeri veya $null. #> param([AllowNull()][AllowEmptyString()][string]$Raw, [Parameter(Mandatory)][string]$Alan) foreach ($a in @(ConvertFrom-KTDize $Raw)) { if ([string]::Equals($a.Ad, $Alan, [StringComparison]::OrdinalIgnoreCase)) { return $a.Deger } } return $null } # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTDizeRaw { <# .SYNOPSIS Salt okunur. Deger yoksa $null. #> param([Parameter(Mandatory)][string]$Path, [Parameter(Mandatory)][string]$Name) $p = Get-ItemProperty -LiteralPath $Path -Name $Name -ErrorAction SilentlyContinue if ($null -eq $p) { return $null } return [string]$p.$Name } function Set-KTDizeRaw { <# .SYNOPSIS Dizeyi yazar; $null ise degeri siler. #> param([Parameter(Mandatory)][string]$Path, [Parameter(Mandatory)][string]$Name, [AllowNull()][string]$Value) if ($null -eq $Value) { Remove-ItemProperty -LiteralPath $Path -Name $Name -ErrorAction SilentlyContinue; return } if (-not (Test-Path -LiteralPath $Path)) { New-Item -Path $Path -Force | Out-Null } New-ItemProperty -LiteralPath $Path -Name $Name -PropertyType String -Value $Value -Force | Out-Null } # ---------------- Get / Test / Set / Undo ---------------- function Get-KTDizeState { param([Parameter(Mandatory)] $Action) $raw = Get-KTDizeRaw -Path ([string]$Action.Yol) -Name ([string]$Action.Ad) return [pscustomobject]@{ Exists = ($null -ne $raw); Raw = $raw; Alan = (Get-KTDizeField $raw ([string]$Action.Alan)) } } function Test-KTDizeState { <# .SYNOPSIS Saf. Alan hedef degerdeyse Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if ([string]$State.Alan -eq [string]$Action.Deger -and $null -ne $State.Alan) { return "Applied" } return "NotApplied" } function Set-KTDize { param([Parameter(Mandatory)] $Action) $cur = Get-KTDizeRaw -Path ([string]$Action.Yol) -Name ([string]$Action.Ad) Set-KTDizeRaw -Path ([string]$Action.Yol) -Name ([string]$Action.Ad) -Value (Set-KTDizeField $cur ([string]$Action.Alan) ([string]$Action.Deger)) } function Undo-KTDize { <# .SYNOPSIS Alan onceki degerine doner (onceden yoksa cikarilir); dize bosalir ve deger onceden yoksa deger silinir. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) $cur = Get-KTDizeRaw -Path ([string]$Action.Yol) -Name ([string]$Action.Ad) $yeni = Set-KTDizeField $cur ([string]$Action.Alan) $(if ($null -eq $Before.Alan) { $null } else { [string]$Before.Alan }) if ($null -eq $yeni -and $Before.Exists) { $yeni = [string]$Before.Raw } if ($yeni -ceq $cur) { return } Set-KTDizeRaw -Path ([string]$Action.Yol) -Name ([string]$Action.Ad) -Value $yeni } function Test-KTDizeRestored { <# .SYNOPSIS Saf. Alanin degeri onceki ile ayni ise basarili (diger alanlara bakilmaz). #> param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) return ([string]$Before.Alan -eq [string]$Now.Alan -and ($null -eq $Before.Alan) -eq ($null -eq $Now.Alan)) } function Test-KTDizeAction { <# .SYNOPSIS Saf. Yol (HKLM:/HKCU:), Ad, Alan (';' '=' bosluk icermez), Deger (';' icermez) zorunlu. #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() if ([string]$Action.Yol -cnotmatch '^(HKLM|HKCU):\\') { $err.Add("DizeAlani: Yol HKLM:\ veya HKCU:\ ile baslamali") } if ([string]::IsNullOrEmpty([string]$Action.Ad)) { $err.Add("DizeAlani: Ad zorunlu") } if ([string]::IsNullOrEmpty([string]$Action.Alan) -or [string]$Action.Alan -match '[;=\s]') { $err.Add("DizeAlani: Alan zorunlu, ';' '=' bosluk icermez") } if ($null -eq $Action.Deger -or [string]$Action.Deger -eq "" -or [string]$Action.Deger -match ';') { $err.Add("DizeAlani: Deger zorunlu, ';' icermez") } return $err.ToArray() } # ============================================================== # Servis islemi (Servis): Get / Test / Set / Undo / Restored / Check / Dogrula # ============================================================== # Islem: @{ Tur = 'Servis'; Ad = 'DiagTrack'; Baslangic = 'Disabled' | 'Manual' | 'Automatic' | 'AutomaticDelayed' } # Durum: @{ Exists; StartType; Status } # Baslangic turu Services\\Start + DelayedAutostart'tan okunur, sc.exe ile yazilir # (PS 5.1 Get-Service/Set-Service gecikmeli otomatigi bilmiyor). Surucu servislerine (Boot/System) dokunulmaz. # Disabled hedefinde calisan servis durdurulur (Adem 21.10.2026). Durdurma -Force OLMADAN ve sureli: # bagimli servis calisiyorsa durmaz; bu durumda baslangic turu yazilmis kalir, sonuc "yeniden baslatmada durur" # notuyla Uygulandi olur ve tweak'in yeniden baslatma gerekcesi Bilgisayar'a cikar (Dogrula). # Servis sistemde yoksa NotApplicable: o islem atlanir (karar 43.11). # Dis komutlar ince sarmalayicilarda (Get-KTServiceRaw, Invoke-KTScConfig, Invoke-KTServiceControl); # testler bunlari sahteleriyle degistirir. $script:KT_ServiceTimeoutSec = 10 $script:KT_ServisBaslangic = "Disabled", "Manual", "Automatic", "AutomaticDelayed" # ---------------- Saf ---------------- function ConvertTo-KTServiceStartType { <# .SYNOPSIS Saf. Start (0-4) + DelayedAutostart -> baslangic turu adi. #> param($Start, $Delayed) switch ([int]$Start) { 0 { return "Boot" } 1 { return "System" } 2 { if ([int]$Delayed -eq 1) { return "AutomaticDelayed" } else { return "Automatic" } } 3 { return "Manual" } 4 { return "Disabled" } default { return "Unknown" } } } function ConvertTo-KTScStartArg { <# .SYNOPSIS Saf. Baslangic turu -> sc.exe 'start=' degeri. Surucu turleri reddedilir. #> param([Parameter(Mandatory)][string]$StartType) switch ($StartType) { "AutomaticDelayed" { return "delayed-auto" } "Automatic" { return "auto" } "Manual" { return "demand" } "Disabled" { return "disabled" } default { throw "Desteklenmeyen baslangic turu: $StartType" } } } # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTServiceRaw { <# .SYNOPSIS Salt okunur. Servis yoksa $null; varsa @{ Start; DelayedAutostart; Status }. #> param([Parameter(Mandatory)][string]$Name) $k = Open-KTRegKey "HKLM:\SYSTEM\CurrentControlSet\Services\$Name" if (-not $k) { return $null } try { $start = $k.GetValue("Start", $null) if ($null -eq $start) { return $null } # Start degeri olmayan anahtar servis degil $delayed = $k.GetValue("DelayedAutostart", 0) } finally { $k.Dispose() } $svc = Get-Service -Name $Name -ErrorAction SilentlyContinue | Select-Object -First 1 $status = if ($svc) { $svc.Status.ToString() } else { "Unknown" } return [pscustomobject]@{ Start = [int]$start; DelayedAutostart = [int]$delayed; Status = $status } } function Invoke-KTScConfig { <# .SYNOPSIS sc.exe config start= . Basarisizsa hata firlatir. #> param([Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][string]$StartType) $out = & sc.exe config "$Name" start= (ConvertTo-KTScStartArg $StartType) 2>&1 if ($LASTEXITCODE -ne 0) { throw "sc.exe config basarisiz ($LASTEXITCODE): $Name -> $StartType | $(($out | Out-String).Trim())" } } function Invoke-KTServiceControl { <# .SYNOPSIS Servisi durdurur/baslatir; takilmasin diye ayri iste, sureli. Sonucu cagiran tekrar okur. #> param([Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][ValidateSet("Stop", "Start")][string]$Command, [int]$TimeoutSec = $script:KT_ServiceTimeoutSec) $job = Start-Job -ScriptBlock { param($n, $c) if ($c -eq "Stop") { Stop-Service -Name $n -ErrorAction SilentlyContinue } else { Start-Service -Name $n -ErrorAction SilentlyContinue } } -ArgumentList $Name, $Command $null = Wait-Job -Job $job -Timeout $TimeoutSec Remove-Job -Job $job -Force -ErrorAction SilentlyContinue } # ---------------- Get / Test / Set / Undo ---------------- function Get-KTServiceState { <# .SYNOPSIS Salt okunur. #> param([Parameter(Mandatory)] $Action) $raw = Get-KTServiceRaw ([string]$Action.Ad) if (-not $raw) { return [pscustomobject]@{ Exists = $false; StartType = $null; Status = $null } } return [pscustomobject]@{ Exists = $true; StartType = (ConvertTo-KTServiceStartType $raw.Start $raw.DelayedAutostart); Status = $raw.Status } } function Test-KTServiceState { <# .SYNOPSIS Saf. Servis yoksa NotApplicable. Baslangic turu esit ve (Disabled hedefinde) durmus ise Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if (-not $State.Exists) { return "NotApplicable" } if ($State.StartType -ne $Action.Baslangic) { return "NotApplied" } if ($Action.Baslangic -eq "Disabled" -and $State.Status -ne "Stopped") { return "NotApplied" } return "Applied" } function Set-KTService { <# .SYNOPSIS Baslangic turunu yazar; Disabled hedefinde calisiyorsa durdurmayi dener (durmazsa hata degil, bkz. Dogrula). #> param([Parameter(Mandatory)] $Action) $name = [string]$Action.Ad $cur = Get-KTServiceState $Action if (-not $cur.Exists) { throw "Servis bulunamadi: $name" } if ($cur.StartType -in "Boot", "System", "Unknown") { throw "Surucu/bilinmeyen servis turune dokunulmaz ($($cur.StartType)): $name" } if ($cur.StartType -ne $Action.Baslangic) { Invoke-KTScConfig -Name $name -StartType ([string]$Action.Baslangic) } if ($Action.Baslangic -eq "Disabled" -and $cur.Status -ne "Stopped") { Invoke-KTServiceControl -Name $name -Command Stop } } function Test-KTServiceVerify { <# .SYNOPSIS Saf. Set sonrasi dogrulama: baslangic turu yazildiysa basarili. Disabled ama hala calisiyorsa not + yeniden baslatma (servis durdurulamadi; bagimli servis olabilir). #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) $ok = $State.Exists -and $State.StartType -eq $Action.Baslangic if ($ok -and $Action.Baslangic -eq "Disabled" -and $State.Status -ne "Stopped") { return [pscustomobject]@{ Ok = $true; Not = "(Servis durdurulamadi; yeniden baslatmadan sonra durur)"; YenidenBaslat = "Bilgisayar" } } return [pscustomobject]@{ Ok = [bool]$ok; Not = ""; YenidenBaslat = "Yok" } } function Undo-KTService { <# .SYNOPSIS Onceki baslangic turunu yazar; onceden calisiyorduysa baslatir. Onceden durmus servisi durdurmaz. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if (-not $Before.Exists) { return } $name = [string]$Action.Ad $cur = Get-KTServiceState $Action if (-not $cur.Exists) { throw "Servis artik yok: $name" } if ($cur.StartType -ne $Before.StartType) { Invoke-KTScConfig -Name $name -StartType ([string]$Before.StartType) } if ($Before.Status -eq "Running" -and $cur.Status -ne "Running") { Invoke-KTServiceControl -Name $name -Command Start } } function Test-KTServiceRestored { <# .SYNOPSIS Saf. Baslangic turu ayni ve onceden calisiyorduysa simdi de calisiyor. #> param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) if (-not $Before.Exists) { return $true } if (-not $Now.Exists -or $Now.StartType -ne $Before.StartType) { return $false } if ($Before.Status -eq "Running" -and $Now.Status -ne "Running") { return $false } return $true } function Test-KTServiceAction { <# .SYNOPSIS Saf. Tanim alanlari: Ad, Baslangic. #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() if ([string]::IsNullOrEmpty([string]$Action.Ad) -or [string]$Action.Ad -match '[\\/]') { $err.Add("Servis: Ad zorunlu, yol icermez") } if ($script:KT_ServisBaslangic -cnotcontains [string]$Action.Baslangic) { $err.Add("Servis: gecersiz Baslangic: $($Action.Baslangic)") } return $err.ToArray() } # ============================================================== # Istege bagli Windows ozelligi islemi (Ozellik): Get / Test / Set / Undo / Restored / Check / Dogrula # ============================================================== # Islem: @{ Tur = 'Ozellik'; Ad = 'Recall'; Etkin = $false } # Durum: @{ Exists; Enabled; State } State: Enabled / Disabled / DisabledWithPayloadRemoved / EnablePending / DisablePending # Kapatma ozelligin dosyalarini silmez (DISM /Disable-Feature, /Remove yok); geri alma ozelligi yeniden acar. # Ozellik sistemde yoksa NotApplicable: o islem atlanir (karar 43.11). # Dis komutlar ince sarmalayicilarda (Get-KTFeatureRaw, Invoke-KTFeatureEnable); testler bunlari degistirir. $script:KT_OzellikAcik = "Enabled", "EnablePending" # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTFeatureRaw { <# .SYNOPSIS Salt okunur (yonetici ister). Ozellik yoksa $null; varsa @{ State }. Once adla sorgulanir (hizli). Bilinmeyen ad DISM'de hata verir ve hata metni dile gore degisir; bu yuzden hata olursa karar tum listeden verilir (yavas: GitHub makinesinde ~1 dk). Liste de okunamazsa hata firlatir. #> param([Parameter(Mandatory)][string]$Name) $f = $null try { $f = Get-WindowsOptionalFeature -Online -FeatureName $Name -ErrorAction Stop | Where-Object { $_.FeatureName -eq $Name } | Select-Object -First 1 } catch { $f = $null } if (-not $f) { $f = Get-WindowsOptionalFeature -Online -ErrorAction Stop | Where-Object { $_.FeatureName -eq $Name } | Select-Object -First 1 } if (-not $f) { return $null } return [pscustomobject]@{ State = $f.State.ToString() } } function Invoke-KTFeatureEnable { <# .SYNOPSIS Ozelligi acar veya kapatir (yeniden baslatmadan). Basarisizsa hata firlatir. #> param([Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][bool]$Enabled) if ($Enabled) { Enable-WindowsOptionalFeature -Online -FeatureName $Name -NoRestart -ErrorAction Stop | Out-Null } else { Disable-WindowsOptionalFeature -Online -FeatureName $Name -NoRestart -ErrorAction Stop | Out-Null } } # ---------------- Get / Test / Set / Undo ---------------- function Get-KTFeatureState { <# .SYNOPSIS Salt okunur. #> param([Parameter(Mandatory)] $Action) $raw = Get-KTFeatureRaw -Name ([string]$Action.Ad) if (-not $raw) { return [pscustomobject]@{ Exists = $false; Enabled = $null; State = $null } } return [pscustomobject]@{ Exists = $true; Enabled = ($script:KT_OzellikAcik -contains $raw.State); State = $raw.State } } function Test-KTFeatureState { <# .SYNOPSIS Saf. Ozellik yoksa NotApplicable; acik/kapali durumu esitse (bekleyen dahil) Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if (-not $State.Exists) { return "NotApplicable" } if ([bool]$State.Enabled -eq [bool]$Action.Etkin) { return "Applied" } else { return "NotApplied" } } function Set-KTFeature { param([Parameter(Mandatory)] $Action) Invoke-KTFeatureEnable -Name ([string]$Action.Ad) -Enabled ([bool]$Action.Etkin) } function Test-KTFeatureVerify { <# .SYNOPSIS Saf. Set sonrasi: durum hedefteyse basarili; islem yeniden baslatma bekliyorsa not + Bilgisayar. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) $ok = $State.Exists -and [bool]$State.Enabled -eq [bool]$Action.Etkin if ($ok -and [string]$State.State -like "*Pending") { return [pscustomobject]@{ Ok = $true; Not = "(Yeniden baslatmadan sonra tamamlanir)"; YenidenBaslat = "Bilgisayar" } } return [pscustomobject]@{ Ok = [bool]$ok; Not = ""; YenidenBaslat = "Yok" } } function Undo-KTFeature { <# .SYNOPSIS Onceki acik/kapali durumunu kurar. Ozellik o arada kalktiysa hata. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if (-not $Before.Exists) { return } $cur = Get-KTFeatureState $Action if (-not $cur.Exists) { throw "Ozellik artik yok: $($Action.Ad)" } if ([bool]$cur.Enabled -ne [bool]$Before.Enabled) { Invoke-KTFeatureEnable -Name ([string]$Action.Ad) -Enabled ([bool]$Before.Enabled) } } function Test-KTFeatureRestored { <# .SYNOPSIS Saf. Ozellik vardi ve acik/kapali durumu ayni ise basarili. #> param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) if (-not $Before.Exists) { return $true } return ($Now.Exists -and [bool]$Now.Enabled -eq [bool]$Before.Enabled) } function Test-KTFeatureAction { <# .SYNOPSIS Saf. Tanim alanlari: Ad (joker yok), Etkin (bool). #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() if ([string]::IsNullOrEmpty([string]$Action.Ad) -or [string]$Action.Ad -match '[\*\?\\ ]') { $err.Add("Ozellik: Ad zorunlu, joker/bosluk icermez") } if ($Action.Etkin -isnot [bool]) { $err.Add("Ozellik: Etkin `$true veya `$false olmali") } return $err.ToArray() } # ============================================================== # Disk alani ile ilgili sistem ozellikleri: hazirda bekletme (HazirdaBekletme), ayrilmis depolama (AyrilmisDepolama), TRIM (Trim) # ============================================================== # HazirdaBekletme: @{ Tur = 'HazirdaBekletme'; Etkin = $false } # Yazma powercfg /hibernate off|on (MS powercfg-command-line-options; kapatinca hiberfil.sys silinir, Hizli Baslangic da kapanir). # Okuma HKLM\SYSTEM\CurrentControlSet\Control\Power HibernateEnabled (powercfg'nin yazdigi deger; GitHub makinesinde karsilastirilir). # AyrilmisDepolama: @{ Tur = 'AyrilmisDepolama'; Etkin = $false } # Get-/Set-WindowsReservedStorageState (DISM /Set-ReservedStorageState ile ayni). Guncelleme surerken degistirilemez (hata). # Durum: @{ Exists; Enabled }. Ozellik yoksa (deger okunamadi) NotApplicable. # Dis komutlar ince sarmalayicilarda; testler bunlari degistirir. # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTHibernateRaw { <# .SYNOPSIS Salt okunur. HibernateEnabled (0/1) veya $null. #> $p = Get-ItemProperty -LiteralPath 'HKLM:\SYSTEM\CurrentControlSet\Control\Power' -Name HibernateEnabled -ErrorAction SilentlyContinue if ($null -eq $p) { return $null } return [int]$p.HibernateEnabled } function Invoke-KTHibernate { <# .SYNOPSIS powercfg /hibernate on|off. Basarisizsa hata. #> param([Parameter(Mandatory)][bool]$Enabled) $arg = if ($Enabled) { "on" } else { "off" } $out = & powercfg.exe /hibernate $arg 2>&1 if ($LASTEXITCODE -ne 0) { throw "powercfg /hibernate $arg basarisiz ($LASTEXITCODE): $(($out | Out-String).Trim())" } } function Get-KTReservedStorageRaw { <# .SYNOPSIS Salt okunur (yonetici). 'Enabled' / 'Disabled' veya $null (desteklenmiyor). #> $s = Get-WindowsReservedStorageState -ErrorAction Stop if ($null -eq $s) { return $null } return [string]$s.ReservedStorageState } function Invoke-KTReservedStorage { param([Parameter(Mandatory)][bool]$Enabled) # 0x800f0975: ayrilmis depolama guncelleme tarafindan kullaniliyor (Adem PC'si 03.11.2026); anlasilir metinle hata try { Set-WindowsReservedStorageState -State $(if ($Enabled) { "Enabled" } else { "Disabled" }) -ErrorAction Stop -WarningAction SilentlyContinue | Out-Null } catch { if ("$($_.Exception.Message)" -like "*0x800f0975*") { throw "Windows guncellemesi ayrilmis depolamayi kullaniyor; guncelleme bitince ve bilgisayar yeniden baslayinca tekrar deneyin" } throw } } # ---------------- HazirdaBekletme ---------------- function Get-KTHibernateState { param([Parameter(Mandatory)] $Action) $v = Get-KTHibernateRaw if ($null -eq $v) { return [pscustomobject]@{ Exists = $false; Enabled = $null } } return [pscustomobject]@{ Exists = $true; Enabled = ($v -ne 0) } } function Set-KTHibernate { param([Parameter(Mandatory)] $Action) Invoke-KTHibernate -Enabled ([bool]$Action.Etkin) } function Undo-KTHibernate { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if (-not $Before.Exists) { return } if ([bool](Get-KTHibernateState $Action).Enabled -ne [bool]$Before.Enabled) { Invoke-KTHibernate -Enabled ([bool]$Before.Enabled) } } # ---------------- AyrilmisDepolama ---------------- function Get-KTReservedStorageState { param([Parameter(Mandatory)] $Action) $v = Get-KTReservedStorageRaw if ($v -notin "Enabled", "Disabled") { return [pscustomobject]@{ Exists = $false; Enabled = $null } } return [pscustomobject]@{ Exists = $true; Enabled = ($v -eq "Enabled") } } function Set-KTReservedStorage { param([Parameter(Mandatory)] $Action) Invoke-KTReservedStorage -Enabled ([bool]$Action.Etkin) } function Undo-KTReservedStorage { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if (-not $Before.Exists) { return } if ([bool](Get-KTReservedStorageState $Action).Enabled -ne [bool]$Before.Enabled) { Invoke-KTReservedStorage -Enabled ([bool]$Before.Enabled) } } # ---------------- Ortak (saf) ---------------- function Test-KTOnOffState { <# .SYNOPSIS Saf. Yoksa NotApplicable; acik/kapali esitse Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if (-not $State.Exists) { return "NotApplicable" } if ([bool]$State.Enabled -eq [bool]$Action.Etkin) { return "Applied" } else { return "NotApplied" } } function Test-KTOnOffRestored { param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) if (-not $Before.Exists) { return $true } return ($Now.Exists -and [bool]$Now.Enabled -eq [bool]$Before.Enabled) } function Test-KTOnOffAction { <# .SYNOPSIS Saf. Tanim alani: Etkin (bool). #> param([Parameter(Mandatory)] $Action) if ($Action.Etkin -isnot [bool]) { return @("$($Action.Tur): Etkin `$true veya `$false olmali") } return @() } # ---------------- Trim (geri alinamaz) ---------------- # Trim: @{ Tur = 'Trim' } sistem surucusunde Optimize-Volume -ReTrim (MS Storage modulu: SSD'ye TRIM isteklerini yeniden gonderir). # Windows bunu zamanlanmis "Surucu iyilestirme" ile de yapar; bu tek seferlik. Her zaman NotApplied; geri alinamaz (zararsiz). # Sistem surucusu okunamazsa NotApplicable. SSD kosulu tanimda (Kosullar Disk = 'SSD'). function Get-KTTrimRaw { <# .SYNOPSIS Salt okunur. Sistem surucusu harfi (C) veya $null. #> $d = [string]$env:SystemDrive if ($d -cnotmatch '^[A-Za-z]:$') { return $null } return $d.Substring(0, 1) } function Invoke-KTTrimRaw { param([Parameter(Mandatory)][string]$Surucu) Optimize-Volume -DriveLetter $Surucu -ReTrim -ErrorAction Stop } function Get-KTTrimState { param([Parameter(Mandatory)] $Action) $s = Get-KTTrimRaw return [pscustomobject]@{ Exists = ($null -ne $s); Surucu = $s } } function Test-KTTrimState { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if (-not $State.Exists) { return "NotApplicable" } return "NotApplied" } function Set-KTTrim { param([Parameter(Mandatory)] $Action) $s = Get-KTTrimRaw if ($null -eq $s) { throw "Sistem surucusu okunamadi" } Invoke-KTTrimRaw -Surucu $s } function Test-KTTrimVerify { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) return [pscustomobject]@{ Ok = $true; Not = ""; YenidenBaslat = "Yok" } } function Test-KTTrimAction { param([Parameter(Mandatory)] $Action) return @() } # ============================================================== # Zamanlanmis gorev islemi (Gorev): Get / Test / Set / Undo / Restored / Check # ============================================================== # Islem: @{ Tur = 'Gorev'; Klasor = '\Microsoft\Windows\...\'; Ad = 'Consolidator'; Etkin = $false } # Durum: @{ Exists; Enabled; State } # Gorev sistemde yoksa NotApplicable: o islem atlanir (karar 43.11). # Dis komutlar ince sarmalayicilarda (Get-KTTaskRaw, Invoke-KTTaskEnable); testler bunlari degistirir. function ConvertTo-KTTaskPath { <# .SYNOPSIS Saf. Gorev klasoru yolunu '\A\B\' bicimine getirir. #> param([AllowEmptyString()][string]$TaskPath) $p = ([string]$TaskPath).Trim('\') if (-not $p) { return "\" } return "\$p\" } # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTTaskRaw { <# .SYNOPSIS Salt okunur. Gorev yoksa $null; varsa @{ State }. #> param([Parameter(Mandatory)][string]$TaskPath, [Parameter(Mandatory)][string]$TaskName) $t = Get-ScheduledTask -TaskPath $TaskPath -TaskName $TaskName -ErrorAction SilentlyContinue | Select-Object -First 1 if (-not $t) { return $null } return [pscustomobject]@{ State = $t.State.ToString() } } function Invoke-KTTaskEnable { <# .SYNOPSIS Gorevi etkinlestirir veya devre disi birakir. Basarisizsa hata firlatir. #> param([Parameter(Mandatory)][string]$TaskPath, [Parameter(Mandatory)][string]$TaskName, [Parameter(Mandatory)][bool]$Enabled) if ($Enabled) { Enable-ScheduledTask -TaskPath $TaskPath -TaskName $TaskName -ErrorAction Stop | Out-Null } else { Disable-ScheduledTask -TaskPath $TaskPath -TaskName $TaskName -ErrorAction Stop | Out-Null } } # ---------------- Get / Test / Set / Undo ---------------- function Get-KTTaskState { <# .SYNOPSIS Salt okunur. #> param([Parameter(Mandatory)] $Action) $raw = Get-KTTaskRaw -TaskPath (ConvertTo-KTTaskPath $Action.Klasor) -TaskName ([string]$Action.Ad) if (-not $raw) { return [pscustomobject]@{ Exists = $false; Enabled = $null; State = $null } } return [pscustomobject]@{ Exists = $true; Enabled = ($raw.State -ne "Disabled"); State = $raw.State } } function Test-KTTaskState { <# .SYNOPSIS Saf. Gorev yoksa NotApplicable; Etkin esitse Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if (-not $State.Exists) { return "NotApplicable" } if ([bool]$State.Enabled -eq [bool]$Action.Etkin) { return "Applied" } else { return "NotApplied" } } function Set-KTTask { param([Parameter(Mandatory)] $Action) Invoke-KTTaskEnable -TaskPath (ConvertTo-KTTaskPath $Action.Klasor) -TaskName ([string]$Action.Ad) -Enabled ([bool]$Action.Etkin) } function Undo-KTTask { <# .SYNOPSIS Onceki etkinlik durumunu kurar. Gorev o arada silindiyse hata. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if (-not $Before.Exists) { return } $cur = Get-KTTaskState $Action if (-not $cur.Exists) { throw "Gorev artik yok: $(ConvertTo-KTTaskPath $Action.Klasor)$($Action.Ad)" } if ([bool]$cur.Enabled -ne [bool]$Before.Enabled) { Invoke-KTTaskEnable -TaskPath (ConvertTo-KTTaskPath $Action.Klasor) -TaskName ([string]$Action.Ad) -Enabled ([bool]$Before.Enabled) } } function Test-KTTaskRestored { <# .SYNOPSIS Saf. Gorev vardi ve etkinlik durumu ayni ise basarili. #> param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) if (-not $Before.Exists) { return $true } return ($Now.Exists -and [bool]$Now.Enabled -eq [bool]$Before.Enabled) } function Test-KTTaskAction { <# .SYNOPSIS Saf. Tanim alanlari: Klasor, Ad, Etkin (bool). #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() if ([string]::IsNullOrEmpty([string]$Action.Klasor) -or -not ([string]$Action.Klasor).StartsWith('\')) { $err.Add("Gorev: Klasor '\' ile baslamali") } if ([string]::IsNullOrEmpty([string]$Action.Ad) -or [string]$Action.Ad -match '\\') { $err.Add("Gorev: Ad zorunlu, yol icermez") } if ($Action.Etkin -isnot [bool]) { $err.Add("Gorev: Etkin `$true veya `$false olmali") } return $err.ToArray() } # ============================================================== # Ag karti gelismis ozelligi (AgKarti): Expand / Get / Test / Set / Undo / Restored / Check # ============================================================== # Tanimdaki islem: @{ Tur = 'AgKarti'; Ozellik = '*EEE'; Deger = '0'; KartTuru = 'Ethernet' } KartTuru istege bagli # Motor, islemi sistem analizinin buldugu her fiziksel karta acar (Expand; sanal VPN kartlari analizde ayiklanir, # karar 43.8). Acilmis islem Kart = InterfaceGuid tasir (ad degisebilir, GUID degismez). # Sistem.AgKartlari: @( @{ Guid = '{...}'; Ad = 'Ethernet'; Tur = 'Ethernet' | 'Wi-Fi' } ). Kart yoksa islem atlanir. # Ozellik RegistryKeyword ile secilir (dil bagimsiz; DisplayName Turkce Windows'ta Turkce). # Kartta ozellik yoksa NotApplicable: atlanir (ornek: uretici anahtarlari EnableGreenEthernet yalniz Realtek'te). # Deger, surucunun gecerli degerler listesinde degilse yazilmaz (hata). # Yazma -NoRestart: kart yeniden baslatilmaz, baglanti kesilmez; deger kart yeniden basladiginda (en gec # bilgisayar yeniden baslatilinca) etkili olur. Bu yuzden AgKarti iceren tweak'ler YenidenBaslat = Bilgisayar. # Dis cagrilar ince sarmalayicilarda (Get-KTNetAdapterRaw, Get-KTNicPropertyRaw, Set-KTNicPropertyRaw). function ConvertTo-KTInterfaceGuid { <# .SYNOPSIS Saf. GUID'i '{BUYUK-HARF}' bicimine getirir (Get-NetAdapter ve kayit defteri bicimi). #> param([Parameter(Mandatory)][string]$Guid) return "{" + ([Guid]$Guid.Trim('{', '}', ' ')).ToString("D").ToUpperInvariant() + "}" } # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTNetAdapterRaw { <# .SYNOPSIS Salt okunur. GUID'e gore kart: @{ Name } veya $null. #> param([Parameter(Mandatory)][string]$Guid) $g = ConvertTo-KTInterfaceGuid $Guid $a = Get-NetAdapter -ErrorAction SilentlyContinue | Where-Object { [string]::Equals([string]$_.InterfaceGuid, $g, [StringComparison]::OrdinalIgnoreCase) } | Select-Object -First 1 if (-not $a) { return $null } return [pscustomobject]@{ Name = [string]$a.Name } } function Get-KTNicPropertyRaw { <# .SYNOPSIS Salt okunur. Ozellik: @{ Value (string[]); Valid (string[]) } veya $null (surucu desteklemiyor). #> param([Parameter(Mandatory)][string]$AdapterName, [Parameter(Mandatory)][string]$Keyword) $p = Get-NetAdapterAdvancedProperty -Name $AdapterName -AllProperties -RegistryKeyword $Keyword -ErrorAction SilentlyContinue | Select-Object -First 1 if (-not $p) { return $null } $valid = [string[]]@($p.ValidRegistryValues | Where-Object { "$_" -ne "" }) # Gizli ozellik (Aygit Yoneticisi'nde gorunmez, gecerli deger listesi yok; Adem'in Realtek kartinda *SelectiveSuspend, # 25.10.2026): degeri dogrulanamaz, kartta yok sayilir if (-not $p.DisplayName -and $valid.Count -eq 0) { return $null } return [pscustomobject]@{ Value = [string[]]@($p.RegistryValue); Valid = $valid } } function Set-KTNicPropertyRaw { param([Parameter(Mandatory)][string]$AdapterName, [Parameter(Mandatory)][string]$Keyword, [Parameter(Mandatory)][string[]]$Value) Set-NetAdapterAdvancedProperty -Name $AdapterName -RegistryKeyword $Keyword -RegistryValue $Value -NoRestart -ErrorAction Stop } # ---------------- Expand / Get / Test / Set / Undo ---------------- function Expand-KTNicProperty { <# .SYNOPSIS Saf. Tanimdaki islemi sistemdeki uygun her kart icin Kart alanli kopyaya acar. Kart verilmisse (yedekten gelen somut islem) oldugu gibi doner. Uygun kart yoksa bos liste. #> param([Parameter(Mandatory)] $Action, $Sistem = $null) if (Get-KTField $Action Kart) { return $Action } $out = [System.Collections.Generic.List[object]]::new() $tur = [string](Get-KTField $Action KartTuru) foreach ($k in @(Get-KTField $Sistem AgKartlari)) { if ($null -eq $k) { continue } if ($tur -and [string](Get-KTField $k Tur) -ne $tur) { continue } $c = Copy-KTAction $Action $c.Kart = ConvertTo-KTInterfaceGuid ([string](Get-KTField $k Guid)) $c.KartAdi = [string](Get-KTField $k Ad) $out.Add($c) } # Virgulsuz: cagiran @(...) ile sarar; ",dizi" + @() ic ice dizi yapar (OGRENILENLER 4) return $out.ToArray() } function Get-KTNicPropertyState { <# .SYNOPSIS Salt okunur: @{ Exists (kart ve ozellik var); Value }. #> param([Parameter(Mandatory)] $Action) $a = Get-KTNetAdapterRaw ([string]$Action.Kart) if (-not $a) { return [pscustomobject]@{ Exists = $false; Value = $null } } $p = Get-KTNicPropertyRaw -AdapterName $a.Name -Keyword ([string]$Action.Ozellik) if (-not $p) { return [pscustomobject]@{ Exists = $false; Value = $null } } return [pscustomobject]@{ Exists = $true; Value = ($p.Value -join ",") } } function Test-KTNicPropertyState { <# .SYNOPSIS Saf. Kart/ozellik yoksa NotApplicable; deger esitse Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if (-not $State.Exists) { return "NotApplicable" } if ([string]$State.Value -eq ((@($Action.Deger) | ForEach-Object { [string]$_ }) -join ",")) { return "Applied" } return "NotApplied" } function Write-KTNicProperty { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)][string[]]$Value) $a = Get-KTNetAdapterRaw ([string]$Action.Kart) if (-not $a) { throw "Ag karti bulunamadi: $($Action.Kart)" } $p = Get-KTNicPropertyRaw -AdapterName $a.Name -Keyword ([string]$Action.Ozellik) if (-not $p) { throw "Kart bu ozelligi desteklemiyor: $($Action.Ozellik)" } if ($p.Valid.Count -gt 0) { foreach ($v in $Value) { if ($p.Valid -notcontains $v) { throw "Gecersiz deger '$v' ($($Action.Ozellik)); gecerli: $($p.Valid -join ',')" } } } Set-KTNicPropertyRaw -AdapterName $a.Name -Keyword ([string]$Action.Ozellik) -Value $Value } function Set-KTNicProperty { param([Parameter(Mandatory)] $Action) Write-KTNicProperty -Action $Action -Value ([string[]]@(@($Action.Deger) | ForEach-Object { [string]$_ })) } function Undo-KTNicProperty { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if (-not $Before.Exists) { return } if ((Get-KTNicPropertyState $Action).Value -eq $Before.Value) { return } Write-KTNicProperty -Action $Action -Value ([string[]]([string]$Before.Value -split ",")) } function Test-KTNicPropertyRestored { param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) if (-not $Before.Exists) { return $true } return ($Now.Exists -and [string]$Now.Value -eq [string]$Before.Value) } function Test-KTNicPropertyAction { <# .SYNOPSIS Saf. Ozellik (RegistryKeyword) ve Deger zorunlu; KartTuru Ethernet / Wi-Fi; Kart tanimda yazilmaz. #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() if ([string]::IsNullOrEmpty([string]$Action.Ozellik) -or [string]$Action.Ozellik -match '\s') { $err.Add("AgKarti: Ozellik (RegistryKeyword) zorunlu, bosluk icermez") } if ($null -eq $Action.Deger -or [string]$Action.Deger -eq "") { $err.Add("AgKarti: Deger zorunlu") } $tur = Get-KTField $Action KartTuru if ($tur -and @("Ethernet", "Wi-Fi") -cnotcontains $tur) { $err.Add("AgKarti: KartTuru Ethernet veya Wi-Fi olmali") } if (Get-KTField $Action Kart) { $err.Add("AgKarti: Kart tanimda yazilmaz; sistem analizinden gelir") } return $err.ToArray() } # ============================================================== # Aygit guc yonetimi (CihazGuc): Expand / Get / Test / Set / Undo / Restored / Check # ============================================================== # Aygit Yoneticisi > Guc Yonetimi sekmesindeki "Guc tasarrufu icin bilgisayar bu aygiti kapatabilir" kutusu. # MS: surucu GUID_POWER_DEVICE_ENABLE WMI sinifini destekliyorsa sekme ve kutu gorunur; deger tek BOOLEAN # (root\wmi MSPower_DeviceEnable.Enable). Kutusu olmayan aygitin WMI kaydi yoktur. # Tanimdaki islem: @{ Tur = 'CihazGuc'; Cihaz = 'AgKarti' | 'HID'; Etkin = $false } # Motor islemi sistem analizinin buldugu her uygun aygita acar (Expand): # AgKarti: Sistem.AgKartlari'ndaki fiziksel kartlar (sanal/VPN haric, karar 56); HID: HIDClass aygitlari. # Acilmis islem Aygit = PnP aygit kimligi (InstanceId) ve AygitAdi tasir. # WMI kaydinin InstanceName'i aygit kimligi + "_0" bicimindedir; eslestirme sonek atilarak, buyuk/kucuk harf duyarsiz. # Bir aygitin birden fazla kaydi olabilir (_0, _1); hepsi okunur ve yazilir, geri alma her kaydi ayri kurar. # KMDF suruculu aygit (ornek Realtek rt25cx21): kutu WMI'ya degil, cerceve degerine yazilir: # HKLM\SYSTEM\CurrentControlSet\Enum\\Device Parameters\WDF\IdleInWorkingState (1 acik, 0 kapali). # MS: kullanici kutuyu acip kapatabiliyorsa cerceve bu degeri tutar (User Control of Device Idle and Wake Behavior); # Adem'in PC'sinde kutu bu degeri degistirdi, WMI kaydi degismedi (24.10.2026). Deger varsa yalniz o kullanilir # (kayit adi 'WDF'); yoksa WMI. Etki aygit yeniden basladiginda (en gec bilgisayar yeniden baslatilinca). # Dis cagrilar ince sarmalayicilarda (Get-KTDevicePowerRaw, Set-KTDevicePowerRaw); testler bunlari degistirir. function ConvertTo-KTPnpIdFromWmi { <# .SYNOPSIS Saf. WMI InstanceName'den aygit kimligi ("..._0" -> "..."). #> param([Parameter(Mandatory)][AllowEmptyString()][string]$InstanceName) return ($InstanceName -replace '_\d+$', '') } # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTDevicePowerRaw { <# .SYNOPSIS Salt okunur (yonetici ister). Aygitin tum kayitlari: @( @{ Instance; Enable (bool) } ); kutu yoksa bos. Ayni aygitin birden fazla kaydi olabilir ("..._0", "..._1"); hepsi okunur ve yazilir. #> param([Parameter(Mandatory)][string]$PnpId) $wdf = Get-KTDeviceIdleRaw -PnpId $PnpId if ($null -ne $wdf) { return [pscustomobject]@{ Instance = "WDF"; Enable = ([int64]$wdf -ne 0) } } foreach ($i in @(Get-CimInstance -Namespace root\wmi -ClassName MSPower_DeviceEnable -ErrorAction Stop)) { if ([string]::Equals((ConvertTo-KTPnpIdFromWmi ([string]$i.InstanceName)), $PnpId, [StringComparison]::OrdinalIgnoreCase)) { [pscustomobject]@{ Instance = [string]$i.InstanceName; Enable = [bool]$i.Enable } } } } function Set-KTDevicePowerRaw { <# .SYNOPSIS Instance verilirse yalniz o kayit, verilmezse aygitin tum kayitlari yazilir. #> param([Parameter(Mandatory)][string]$PnpId, [Parameter(Mandatory)][bool]$Enable, [string]$Instance) if ($Instance -eq "WDF" -or (-not $Instance -and $null -ne (Get-KTDeviceIdleRaw -PnpId $PnpId))) { Set-ItemProperty -LiteralPath (Get-KTDeviceIdlePath $PnpId) -Name IdleInWorkingState -Value ([int]$Enable) -Type DWord -ErrorAction Stop return } $hit = 0 foreach ($i in @(Get-CimInstance -Namespace root\wmi -ClassName MSPower_DeviceEnable -ErrorAction Stop)) { $n = [string]$i.InstanceName if ($Instance) { if (-not [string]::Equals($n, $Instance, [StringComparison]::OrdinalIgnoreCase)) { continue } } elseif (-not [string]::Equals((ConvertTo-KTPnpIdFromWmi $n), $PnpId, [StringComparison]::OrdinalIgnoreCase)) { continue } Set-CimInstance -InputObject $i -Property @{ Enable = $Enable } -ErrorAction Stop $hit++ } if ($hit -eq 0) { throw "Aygitin guc yonetimi kaydi yok: $PnpId $Instance" } } function Get-KTDeviceIdlePath { param([Parameter(Mandatory)][string]$PnpId) "HKLM:\SYSTEM\CurrentControlSet\Enum\$PnpId\Device Parameters\WDF" } function Get-KTDeviceIdleRaw { <# .SYNOPSIS Salt okunur. KMDF cerceve degeri IdleInWorkingState veya $null (deger yok: WMI kullanilir). #> param([Parameter(Mandatory)][string]$PnpId) $p = Get-ItemProperty -LiteralPath (Get-KTDeviceIdlePath $PnpId) -Name IdleInWorkingState -ErrorAction SilentlyContinue if ($p) { return $p.IdleInWorkingState } else { return $null } } function ConvertTo-KTDevicePowerList { <# .SYNOPSIS Saf. Kayit listesi -> "kayit=1;kayit=0" (ad sirasiyla; yedekte saklanir). #> param($Rows) return ((@($Rows) | Where-Object { $_ } | Sort-Object Instance | ForEach-Object { "$($_.Instance)=$([int][bool]$_.Enable)" }) -join ";") } # ---------------- Expand / Get / Test / Set / Undo ---------------- function Expand-KTDevicePower { <# .SYNOPSIS Saf. Tanimdaki islemi uygun her aygit icin Aygit alanli kopyaya acar (AgKarti: Sistem.AgKartlari; HID: Sistem.GucCihazlari). Aygit verilmisse (yedekten gelen somut islem) oldugu gibi doner. Uygun aygit yoksa bos liste. #> param([Parameter(Mandatory)] $Action, $Sistem = $null) if (Get-KTField $Action Aygit) { return $Action } $out = [System.Collections.Generic.List[object]]::new() $cihaz = [string](Get-KTField $Action Cihaz) # AgKarti: her fiziksel kart (WMI kaydi olmayan KMDF kart da); HID: kutusu olan HIDClass aygitlari $liste = if ($cihaz -eq "AgKarti") { @(Get-KTField $Sistem AgKartlari) } else { @(@(Get-KTField $Sistem GucCihazlari) | Where-Object { $_ -and [string](Get-KTField $_ Sinif) -eq "HIDClass" }) } foreach ($d in $liste) { if ($null -eq $d) { continue } $id = [string](Get-KTField $d PnpId) if (-not $id) { continue } $c = Copy-KTAction $Action $c.Aygit = $id $c.AygitAdi = [string](Get-KTField $d Ad) $out.Add($c) } # Virgulsuz: cagiran @(...) ile sarar (OGRENILENLER 4) return $out.ToArray() } function Get-KTDevicePowerState { <# .SYNOPSIS Salt okunur: @{ Exists (aygit ve kutu var); Enabled (herhangi bir kayit acik); Liste ("kayit=1;kayit=0") }. #> param([Parameter(Mandatory)] $Action) $rows = @(Get-KTDevicePowerRaw -PnpId ([string]$Action.Aygit)) if ($rows.Count -eq 0) { return [pscustomobject]@{ Exists = $false; Enabled = $null; Liste = "" } } return [pscustomobject]@{ Exists = $true; Enabled = [bool](@($rows | Where-Object { $_.Enable }).Count); Liste = (ConvertTo-KTDevicePowerList $rows) } } function Test-KTDevicePowerState { <# .SYNOPSIS Saf. Kayit yoksa NotApplicable; tum kayitlar hedefteyse Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if (-not $State.Exists) { return "NotApplicable" } $hedef = [int][bool]$Action.Etkin foreach ($p in @(([string]$State.Liste) -split ';' | Where-Object { $_ })) { if ([int]($p -split '=')[-1] -ne $hedef) { return "NotApplied" } } return "Applied" } function Set-KTDevicePower { param([Parameter(Mandatory)] $Action) Set-KTDevicePowerRaw -PnpId ([string]$Action.Aygit) -Enable ([bool]$Action.Etkin) } function Undo-KTDevicePower { <# .SYNOPSIS Her kaydin onceki degerini kurar. Aygit o arada kalktiysa hata. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if (-not $Before.Exists) { return } $cur = Get-KTDevicePowerState $Action if (-not $cur.Exists) { throw "Aygit artik yok: $($Action.Aygit)" } if ([string]$cur.Liste -eq [string]$Before.Liste) { return } foreach ($p in @(([string]$Before.Liste) -split ';' | Where-Object { $_ })) { $i = $p.LastIndexOf('=') Set-KTDevicePowerRaw -PnpId ([string]$Action.Aygit) -Instance $p.Substring(0, $i) -Enable ([bool][int]$p.Substring($i + 1)) } } function Test-KTDevicePowerRestored { param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) if (-not $Before.Exists) { return $true } return ($Now.Exists -and [string]$Now.Liste -eq [string]$Before.Liste) } function Test-KTDevicePowerAction { <# .SYNOPSIS Saf. Cihaz AgKarti / HID; Etkin bool zorunlu; Aygit tanimda yazilmaz. #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() if (@("AgKarti", "HID") -cnotcontains [string](Get-KTField $Action Cihaz)) { $err.Add("CihazGuc: Cihaz AgKarti veya HID olmali") } if ((Get-KTField $Action Etkin) -isnot [bool]) { $err.Add("CihazGuc: Etkin `$true veya `$false olmali") } if (Get-KTField $Action Aygit) { $err.Add("CihazGuc: Aygit tanimda yazilmaz; sistem analizinden gelir") } return $err.ToArray() } # ============================================================== # Ag karti DNS sunuculari (Dns): Get / Test / Set / Undo / Restored / Check (Expand: AgKarti ile ortak) # ============================================================== # Tanimdaki islem: @{ Tur = 'Dns'; Sunucular = @('1.1.1.1', '1.0.0.1', '2606:4700:4700::1111') } KartTuru istege bagli # Motor islemi her fiziksel karta acar (Expand-KTNicProperty; Kart = InterfaceGuid, KartAdi). # Yazma MS Set-DnsClientServerAddress ile: statik sunucular DHCP'nin verdigini gecersiz kilar. IPv4 ve IPv6 adresleri # ayni listede verilir. Geri alma: once statik yoksa -ResetServerAddresses (DHCP'ye doner), varsa onceki statik liste. # Statik liste yazilirken once sifirlama yalniz eski ailesi listede yoksa yapilir (Set-KTDnsRaw). # Okuma: kartin statik sunuculari kayitta (Tcpip / Tcpip6 Parameters\Interfaces\{GUID} NameServer; DHCP'de bos). # Get-DnsClientServerAddress etkin sunuculari verir, statik mi DHCP mi ayirmaz; bu yuzden kayit okunur. # Dis cagrilar ince sarmalayicilarda (Get-KTDnsStaticRaw, Set-KTDnsRaw); testler bunlari degistirir. function Split-KTDnsList { <# .SYNOPSIS Saf. Kayittaki NameServer dizesi (virgul veya bosluk ayracli) -> adres listesi. #> param([AllowNull()][AllowEmptyString()][string]$Text) if (-not $Text) { return @() } return @($Text -split '[,\s]+' | Where-Object { $_ }) } function Get-KTDnsFamilies { <# .SYNOPSIS Saf. Adres listesi -> @{ V4 = 'a,b'; V6 = 'c,d' } (sira korunur; IPv6 ':' icerir). #> param([string[]]$Addresses) $v4 = @($Addresses | Where-Object { $_ -and -not $_.Contains(":") }) $v6 = @($Addresses | Where-Object { $_ -and $_.Contains(":") }) return [pscustomobject]@{ V4 = ($v4 -join ","); V6 = (($v6 | ForEach-Object { $_.ToLowerInvariant() }) -join ",") } } # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTDnsStaticRaw { <# .SYNOPSIS Salt okunur. Kartin statik DNS'i: @{ V4 (string[]); V6 (string[]) } veya $null (kart yok). #> param([Parameter(Mandatory)][string]$Guid) $g = ConvertTo-KTInterfaceGuid $Guid if (-not (Get-KTNetAdapterRaw $g)) { return $null } $oku = { param($yol) $p = Get-ItemProperty -LiteralPath $yol -Name NameServer -ErrorAction SilentlyContinue; if ($p) { [string]$p.NameServer } else { "" } } return [pscustomobject]@{ V4 = [string[]]@(Split-KTDnsList (& $oku "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\$g")) V6 = [string[]]@(Split-KTDnsList (& $oku "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\Interfaces\$g")) } } function Set-KTDnsRaw { <# .SYNOPSIS Adres listesi bossa DHCP'ye doner (-ResetServerAddresses), doluysa statik yazar. Sifirlama yalniz gerektiginde: liste bos veya listede olmayan bir ailenin (IPv4/IPv6) statik sunucusu kalmissa (-ServerAddresses yalniz verilen aileleri yazar). Boylece yazma arada DHCP DNS'iyle kalmaz. #> param([Parameter(Mandatory)][string]$Guid, [string[]]$Addresses) $g = ConvertTo-KTInterfaceGuid $Guid $a = Get-NetAdapter -ErrorAction Stop | Where-Object { [string]::Equals([string]$_.InterfaceGuid, $g, [StringComparison]::OrdinalIgnoreCase) } | Select-Object -First 1 if (-not $a) { throw "Ag karti bulunamadi: $g" } $f = Get-KTDnsFamilies -Addresses $Addresses $cur = Get-KTDnsStaticRaw $g $kalan = $cur -and ((-not $f.V4 -and @($cur.V4).Count -gt 0) -or (-not $f.V6 -and @($cur.V6).Count -gt 0)) if (@($Addresses).Count -eq 0 -or $kalan) { Set-DnsClientServerAddress -InterfaceIndex $a.ifIndex -ResetServerAddresses -ErrorAction Stop } if (@($Addresses).Count -gt 0) { Set-DnsClientServerAddress -InterfaceIndex $a.ifIndex -ServerAddresses $Addresses -ErrorAction Stop } } # ---------------- Get / Test / Set / Undo ---------------- function Get-KTDnsState { <# .SYNOPSIS Salt okunur: @{ Exists (kart var); V4; V6 } (virgullu dize; DHCP'de bos). #> param([Parameter(Mandatory)] $Action) $raw = Get-KTDnsStaticRaw ([string]$Action.Kart) if (-not $raw) { return [pscustomobject]@{ Exists = $false; V4 = ""; V6 = "" } } $f = Get-KTDnsFamilies -Addresses (@($raw.V4) + @($raw.V6)) return [pscustomobject]@{ Exists = $true; V4 = $f.V4; V6 = $f.V6 } } function Test-KTDnsState { <# .SYNOPSIS Saf. Kart yoksa NotApplicable; statik IPv4 ve IPv6 listeleri hedefle ayniysa Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if (-not $State.Exists) { return "NotApplicable" } $t = Get-KTDnsFamilies -Addresses ([string[]]@($Action.Sunucular)) if ([string]$State.V4 -eq $t.V4 -and [string]$State.V6 -eq $t.V6) { return "Applied" } return "NotApplied" } function Set-KTDns { param([Parameter(Mandatory)] $Action) Set-KTDnsRaw -Guid ([string]$Action.Kart) -Addresses ([string[]]@($Action.Sunucular)) } function Undo-KTDns { <# .SYNOPSIS Onceki statik listeyi kurar; once statik yoksa DHCP'ye doner. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if (-not $Before.Exists) { return } $cur = Get-KTDnsState $Action if ($cur.Exists -and [string]$cur.V4 -eq [string]$Before.V4 -and [string]$cur.V6 -eq [string]$Before.V6) { return } $eski = [string[]]@(@(Split-KTDnsList ([string]$Before.V4)) + @(Split-KTDnsList ([string]$Before.V6))) Set-KTDnsRaw -Guid ([string]$Action.Kart) -Addresses $eski } function Test-KTDnsRestored { param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) if (-not $Before.Exists) { return $true } return ($Now.Exists -and [string]$Now.V4 -eq [string]$Before.V4 -and [string]$Now.V6 -eq [string]$Before.V6) } function Test-KTDnsAction { <# .SYNOPSIS Saf. Sunucular en az bir gecerli IP adresi; KartTuru Ethernet / Wi-Fi; Kart tanimda yazilmaz. #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() $list = @(Get-KTField $Action Sunucular) if ($list.Count -eq 0) { $err.Add("Dns: Sunucular zorunlu") } foreach ($s in $list) { $ip = $null; if (-not [System.Net.IPAddress]::TryParse([string]$s, [ref]$ip)) { $err.Add("Dns: gecersiz adres '$s'") } } $tur = Get-KTField $Action KartTuru if ($tur -and @("Ethernet", "Wi-Fi") -cnotcontains $tur) { $err.Add("Dns: KartTuru Ethernet veya Wi-Fi olmali") } if (Get-KTField $Action Kart) { $err.Add("Dns: Kart tanimda yazilmaz; sistem analizinden gelir") } return $err.ToArray() } # ============================================================== # Windows geneli ag bosaltma ayari (AgGenel): Get / Test / Set / Undo / Restored / Check # ============================================================== # Tanimdaki islem: @{ Tur = 'AgGenel'; Ozellik = 'ReceiveSideScaling'; Deger = 'Enabled' } # Ozellik Get-NetOffloadGlobalSetting ciktisindaki ozellik adi, Deger o ozelligin metin degeri (Enabled / Disabled). # Kart ozelligi (AgKarti) kart bazinda; bu ayar tum kartlar icin Windows genelinde (ornek: genel RSS kapaliysa kart # ayari etkisiz). Yazma Set-NetOffloadGlobalSetting - . # Durum: @{ Exists; Value }. Ozellik ciktida yoksa NotApplicable. # Dis cagrilar ince sarmalayicilarda (Get-KTNetGlobalRaw, Set-KTNetGlobalRaw); testler bunlari degistirir. # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTNetGlobalRaw { <# .SYNOPSIS Salt okunur. Ozelligin metin degeri veya $null (ozellik yok). #> param([Parameter(Mandatory)][string]$Name) $g = Get-NetOffloadGlobalSetting -ErrorAction Stop $p = $g.PSObject.Properties[$Name] if ($null -eq $p -or $null -eq $p.Value) { return $null } return [string]$p.Value } function Set-KTNetGlobalRaw { param([Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][string]$Value) $prm = @{ $Name = $Value; ErrorAction = "Stop" } Set-NetOffloadGlobalSetting @prm } # ---------------- Get / Test / Set / Undo ---------------- function Get-KTNetGlobalState { param([Parameter(Mandatory)] $Action) $v = Get-KTNetGlobalRaw ([string]$Action.Ozellik) if ($null -eq $v) { return [pscustomobject]@{ Exists = $false; Value = $null } } return [pscustomobject]@{ Exists = $true; Value = $v } } function Test-KTNetGlobalState { <# .SYNOPSIS Saf. Ozellik yoksa NotApplicable; deger esitse (buyuk/kucuk harf duyarsiz) Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if (-not $State.Exists) { return "NotApplicable" } if ([string]::Equals([string]$State.Value, [string]$Action.Deger, [StringComparison]::OrdinalIgnoreCase)) { return "Applied" } return "NotApplied" } function Set-KTNetGlobal { param([Parameter(Mandatory)] $Action) Set-KTNetGlobalRaw -Name ([string]$Action.Ozellik) -Value ([string]$Action.Deger) } function Undo-KTNetGlobal { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if (-not $Before.Exists) { return } if ([string]::Equals([string](Get-KTNetGlobalState $Action).Value, [string]$Before.Value, [StringComparison]::OrdinalIgnoreCase)) { return } Set-KTNetGlobalRaw -Name ([string]$Action.Ozellik) -Value ([string]$Before.Value) } function Test-KTNetGlobalRestored { param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) if (-not $Before.Exists) { return $true } return ($Now.Exists -and [string]::Equals([string]$Now.Value, [string]$Before.Value, [StringComparison]::OrdinalIgnoreCase)) } function Test-KTNetGlobalAction { <# .SYNOPSIS Saf. Ozellik (ozellik adi, harf ve rakam) ve Deger zorunlu. #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() if ([string]$Action.Ozellik -cnotmatch '^[A-Za-z][A-Za-z0-9]*$') { $err.Add("AgGenel: Ozellik zorunlu (Get-NetOffloadGlobalSetting ozellik adi)") } if ([string]::IsNullOrEmpty([string]$Action.Deger)) { $err.Add("AgGenel: Deger zorunlu") } return $err.ToArray() } # ============================================================== # HDR ac/kapat (Hdr): Expand / Get / Test / Set / Undo / Restored / Check (karar 90) # ============================================================== # Ayarlar > Ekran > HDR anahtari. Windows SDK wingdi.h (10.0.26100): DisplayConfigGetDeviceInfo tip 15 # GET_ADVANCED_COLOR_INFO_2 (36 bayt; bitler 20. baytta: 4 highDynamicRangeSupported, 5 highDynamicRangeUserEnabled), # DisplayConfigSetDeviceInfo tip 16 SET_HDR_STATE (24 bayt: 20 bayt baslik + enableHdr biti). # Tanimdaki islem: @{ Tur = 'Hdr'; Etkin = $true }. Motor islemi HDR destekli her etkin ekrana acar (Expand: # Sistem.Ekranlar HdrDestek); acilmis islem HedefId ve EkranAdi tasir. Ekran o an bagli degilse Exists = $false. # Dis cagrilar ince sarmalayicilarda (Get-KTHdrRaw, Set-KTHdrRaw); testler bunlari degistirir. $script:KT_HdrKaynak = @' using System; using System.Runtime.InteropServices; public static class KTHdr { [DllImport("user32.dll")] static extern int GetDisplayConfigBufferSizes(uint flags, out uint numPaths, out uint numModes); [DllImport("user32.dll")] static extern int QueryDisplayConfig(uint flags, ref uint numPaths, byte[] paths, ref uint numModes, byte[] modes, IntPtr topology); [DllImport("user32.dll")] static extern int DisplayConfigGetDeviceInfo(byte[] packet); [DllImport("user32.dll")] static extern int DisplayConfigSetDeviceInfo(byte[] packet); // Etkin yollarda hedefin adaptor LUID'i (DISPLAYCONFIG_PATH_INFO 72 bayt: targetInfo.adapterId 20, id 28); yoksa null. // Hedef kimligi ekran kartina gore tektir (LUID + id); ayni id iki farkli kartta varsa belirsiz = true (yanlis ekrana // yazilmasin). LUID yeniden baslatmada degisebildigi icin yedekte yalniz id tutulur. static byte[] Luid(uint target, out bool belirsiz) { belirsiz = false; byte[] bulunan = null; uint np, nm; if (GetDisplayConfigBufferSizes(2, out np, out nm) != 0) return null; var p = new byte[np * 72]; var m = new byte[nm * 64]; if (QueryDisplayConfig(2, ref np, p, ref nm, m, IntPtr.Zero) != 0) return null; for (int i = 0; i < np; i++) { if (BitConverter.ToUInt32(p, i * 72 + 28) != target) continue; var l = new byte[8]; Array.Copy(p, i * 72 + 20, l, 0, 8); if (bulunan == null) { bulunan = l; continue; } if (BitConverter.ToInt64(bulunan, 0) != BitConverter.ToInt64(l, 0)) { belirsiz = true; return null; } } return bulunan; } static byte[] Paket(int type, int size, byte[] luid, uint id) { var b = new byte[size]; BitConverter.GetBytes(type).CopyTo(b, 0); BitConverter.GetBytes(size).CopyTo(b, 4); luid.CopyTo(b, 8); BitConverter.GetBytes(id).CopyTo(b, 16); return b; } // -1: ekran yok; -2: okunamadi; -3: ayni hedef id iki ekran kartinda; yoksa tip 15 bitleri public static long Get(uint target) { bool belirsiz; var l = Luid(target, out belirsiz); if (belirsiz) return -3; if (l == null) return -1; var b = Paket(15, 36, l, target); if (DisplayConfigGetDeviceInfo(b) != 0) return -2; return BitConverter.ToUInt32(b, 20); } public static int Set(uint target, bool on) { bool belirsiz; var l = Luid(target, out belirsiz); if (belirsiz) return -3; if (l == null) return -1; var b = Paket(16, 24, l, target); BitConverter.GetBytes(on ? 1u : 0u).CopyTo(b, 20); return DisplayConfigSetDeviceInfo(b); } } '@ function Initialize-KTHdr { if (-not ("KTHdr" -as [type])) { Add-Type -TypeDefinition $script:KT_HdrKaynak -ErrorAction Stop } } # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTHdrRaw { <# .SYNOPSIS Salt okunur. Hedefin HDR durumu: @{ Bagli; Destek; Acik } ($null: okunamadi). #> param([Parameter(Mandatory)][uint32]$HedefId) Initialize-KTHdr $v = [KTHdr]::Get($HedefId) if ($v -eq -1) { return [pscustomobject]@{ Bagli = $false; Destek = $false; Acik = $false } } if ($v -eq -3) { throw "HDR hedefi belirsiz (hedef $HedefId iki ekran kartinda)" } if ($v -lt 0) { throw "HDR durumu okunamadi (hedef $HedefId)" } return [pscustomobject]@{ Bagli = $true; Destek = [bool]($v -band 0x10); Acik = [bool]($v -band 0x20) } } function Set-KTHdrRaw { param([Parameter(Mandatory)][uint32]$HedefId, [Parameter(Mandatory)][bool]$Acik) Initialize-KTHdr $rc = [KTHdr]::Set($HedefId, $Acik) if ($rc -ne 0) { throw "HDR ayarlanamadi (hedef $HedefId, kod $rc)" } } # ---------------- Expand / Get / Test / Set / Undo ---------------- function Expand-KTHdr { <# .SYNOPSIS Saf. Islemi HDR destekli her ekran icin HedefId alanli kopyaya acar; HedefId verilmisse oldugu gibi doner. #> param([Parameter(Mandatory)] $Action, $Sistem = $null) if ($null -ne (Get-KTField $Action HedefId)) { return $Action } $out = [System.Collections.Generic.List[object]]::new() foreach ($e in @(Get-KTField $Sistem Ekranlar)) { if ($null -eq $e -or -not (Get-KTField $e HdrDestek)) { continue } $c = Copy-KTAction $Action $c.HedefId = [uint32](Get-KTField $e HedefId) $c.EkranAdi = [string](Get-KTField $e Ad) $out.Add($c) } return $out.ToArray() } function Get-KTHdrState { <# .SYNOPSIS Salt okunur: @{ Exists (ekran bagli ve HDR destekli); Enabled }. #> param([Parameter(Mandatory)] $Action) $r = Get-KTHdrRaw -HedefId ([uint32]$Action.HedefId) return [pscustomobject]@{ Exists = ($r.Bagli -and $r.Destek); Enabled = [bool]$r.Acik } } function Set-KTHdr { param([Parameter(Mandatory)] $Action) Set-KTHdrRaw -HedefId ([uint32]$Action.HedefId) -Acik ([bool]$Action.Etkin) } function Undo-KTHdr { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if (-not $Before.Exists) { return } $cur = Get-KTHdrState $Action if (-not $cur.Exists) { throw "Ekran artik bagli degil: $($Action.EkranAdi)" } if ([bool]$cur.Enabled -ne [bool]$Before.Enabled) { Set-KTHdrRaw -HedefId ([uint32]$Action.HedefId) -Acik ([bool]$Before.Enabled) } } function Test-KTHdrAction { <# .SYNOPSIS Saf. Etkin bool zorunlu; HedefId tanimda yazilmaz (sistem analizinden gelir). #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() if ((Get-KTField $Action Etkin) -isnot [bool]) { $err.Add("Hdr: Etkin `$true veya `$false olmali") } if ($null -ne (Get-KTField $Action HedefId)) { $err.Add("Hdr: HedefId tanimda yazilmaz; sistem analizinden gelir") } return $err.ToArray() } # ============================================================== # Ag bagdastiricisi baglamasi (AgBaglama): Expand / Get / Test / Set / Undo / Restored / Check # ============================================================== # Kart ozelliklerindeki "Bu baglanti asagidaki ogeleri kullanir" listesindeki bilesen (protokol/istemci/hizmet). # Tanimdaki islem: @{ Tur = 'AgBaglama'; Bilesen = 'ms_lldp'; Etkin = $false; KartTuru = 'Ethernet' } KartTuru istege bagli # Bilesen ComponentID ile secilir (dil bagimsiz; DisplayName Turkce Windows'ta Turkce). # Motor islemi sistem analizinin buldugu her fiziksel karta acar (Expand-KTNicProperty; sanal/VPN kartlari analizde # ayiklanir, karar 43.8). Acilmis islem Kart = InterfaceGuid ve KartAdi tasir. # Kartta bilesen yoksa NotApplicable: atlanir. # Yazma Enable-/Disable-NetAdapterBinding; kart yeniden baslatilmaz, etki hemen. # Durum: @{ Exists; Enabled }. Geri alma onceki Enabled'i kurar. # Dis cagrilar ince sarmalayicilarda (Get-KTNetBindingRaw, Set-KTNetBindingRaw); testler bunlari degistirir. # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTNetBindingRaw { <# .SYNOPSIS Salt okunur. Bilesenin bu kartta etkin olup olmadigi ($true/$false) veya $null (kart ya da bilesen yok). #> param([Parameter(Mandatory)][string]$AdapterName, [Parameter(Mandatory)][string]$ComponentId) # Bilesen listesi kart o an yeniden yapilanirken bos donebilir (CI'da goruldu, 02.11.2026): kart hic baglama # dondurmuyorsa bir kez kisa bekleyip yeniden okunur; bilesen gercekten yoksa yine $null. for ($i = 0; $i -lt 2; $i++) { $b = Get-NetAdapterBinding -Name $AdapterName -ComponentID $ComponentId -ErrorAction SilentlyContinue | Select-Object -First 1 if ($b) { break } if ($i -eq 0 -and @(Get-NetAdapterBinding -Name $AdapterName -AllBindings -ErrorAction SilentlyContinue).Count -gt 0) { break } if ($i -eq 0) { Start-Sleep -Milliseconds 1500 } } if (-not $b) { return $null } return [bool]$b.Enabled } function Set-KTNetBindingRaw { param([Parameter(Mandatory)][string]$AdapterName, [Parameter(Mandatory)][string]$ComponentId, [Parameter(Mandatory)][bool]$Enabled) if ($Enabled) { Enable-NetAdapterBinding -Name $AdapterName -ComponentID $ComponentId -ErrorAction Stop } else { Disable-NetAdapterBinding -Name $AdapterName -ComponentID $ComponentId -ErrorAction Stop } } # ---------------- Get / Test / Set / Undo ---------------- function Get-KTNetBindingState { <# .SYNOPSIS Salt okunur: @{ Exists (kart ve bilesen var); Enabled }. #> param([Parameter(Mandatory)] $Action) $a = Get-KTNetAdapterRaw ([string]$Action.Kart) if (-not $a) { return [pscustomobject]@{ Exists = $false; Enabled = $null } } $e = Get-KTNetBindingRaw -AdapterName $a.Name -ComponentId ([string]$Action.Bilesen) if ($null -eq $e) { return [pscustomobject]@{ Exists = $false; Enabled = $null } } return [pscustomobject]@{ Exists = $true; Enabled = [bool]$e } } function Test-KTNetBindingState { <# .SYNOPSIS Saf. Kart/bilesen yoksa NotApplicable; Enabled hedefe esitse Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if (-not $State.Exists) { return "NotApplicable" } if ([bool]$State.Enabled -eq [bool]$Action.Etkin) { return "Applied" } return "NotApplied" } function Write-KTNetBinding { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)][bool]$Enabled) $a = Get-KTNetAdapterRaw ([string]$Action.Kart) if (-not $a) { throw "Ag karti bulunamadi: $($Action.Kart)" } Set-KTNetBindingRaw -AdapterName $a.Name -ComponentId ([string]$Action.Bilesen) -Enabled $Enabled } function Set-KTNetBinding { param([Parameter(Mandatory)] $Action) Write-KTNetBinding -Action $Action -Enabled ([bool]$Action.Etkin) } function Undo-KTNetBinding { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if (-not $Before.Exists) { return } $cur = Get-KTNetBindingState $Action if ($cur.Exists -and [bool]$cur.Enabled -eq [bool]$Before.Enabled) { return } Write-KTNetBinding -Action $Action -Enabled ([bool]$Before.Enabled) } function Test-KTNetBindingRestored { param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) if (-not $Before.Exists) { return $true } return ($Now.Exists -and [bool]$Now.Enabled -eq [bool]$Before.Enabled) } function Test-KTNetBindingAction { <# .SYNOPSIS Saf. Bilesen (ComponentID, ornek ms_lldp) ve Etkin bool zorunlu; KartTuru Ethernet / Wi-Fi; Kart tanimda yazilmaz. #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() if ([string](Get-KTField $Action Bilesen) -cnotmatch '^[A-Za-z][A-Za-z0-9_]*$') { $err.Add("AgBaglama: Bilesen zorunlu (ComponentID, ornek ms_lldp)") } if ((Get-KTField $Action Etkin) -isnot [bool]) { $err.Add("AgBaglama: Etkin `$true veya `$false olmali") } $tur = Get-KTField $Action KartTuru if ($tur -and @("Ethernet", "Wi-Fi") -cnotcontains $tur) { $err.Add("AgBaglama: KartTuru Ethernet veya Wi-Fi olmali") } if (Get-KTField $Action Kart) { $err.Add("AgBaglama: Kart tanimda yazilmaz; sistem analizinden gelir") } return $err.ToArray() } # ============================================================== # Windows Time zaman kaynagi (ZamanSunucusu): Get / Test / Set / Undo / Restored / Check / Dogrula / Atla # ============================================================== # Tanimdaki islem: @{ Tur = 'ZamanSunucusu'; Sunucular = @('0.pool.ntp.org', '1.pool.ntp.org') } # Yapilan (bagimsiz PC): w32time servisi devre disiysa El ile yapilir ve baslatilir, sonra # w32tm /config /manualpeerlist:",0x8 ..." /syncfromflags:manual /update (0x8 = istemci modu) # Dogrulama: ayar okunur ve w32tm /resync basarili olmali; esitleme hatasi basari sayilmaz (motor geri alir). # Etki alanina bagli PC'de dokunulmaz (saat etki alani hiyerarsisinden gelir): islem atlanir, nedeni ekrana yazilir. # Durum: @{ Exists (servis var); Domain; NtpServer; Type; StartType; Status } # NtpServer ve Type HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters altindan okunur (yalniz okuma; # yazma w32tm ile). Type: NTP (elle liste), NT5DS (etki alani), AllSync, NoSync. # Geri alma: onceki liste ve kaynak turu w32tm ile, onceki baslangic turu sc.exe ile; servis onceden calismiyorsa durdurulur. # Dis cagrilar ince sarmalayicilarda (Get-KTTimeRaw, Invoke-KTW32tmConfig, Invoke-KTW32tmResync; servis icin 22_Servis); # testler bunlari degistirir. $script:KT_W32TimeParams = "HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" # Type degeri -> w32tm /syncfromflags (MS Learn: Windows Time service tools and settings) $script:KT_W32TypeFlags = @{ NTP = "MANUAL"; NT5DS = "DOMHIER"; AllSync = "ALL"; NoSync = "NO" } # ---------------- Saf ---------------- function ConvertTo-KTPeerList { <# .SYNOPSIS Saf. Sunucu adlari -> manualpeerlist metni ("a,0x8 b,0x8"). #> param([Parameter(Mandatory)] $Sunucular) return ((@($Sunucular) | ForEach-Object { "$_,0x8" }) -join " ") } function Get-KTPeerNames { <# .SYNOPSIS Saf. NtpServer metninden sunucu adlari (bayraklar atilir, kucuk harf, sirali). #> param([AllowEmptyString()][string]$NtpServer) return [string[]]@(@($NtpServer -split '\s+' | Where-Object { $_ } | ForEach-Object { ($_ -split ',')[0].ToLowerInvariant() }) | Sort-Object -Unique) } # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTTimeRaw { <# .SYNOPSIS Salt okunur. @{ Domain (bool); NtpServer; Type }. #> $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop $p = Get-ItemProperty -LiteralPath $script:KT_W32TimeParams -ErrorAction SilentlyContinue return [pscustomobject]@{ Domain = [bool]$cs.PartOfDomain; NtpServer = [string]$p.NtpServer; Type = [string]$p.Type } } function Invoke-KTW32tmConfig { <# .SYNOPSIS w32tm /config ... /update. Basarisizsa hata firlatir. #> param([Parameter(Mandatory)][AllowEmptyString()][string]$PeerList, [Parameter(Mandatory)][string]$SyncFrom) $args0 = @("/config", "/syncfromflags:$SyncFrom", "/update") if ($PeerList) { $args0 = @("/config", "/manualpeerlist:$PeerList", "/syncfromflags:$SyncFrom", "/update") } $out = & w32tm.exe @args0 2>&1 if ($LASTEXITCODE -ne 0) { throw "w32tm $($args0 -join ' ') basarisiz ($LASTEXITCODE): $(($out | Out-String).Trim())" } } function Invoke-KTW32tmResync { <# .SYNOPSIS w32tm /resync /force; ayar yeni yazildiysa ilk deneme 'zaman verisi yok' donebilir: 3 deneme. Basariliysa $true. #> for ($i = 0; $i -lt 3; $i++) { if ($i -gt 0) { Start-Sleep -Seconds 3 } $null = & w32tm.exe /resync /force 2>&1 if ($LASTEXITCODE -eq 0) { return $true } } return $false } # ---------------- Get / Test / Set / Undo ---------------- function Get-KTTimeState { <# .SYNOPSIS Salt okunur. #> param([Parameter(Mandatory)] $Action) $svc = Get-KTServiceRaw "W32Time" if (-not $svc) { return [pscustomobject]@{ Exists = $false; Domain = $false; NtpServer = ""; Type = ""; StartType = ""; Status = "" } } $t = Get-KTTimeRaw return [pscustomobject]@{ Exists = $true; Domain = [bool]$t.Domain; NtpServer = [string]$t.NtpServer; Type = [string]$t.Type StartType = (ConvertTo-KTServiceStartType $svc.Start $svc.DelayedAutostart); Status = [string]$svc.Status } } function Test-KTTimeState { <# .SYNOPSIS Saf. Servis yoksa NotApplicable; Type NTP, sunucu adlari ayni ve servis devre disi degilse Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if (-not $State.Exists) { return "NotApplicable" } $hedef = (Get-KTPeerNames (ConvertTo-KTPeerList $Action.Sunucular)) -join " " if ([string]$State.Type -eq "NTP" -and (Get-KTPeerNames ([string]$State.NtpServer)) -join " " -eq $hedef -and [string]$State.StartType -ne "Disabled") { return "Applied" } return "NotApplied" } function Get-KTTimeSkip { <# .SYNOPSIS Saf. Etki alanina bagli PC'de islem atlanir. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if ($State.Domain) { return "(Etki alanina bagli: saat etki alanindan alinir, dokunulmadi)" } return "" } function Start-KTTimeService { <# .SYNOPSIS Servis devre disiysa El ile yapar ve calismiyorsa baslatir (w32tm /config /update calisan servis ister). #> $svc = Get-KTServiceRaw "W32Time" if (-not $svc) { throw "W32Time servisi yok" } if ((ConvertTo-KTServiceStartType $svc.Start $svc.DelayedAutostart) -eq "Disabled") { Invoke-KTScConfig -Name "W32Time" -StartType "Manual" } if ([string]$svc.Status -ne "Running") { Invoke-KTServiceControl -Name "W32Time" -Command "Start" if ([string](Get-KTServiceRaw "W32Time").Status -ne "Running") { throw "W32Time servisi baslatilamadi" } } } function Set-KTTime { param([Parameter(Mandatory)] $Action) $st = Get-KTTimeState $Action if ($st.Domain) { throw "Etki alanina bagli bilgisayarda zaman kaynagi degistirilmez" } Start-KTTimeService Invoke-KTW32tmConfig -PeerList (ConvertTo-KTPeerList $Action.Sunucular) -SyncFrom "MANUAL" } function Test-KTTimeVerify { <# .SYNOPSIS Ayar okundu ve esitleme basarili olmali; esitleme hatasi basari sayilmaz. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if ((Test-KTTimeState $Action $State) -ne "Applied") { return [pscustomobject]@{ Ok = $false; Not = ""; YenidenBaslat = "Yok" } } return [pscustomobject]@{ Ok = [bool](Invoke-KTW32tmResync); Not = ""; YenidenBaslat = "Yok" } } function Undo-KTTime { <# .SYNOPSIS Onceki liste, kaynak turu ve baslangic turu. Servis onceden calismiyorsa durdurulur. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if (-not $Before.Exists) { return } $cur = Get-KTTimeState $Action if (Test-KTTimeRestored -Before $Before -Now $cur) { return } $flag = $script:KT_W32TypeFlags[[string]$Before.Type] if (-not $flag) { throw "Onceki zaman kaynagi turu bilinmiyor: $($Before.Type)" } Start-KTTimeService Invoke-KTW32tmConfig -PeerList ([string]$Before.NtpServer) -SyncFrom $flag if ([string]$Before.StartType -and [string]$Before.StartType -ne [string](Get-KTTimeState $Action).StartType) { Invoke-KTScConfig -Name "W32Time" -StartType ([string]$Before.StartType) } if ([string]$Before.Status -ne "Running") { Invoke-KTServiceControl -Name "W32Time" -Command "Stop" } } function Test-KTTimeRestored { <# .SYNOPSIS Saf. Liste (tam metin), kaynak turu ve baslangic turu onceki gibi mi. #> param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) if (-not $Before.Exists) { return $true } return ($Now.Exists -and [string]$Now.NtpServer -eq [string]$Before.NtpServer -and [string]$Now.Type -eq [string]$Before.Type -and [string]$Now.StartType -eq [string]$Before.StartType) } function Test-KTTimeAction { <# .SYNOPSIS Saf. Sunucular: en az bir ad (harf, rakam, nokta, tire). #> param([Parameter(Mandatory)] $Action) $s = @(Get-KTField $Action Sunucular | Where-Object { $_ }) if ($s.Count -eq 0) { return @("ZamanSunucusu: Sunucular en az bir ad olmali") } foreach ($n in $s) { if ([string]$n -cnotmatch '^[A-Za-z0-9]([A-Za-z0-9.-]*[A-Za-z0-9])?$') { return @("ZamanSunucusu: gecersiz sunucu adi: $n") } } return @() } # ============================================================== # PnP aygiti etkin / devre disi (Aygit): Get / Test / Set / Undo / Restored / Check / Dogrula # ============================================================== # Aygit Yoneticisi'nde "Aygiti devre disi birak" ile ayni is (pnputil /disable-device, /enable-device). # Tanimdaki islem: @{ Tur = 'Aygit'; Kimlik = 'ACPI000C'; Etkin = $false } # Kimlik ACPI donanim kimligi: aygitin HardwareID listesinde 'ACPI\' veya '*' olan, takili (present) # her aygit. Ornek: ACPI000C = ACPI Processor Aggregator (ACPI belirtimi), PNP0C14 = Windows Management Interface for ACPI. # Durum: @{ Exists (eslesen aygit var); Liste ("InstanceId=1;InstanceId=0", 1 etkin, 0 devre disi) }. # Devre disi: ConfigManagerErrorCode 22 (CM_PROB_DISABLED). # Geri alma her aygitin onceki durumunu InstanceId ile kurar (yalniz degisen aygitlar). # pnputil 3010 (yeniden baslatma gerekli) donerse: aygit yeniden baslatinca degisir; dogrulama bunu kabul eder ve # tweak'in yeniden baslatma gerekcesi Bilgisayar olur. # Dis cagrilar ince sarmalayicilarda (Get-KTPnpDeviceRaw, Invoke-KTPnpUtil); testler bunlari degistirir. $script:KT_AygitBekleyen = @{} # InstanceId -> $true: pnputil 3010 dondu, etki yeniden baslatmada $script:KT_AygitKritik = @{} # InstanceId -> $true: Windows kritik sistem aygitini kapatmaya izin vermedi (atlandi) $script:KT_PnpKapatilamaz = -536870351 # 0xE0000231 ERROR_NOT_DISABLEABLE (Adem PC'si 03.11.2026: ACPI\PNP0C14\AOD) # ---------------- Saf ---------------- function Test-KTHardwareIdMatch { <# .SYNOPSIS Saf. HardwareID listesinde 'ACPI\' veya '*' var mi (buyuk/kucuk harf duyarsiz). #> param($HardwareIds, [Parameter(Mandatory)][string]$Kimlik) foreach ($h in @($HardwareIds)) { $s = [string]$h if ([string]::Equals($s, "ACPI\$Kimlik", [StringComparison]::OrdinalIgnoreCase) -or [string]::Equals($s, "*$Kimlik", [StringComparison]::OrdinalIgnoreCase)) { return $true } } return $false } function ConvertTo-KTDeviceList { <# .SYNOPSIS Saf. @( @{ InstanceId; Enabled } ) -> "id=1;id=0" (InstanceId sirasiyla). #> param($Rows) return ((@($Rows) | Where-Object { $_ } | Sort-Object InstanceId | ForEach-Object { "$($_.InstanceId)=$([int][bool]$_.Enabled)" }) -join ";") } function ConvertFrom-KTDeviceList { <# .SYNOPSIS Saf. "id=1;id=0" -> @( @{ InstanceId; Enabled } ). InstanceId '=' icermez; son '=' ayractir. #> param([AllowEmptyString()][string]$Liste) foreach ($p in @($Liste -split ';' | Where-Object { $_ })) { $i = $p.LastIndexOf('=') [pscustomobject]@{ InstanceId = $p.Substring(0, $i); Enabled = ($p.Substring($i + 1) -eq "1") } } } function Test-KTDeviceDisableable { <# .SYNOPSIS Saf. DevNodeStatus'ta DN_DISABLEABLE (0x00002000, cfg.h) var mi. Devre disi aygitta bayrak kalkabilir: o zaman zaten kapali aygit (DN_HAS_PROBLEM 0x400) da uygun sayilir ki geri alma ve durum okuma onu gorsun. #> param([Parameter(Mandatory)][uint32]$Status) return (($Status -band 0x2000) -ne 0) -or (($Status -band 0x400) -ne 0) } # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTPnpDeviceRaw { <# .SYNOPSIS Salt okunur. Kimlige uyan takili aygitlar: @( @{ InstanceId; Enabled } ). #> param([Parameter(Mandatory)][string]$Kimlik) foreach ($d in @(Get-PnpDevice -PresentOnly -ErrorAction SilentlyContinue)) { if (-not (Test-KTHardwareIdMatch $d.HardwareID $Kimlik)) { continue } # Windows'un kapatilmasina izin vermedigi (kritik) aygit yok sayilir: DevNodeStatus'ta DN_DISABLEABLE (0x2000) yoksa. # Hepsi kritikse islem "Sistemde yok" olur, tweak uygun degil sayilir (Adem PC'si 03.11.2026: uc PNP0C14 aygiti kritik). $st = $null try { $st = (Get-PnpDeviceProperty -InstanceId $d.InstanceId -KeyName 'DEVPKEY_Device_DevNodeStatus' -ErrorAction Stop).Data } catch { } if ($null -ne $st -and -not (Test-KTDeviceDisableable ([uint32]$st))) { continue } [pscustomobject]@{ InstanceId = [string]$d.InstanceId; Enabled = ([int]$d.ConfigManagerErrorCode -ne 22) } } } function Invoke-KTPnpUtil { <# .SYNOPSIS pnputil /enable-device | /disable-device "". 0 -> $false, 3010 -> $true (yeniden baslatma gerekli), diger -> hata. #> param([Parameter(Mandatory)][string]$InstanceId, [Parameter(Mandatory)][bool]$Enable) $cmd = if ($Enable) { "/enable-device" } else { "/disable-device" } $out = & pnputil.exe $cmd $InstanceId 2>&1 if ($LASTEXITCODE -eq 3010) { return $true } # Kritik sistem aygiti kapatilamaz: hata degil, o aygit atlanir (dogrulama kabul eder, nota yazilir) if (-not $Enable -and $LASTEXITCODE -eq $script:KT_PnpKapatilamaz) { $script:KT_AygitKritik[$InstanceId] = $true; return $false } if ($LASTEXITCODE -ne 0) { throw "pnputil $cmd basarisiz ($LASTEXITCODE): $InstanceId | $(($out | Out-String).Trim())" } return $false } # ---------------- Get / Test / Set / Undo ---------------- function Get-KTDeviceState { <# .SYNOPSIS Salt okunur. #> param([Parameter(Mandatory)] $Action) $rows = @(Get-KTPnpDeviceRaw -Kimlik ([string]$Action.Kimlik)) return [pscustomobject]@{ Exists = ($rows.Count -gt 0); Liste = (ConvertTo-KTDeviceList $rows) } } function Test-KTDeviceState { <# .SYNOPSIS Saf. Aygit yoksa NotApplicable; hepsi hedefteyse Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if (-not $State.Exists) { return "NotApplicable" } foreach ($d in @(ConvertFrom-KTDeviceList ([string]$State.Liste))) { if ([bool]$d.Enabled -ne [bool]$Action.Etkin) { return "NotApplied" } } return "Applied" } function Set-KTDevice { param([Parameter(Mandatory)] $Action) foreach ($d in @(ConvertFrom-KTDeviceList ([string](Get-KTDeviceState $Action).Liste))) { if ([bool]$d.Enabled -eq [bool]$Action.Etkin) { continue } if (Invoke-KTPnpUtil -InstanceId $d.InstanceId -Enable ([bool]$Action.Etkin)) { $script:KT_AygitBekleyen[$d.InstanceId] = $true } } } function Test-KTDeviceVerify { <# .SYNOPSIS Hedefteyse Ok; degilse yalniz hedefte olmayan her aygit icin pnputil 3010 dondurmusse Ok + yeniden baslatma. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if ((Test-KTDeviceState $Action $State) -eq "Applied") { return [pscustomobject]@{ Ok = $true; Not = ""; YenidenBaslat = "Yok" } } $bekleyen = 0; $kritik = 0 foreach ($d in @(ConvertFrom-KTDeviceList ([string]$State.Liste))) { if ([bool]$d.Enabled -eq [bool]$Action.Etkin) { continue } if ($script:KT_AygitBekleyen[$d.InstanceId]) { $bekleyen++ } elseif ($script:KT_AygitKritik[$d.InstanceId]) { $kritik++ } else { return [pscustomobject]@{ Ok = $false; Not = ""; YenidenBaslat = "Yok" } } } $not = @() if ($bekleyen) { $not += "(Aygit yeniden baslatinca devre disi kalir)" } if ($kritik) { $not += "($kritik aygit kritik sistem aygiti; Windows kapatmaya izin vermedi, atlandi)" } return [pscustomobject]@{ Ok = $true; Not = ($not -join " "); YenidenBaslat = $(if ($bekleyen) { "Bilgisayar" } else { "Yok" }) } } function Undo-KTDevice { <# .SYNOPSIS Her aygitin onceki durumu (InstanceId ile). O arada kalkan aygit atlanir. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if (-not $Before.Exists) { return } $cur = @{} foreach ($d in @(ConvertFrom-KTDeviceList ([string](Get-KTDeviceState $Action).Liste))) { $cur[$d.InstanceId] = [bool]$d.Enabled } foreach ($d in @(ConvertFrom-KTDeviceList ([string]$Before.Liste))) { if (-not $cur.ContainsKey($d.InstanceId) -or $cur[$d.InstanceId] -eq [bool]$d.Enabled) { continue } [void](Invoke-KTPnpUtil -InstanceId $d.InstanceId -Enable ([bool]$d.Enabled)) } } function Test-KTDeviceRestored { <# .SYNOPSIS Saf. Onceki listedeki her aygit (hala varsa) onceki durumda mi. #> param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) if (-not $Before.Exists) { return $true } $now = @{} foreach ($d in @(ConvertFrom-KTDeviceList ([string]$Now.Liste))) { $now[$d.InstanceId] = [bool]$d.Enabled } foreach ($d in @(ConvertFrom-KTDeviceList ([string]$Before.Liste))) { if ($now.ContainsKey($d.InstanceId) -and $now[$d.InstanceId] -ne [bool]$d.Enabled) { return $false } } return $true } function Test-KTDeviceAction { <# .SYNOPSIS Saf. Kimlik ACPI kimligi (harf/rakam, 7-8 karakter); Etkin bool. #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() if ([string](Get-KTField $Action Kimlik) -cnotmatch '^[A-Z0-9]{7,8}$') { $err.Add("Aygit: Kimlik ACPI donanim kimligi olmali (ornek ACPI000C)") } if ((Get-KTField $Action Etkin) -isnot [bool]) { $err.Add("Aygit: Etkin `$true veya `$false olmali") } return $err.ToArray() } # ============================================================== # Guc plani ayari islemi (GucAyari): Get / Test / Set / Undo / Restored / Check / Resolve # ============================================================== # Islem: @{ Tur = 'GucAyari'; Plan = 'Aktif' | ; Grup = ; Ayar = ; Deger = } # Durum: @{ Exists; Plan; AC; DC } # AC (prizde) ve DC (pilde) ayni hedefe yazilir (karar 44.5). # Deger = 'Varsayilan': Windows varsayilani. Plan Windows'un uc kisilik planindan biriyse (Dengeli, Yuksek performans, # Guc tasarrufu; MS: plan GUID'i kisilik GUID'iyle ayni) o kisiligin AC ve DC varsayilan indeksi # PowerReadACDefaultIndex / PowerReadDCDefaultIndex ile okunur, Resolve somut DegerAC / DegerDC yazar. # Baska plan (Nihai Performans, Ultra Performans kopyasi, OEM plani) veya varsayilani olmayan ayar: islem atlanir # (VarsayilanYok; OEM planinda Windows genel varsayilani fabrika ayari gibi yazilmaz). powercfg /restoredefaultschemes kullanilmaz. # 'Aktif', yedekten ONCE Resolve ile o anki planin GUID'ine cevrilir: geri alma, arada aktif plan degisse bile ayni plana yazar. # Okuma powrprof.dll ile (dil bagimsiz, gizli ayarlari da okur; powercfg /q ciktisi dile gore degisir ve gizli ayarlari gostermez). # Yazma powercfg.exe ile (GUID parametreleri dil bagimsiz). Ayar veya plan yoksa API 2 dondurur -> NotApplicable. # Aktif plana yazilan deger ancak plan yeniden etkinlestirilince devreye girer (/setactive). # Dis cagrilar ince sarmalayicilarda (Get-KTActivePowerScheme, Read-KTPowerValue, Invoke-KTPowerCfg); # testler bunlari sahteleriyle degistirir. $script:KT_PowerNativeSource = @' [DllImport("powrprof.dll")] public static extern uint PowerGetActiveScheme(IntPtr root, out IntPtr guid); [DllImport("powrprof.dll")] public static extern uint PowerReadACValueIndex(IntPtr root, ref Guid scheme, ref Guid sub, ref Guid setting, out uint value); [DllImport("powrprof.dll")] public static extern uint PowerReadDCValueIndex(IntPtr root, ref Guid scheme, ref Guid sub, ref Guid setting, out uint value); [DllImport("powrprof.dll")] public static extern uint PowerReadACDefaultIndex(IntPtr root, ref Guid personality, ref Guid sub, ref Guid setting, out uint value); [DllImport("powrprof.dll")] public static extern uint PowerReadDCDefaultIndex(IntPtr root, ref Guid personality, ref Guid sub, ref Guid setting, out uint value); [DllImport("kernel32.dll")] public static extern IntPtr LocalFree(IntPtr p); [DllImport("powrprof.dll")] public static extern uint PowerSetActiveOverlayScheme(ref Guid overlay); [StructLayout(LayoutKind.Sequential)] public struct PowerStatus { public byte ACLineStatus; public byte BatteryFlag; public byte BatteryLifePercent; public byte SystemStatusFlag; public int BatteryLifeTime; public int BatteryFullLifeTime; } [DllImport("kernel32.dll")] public static extern bool GetSystemPowerStatus(out PowerStatus status); '@ # Kisilik GUID'leri (MS Learn Power Setting GUIDs: GUID_TYPICAL/MIN/MAX_POWER_SAVINGS); stok planlarin GUID'i de bunlar $script:KT_GucKisilikleri = @{ "381b4222-f694-41f0-9685-ff5bb260df2e" = "Dengeli" "8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c" = "Yuksek performans" "a1841308-3541-4fab-bc81-f71556f20b4a" = "Guc tasarrufu" } function Initialize-KTPowerNative { if (-not ("KelTweaker.PowerNative" -as [type])) { Add-Type -Namespace KelTweaker -Name PowerNative -MemberDefinition $script:KT_PowerNativeSource -ErrorAction Stop } } function ConvertTo-KTGuidText { <# .SYNOPSIS Saf. GUID'i kucuk harf, parantezsiz metne cevirir. Gecersizse hata. #> param([Parameter(Mandatory)][string]$Guid) return ([Guid]$Guid.Trim('{', '}', ' ')).ToString("D") } # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTActivePowerScheme { <# .SYNOPSIS Salt okunur. Aktif plan GUID'i. #> Initialize-KTPowerNative $p = [IntPtr]::Zero $rc = [KelTweaker.PowerNative]::PowerGetActiveScheme([IntPtr]::Zero, [ref]$p) if ($rc -ne 0) { throw "Aktif guc plani okunamadi (rc=$rc)" } try { return ([Runtime.InteropServices.Marshal]::PtrToStructure($p, [type][Guid])).ToString("D") } finally { [KelTweaker.PowerNative]::LocalFree($p) | Out-Null } } function Read-KTPowerValue { <# .SYNOPSIS Salt okunur. AC veya DC degeri: @{ Rc; Value }. Rc 0 = basarili, 2 = ayar/plan yok. #> param([Parameter(Mandatory)][string]$Scheme, [Parameter(Mandatory)][string]$SubGroup, [Parameter(Mandatory)][string]$Setting, [Parameter(Mandatory)][ValidateSet("AC", "DC")][string]$Mode) Initialize-KTPowerNative $s = [Guid]$Scheme; $g = [Guid]$SubGroup; $t = [Guid]$Setting; $v = [uint32]0 $rc = if ($Mode -eq "AC") { [KelTweaker.PowerNative]::PowerReadACValueIndex([IntPtr]::Zero, [ref]$s, [ref]$g, [ref]$t, [ref]$v) } else { [KelTweaker.PowerNative]::PowerReadDCValueIndex([IntPtr]::Zero, [ref]$s, [ref]$g, [ref]$t, [ref]$v) } return [pscustomobject]@{ Rc = [int]$rc; Value = [int64]$v } } function Read-KTPowerDefault { <# .SYNOPSIS Salt okunur. Kisiligin AC veya DC varsayilan indeksi: @{ Rc; Value }. Rc 0 = basarili. #> param([Parameter(Mandatory)][string]$Personality, [Parameter(Mandatory)][string]$SubGroup, [Parameter(Mandatory)][string]$Setting, [Parameter(Mandatory)][ValidateSet("AC", "DC")][string]$Mode) Initialize-KTPowerNative $s = [Guid]$Personality; $g = [Guid]$SubGroup; $t = [Guid]$Setting; $v = [uint32]0 $rc = if ($Mode -eq "AC") { [KelTweaker.PowerNative]::PowerReadACDefaultIndex([IntPtr]::Zero, [ref]$s, [ref]$g, [ref]$t, [ref]$v) } else { [KelTweaker.PowerNative]::PowerReadDCDefaultIndex([IntPtr]::Zero, [ref]$s, [ref]$g, [ref]$t, [ref]$v) } return [pscustomobject]@{ Rc = [int]$rc; Value = [int64]$v } } function Invoke-KTPowerCfg { <# .SYNOPSIS powercfg.exe'yi verilen argumanlarla calistirir; basarisizsa hata firlatir. #> param([Parameter(Mandatory)][string[]]$Arguments) $out = & powercfg.exe @Arguments 2>&1 if ($LASTEXITCODE -ne 0) { throw "powercfg $($Arguments -join ' ') basarisiz ($LASTEXITCODE): $(($out | Out-String).Trim())" } } # ---------------- GucAyari ---------------- function Resolve-KTPowerSetting { <# .SYNOPSIS 'Aktif' planini o anki GUID'e cevirir; yeni kopya dondurur (tanim degismez). #> param([Parameter(Mandatory)] $Action) $c = Copy-KTAction $Action $c.Plan = if ([string]$Action.Plan -eq "Aktif") { Get-KTActivePowerScheme } else { ConvertTo-KTGuidText $Action.Plan } # Windows varsayilani: yalniz stok kisilik planinda; yedekten gelen somut islemde (DegerAC var) yeniden okunmaz if ([string]$Action.Deger -eq "Varsayilan" -and $null -eq (Get-KTField $Action DegerAC)) { if (-not $script:KT_GucKisilikleri.ContainsKey($c.Plan)) { $c.VarsayilanYok = "(Etkin plan Windows'un Dengeli / Yuksek performans / Guc tasarrufu planlarindan biri degil; varsayilan bilinmiyor)" return $c } $ac = Read-KTPowerDefault $c.Plan $Action.Grup $Action.Ayar "AC" $dc = Read-KTPowerDefault $c.Plan $Action.Grup $Action.Ayar "DC" if ($ac.Rc -ne 0 -or $dc.Rc -ne 0) { $c.VarsayilanYok = "(Bu ayarin Windows varsayilani okunamadi)"; return $c } $c.DegerAC = $ac.Value; $c.DegerDC = $dc.Value } return $c } function Get-KTPowerTarget { <# .SYNOPSIS Saf. Islemin AC ve DC hedefi: @{ AC; DC } veya $null (Windows varsayilani cozulmedi). #> param([Parameter(Mandatory)] $Action) if ([string]$Action.Deger -eq "Varsayilan") { if ($null -eq (Get-KTField $Action DegerAC) -or $null -eq (Get-KTField $Action DegerDC)) { return $null } return [pscustomobject]@{ AC = [int64]$Action.DegerAC; DC = [int64]$Action.DegerDC } } return [pscustomobject]@{ AC = [int64]$Action.Deger; DC = [int64]$Action.Deger } } function Get-KTPowerSettingSkip { <# .SYNOPSIS Saf. Windows varsayilani cozulemediyse islem atlanir (neden ekrana yazilir). #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) $why = [string](Get-KTField $Action VarsayilanYok) if ($why) { return $why } if ($null -eq (Get-KTPowerTarget $Action)) { return "(Bu ayarin Windows varsayilani okunamadi)" } return "" } function Get-KTPowerSettingState { <# .SYNOPSIS Salt okunur: @{ Exists; Plan; AC; DC }. #> param([Parameter(Mandatory)] $Action) $plan = if ([string]$Action.Plan -eq "Aktif") { Get-KTActivePowerScheme } else { ConvertTo-KTGuidText $Action.Plan } $ac = Read-KTPowerValue $plan $Action.Grup $Action.Ayar "AC" $dc = Read-KTPowerValue $plan $Action.Grup $Action.Ayar "DC" if ($ac.Rc -eq 2 -or $dc.Rc -eq 2) { return [pscustomobject]@{ Exists = $false; Plan = $plan; AC = $null; DC = $null } } if ($ac.Rc -ne 0 -or $dc.Rc -ne 0) { throw "Guc ayari okunamadi (AC rc=$($ac.Rc), DC rc=$($dc.Rc)): $($Action.Ayar)" } return [pscustomobject]@{ Exists = $true; Plan = $plan; AC = $ac.Value; DC = $dc.Value } } function Test-KTPowerSettingState { <# .SYNOPSIS Saf. Ayar yoksa NotApplicable; AC ve DC hedefe esitse Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if (-not $State.Exists) { return "NotApplicable" } $h = Get-KTPowerTarget $Action if ($null -eq $h) { return "NotApplied" } # varsayilan cozulmedi: Atla atlar if ([int64]$State.AC -eq $h.AC -and [int64]$State.DC -eq $h.DC) { return "Applied" } return "NotApplied" } function Write-KTPowerSettingValues { param([Parameter(Mandatory)][string]$Plan, [Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $AC, [Parameter(Mandatory)] $DC) Invoke-KTPowerCfg @("/setacvalueindex", $Plan, [string]$Action.Grup, [string]$Action.Ayar, [string][int64]$AC) Invoke-KTPowerCfg @("/setdcvalueindex", $Plan, [string]$Action.Grup, [string]$Action.Ayar, [string][int64]$DC) if ($Plan -eq (Get-KTActivePowerScheme)) { Invoke-KTPowerCfg @("/setactive", $Plan) } } function Set-KTPowerSetting { param([Parameter(Mandatory)] $Action) $st = Get-KTPowerSettingState $Action if (-not $st.Exists) { throw "Guc ayari bulunamadi: $($Action.Ayar)" } $h = Get-KTPowerTarget $Action if ($null -eq $h) { throw "Guc ayarinin Windows varsayilani cozulmedi: $($Action.Ayar)" } Write-KTPowerSettingValues -Plan $st.Plan -Action $Action -AC $h.AC -DC $h.DC } function Undo-KTPowerSetting { <# .SYNOPSIS AC ve DC'yi onceki degerlerine yazar (ayni plana). #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if (-not $Before.Exists) { return } Write-KTPowerSettingValues -Plan ([string]$Before.Plan) -Action $Action -AC $Before.AC -DC $Before.DC } function Test-KTPowerSettingRestored { <# .SYNOPSIS Saf. AC ve DC onceki degerlerde mi. #> param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) if (-not $Before.Exists) { return $true } return ($Now.Exists -and [int64]$Now.AC -eq [int64]$Before.AC -and [int64]$Now.DC -eq [int64]$Before.DC) } function Test-KTPowerSettingAction { <# .SYNOPSIS Saf. Tanim alanlari: Plan ('Aktif' veya GUID), Grup ve Ayar (GUID), Deger (0..4294967295 veya 'Varsayilan'). #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() $isGuid = { param($v) $g = [Guid]::Empty; [Guid]::TryParse(([string]$v).Trim('{', '}', ' '), [ref]$g) } if ([string]$Action.Plan -cne "Aktif" -and -not (& $isGuid $Action.Plan)) { $err.Add("GucAyari: Plan 'Aktif' veya GUID olmali") } if (-not (& $isGuid $Action.Grup)) { $err.Add("GucAyari: Grup GUID olmali") } if (-not (& $isGuid $Action.Ayar)) { $err.Add("GucAyari: Ayar GUID olmali") } $d = $Action.Deger if ($null -ne (Get-KTField $Action DegerAC) -or $null -ne (Get-KTField $Action DegerDC)) { $err.Add("GucAyari: DegerAC / DegerDC tanimda yazilmaz") } if ($d -is [string] -and $d -ceq "Varsayilan") { } elseif ($null -eq $d -or $d -is [string] -or $d -is [bool] -or [int64]$d -lt 0 -or [int64]$d -gt 4294967295) { $err.Add("GucAyari: Deger 0..4294967295 sayi olmali") } return $err.ToArray() } # ============================================================== # Guc plani kurma (GucPlani) ve etkin plan (AktifPlan) islemleri # ============================================================== # GucPlani : @{ Tur = 'GucPlani'; Kaynak = ; Plan = ; Ad = 'Ultra Performans' } # powercfg /duplicatescheme + /changename (MS Learn powercfg: hedef GUID istege bagli 3. arguman). # Sabit hedef GUID sayesinde tekrar calistirmada plan tanini, ikinci kopya olusmaz; plan varsa yeniden kurulmaz (karar 44). # Geri alma: plan onceden yoksa silinir (etkinse silinemez -> hata; AktifPlan geri almasi once calisir). # Durum: @{ Exists } # AktifPlan: @{ Tur = 'AktifPlan'; Plan = } etkin plani degistirir; geri alma onceki etkin plani kurar. # Durum: @{ Exists (hedef plan kayitli mi); Active } # Plan kaydi HKLM\SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes\ altinda okunur. function Test-KTPowerSchemeExists { <# .SYNOPSIS Salt okunur. Plan kayitli mi. #> param([Parameter(Mandatory)][string]$Scheme) $k = Open-KTRegKey "HKLM:\SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes\$Scheme" if ($k) { $k.Dispose(); return $true } return $false } # ---------------- GucPlani ---------------- function Get-KTPowerPlanInstallState { <# .SYNOPSIS Salt okunur. #> param([Parameter(Mandatory)] $Action) return [pscustomobject]@{ Exists = (Test-KTPowerSchemeExists (ConvertTo-KTGuidText $Action.Plan)) } } function Test-KTPowerPlanInstallState { <# .SYNOPSIS Saf. Plan varsa Applied (yeniden kurulmaz; ayarlari ayri tweak'ler yazar). #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if ($State.Exists) { return "Applied" } else { return "NotApplied" } } function Set-KTPowerPlanInstall { <# .SYNOPSIS Kaynak plani sabit GUID'e kopyalar ve adini verir. #> param([Parameter(Mandatory)] $Action) $plan = ConvertTo-KTGuidText $Action.Plan Invoke-KTPowerCfg @("/duplicatescheme", (ConvertTo-KTGuidText $Action.Kaynak), $plan) Invoke-KTPowerCfg @("/changename", $plan, [string]$Action.Ad) } function Undo-KTPowerPlanInstall { <# .SYNOPSIS Plan onceden yoksa siler. Etkin plan silinemez: once etkin plan geri alinmis olmali. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if ($Before.Exists) { return } $plan = ConvertTo-KTGuidText $Action.Plan if (-not (Test-KTPowerSchemeExists $plan)) { return } if ((Get-KTActivePowerScheme) -eq $plan) { throw "Plan etkin oldugu icin silinemedi: $plan" } Invoke-KTPowerCfg @("/delete", $plan) } function Test-KTPowerPlanInstallRestored { param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) return ([bool]$Now.Exists -eq [bool]$Before.Exists) } function Test-KTPowerPlanInstallAction { <# .SYNOPSIS Saf. Kaynak ve Plan GUID, farkli; Ad ASCII. #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() $g = @{} foreach ($f in "Kaynak", "Plan") { try { $g[$f] = ConvertTo-KTGuidText ([string]$Action.$f) } catch { $err.Add("GucPlani: $f GUID olmali") } } if ($g.Kaynak -and $g.Kaynak -eq $g.Plan) { $err.Add("GucPlani: Plan, Kaynak ile ayni olamaz") } if ([string]::IsNullOrEmpty([string]$Action.Ad) -or -not (Test-KTAscii ([string]$Action.Ad))) { $err.Add("GucPlani: Ad zorunlu ve ASCII") } return $err.ToArray() } # ---------------- AktifPlan ---------------- function Get-KTActivePlanState { <# .SYNOPSIS Salt okunur. #> param([Parameter(Mandatory)] $Action) return [pscustomobject]@{ Exists = (Test-KTPowerSchemeExists (ConvertTo-KTGuidText $Action.Plan)); Active = (Get-KTActivePowerScheme) } } function Test-KTActivePlanState { <# .SYNOPSIS Saf. Hedef plan etkinse Applied. Plan yoksa da NotApplied: ayni tweak'te GucPlani once kurar; yoksa Set hata verir. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if ($State.Active -eq (ConvertTo-KTGuidText $Action.Plan)) { return "Applied" } return "NotApplied" } function Set-KTActivePlan { param([Parameter(Mandatory)] $Action) $plan = ConvertTo-KTGuidText $Action.Plan if (-not (Test-KTPowerSchemeExists $plan)) { throw "Guc plani yok: $plan" } Invoke-KTPowerCfg @("/setactive", $plan) } function Undo-KTActivePlan { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if ((Get-KTActivePowerScheme) -eq $Before.Active) { return } if (-not (Test-KTPowerSchemeExists ([string]$Before.Active))) { throw "Onceki guc plani artik yok: $($Before.Active)" } Invoke-KTPowerCfg @("/setactive", [string]$Before.Active) } function Test-KTActivePlanRestored { param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) return ($Now.Active -eq $Before.Active) } function Test-KTActivePlanAction { param([Parameter(Mandatory)] $Action) try { ConvertTo-KTGuidText ([string]$Action.Plan) | Out-Null; return @() } catch { return @("AktifPlan: Plan GUID olmali") } } # ---------------- GucPlaniSil ---------------- # @{ Tur = 'GucPlaniSil'; Plan = } plani siler (Y18 Varsayilana al: Ultra Performans). powercfg /restoredefaultschemes kullanilmaz. # Etkin plan silinmez (hata): ayni listede once AktifPlan baska plani etkinlestirir. # Silmeden once plan yedek klasorune disa aktarilir (powercfg /export); geri alma ayni GUID'le iceri aktarir (/import), # plan ayarlariyla geri gelir. Dosya yolu Resolve'da (yedekten once) islemin Dosya alanina yazilir. # Durum: @{ Exists } function Resolve-KTPowerPlanDelete { <# .SYNOPSIS Somut kopya: Dosya alani yoksa yedek klasorunde zaman damgali .pow yolu. #> param([Parameter(Mandatory)] $Action) $c = Copy-KTAction $Action $c.Plan = ConvertTo-KTGuidText $Action.Plan if (-not (Get-KTField $Action Dosya)) { $c.Dosya = Join-Path $script:KT_BackupDir ("GucPlani_{0}_{1}.pow" -f $c.Plan, (Get-Date).ToString("yyyyMMdd_HHmmss")) } return $c } function Export-KTPowerScheme { <# .SYNOPSIS Plani dosyaya aktarir (klasor yoksa olusturur). #> param([Parameter(Mandatory)][string]$Plan, [Parameter(Mandatory)][string]$Dosya) $d = Split-Path -Parent $Dosya if ($d -and -not (Test-Path -LiteralPath $d)) { New-Item -ItemType Directory -Path $d -Force | Out-Null } Invoke-KTPowerCfg @("/export", $Dosya, $Plan) if (-not (Test-Path -LiteralPath $Dosya)) { throw "Guc plani disa aktarilamadi: $Dosya" } } function Get-KTPowerPlanDeleteState { <# .SYNOPSIS Salt okunur. #> param([Parameter(Mandatory)] $Action) return [pscustomobject]@{ Exists = (Test-KTPowerSchemeExists (ConvertTo-KTGuidText $Action.Plan)) } } function Test-KTPowerPlanDeleteState { <# .SYNOPSIS Saf. Plan yoksa Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if ($State.Exists) { return "NotApplied" } else { return "Applied" } } function Set-KTPowerPlanDelete { param([Parameter(Mandatory)] $Action) $plan = ConvertTo-KTGuidText $Action.Plan if (-not (Test-KTPowerSchemeExists $plan)) { return } if ((Get-KTActivePowerScheme) -eq $plan) { throw "Plan etkin oldugu icin silinmedi: $plan" } if (-not [string](Get-KTField $Action Dosya)) { throw "GucPlaniSil: yedek dosya yolu yok (Resolve calismadi)" } Export-KTPowerScheme -Plan $plan -Dosya ([string]$Action.Dosya) Invoke-KTPowerCfg @("/delete", $plan) } function Undo-KTPowerPlanDelete { <# .SYNOPSIS Plan onceden varsa ve simdi yoksa disa aktarilan dosyadan ayni GUID'le geri getirir. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if (-not $Before.Exists) { return } $plan = ConvertTo-KTGuidText $Action.Plan if (Test-KTPowerSchemeExists $plan) { return } $f = [string](Get-KTField $Action Dosya) if (-not $f -or -not (Test-Path -LiteralPath $f)) { throw "Guc plani yedek dosyasi yok: $f" } Invoke-KTPowerCfg @("/import", $f, $plan) } function Test-KTPowerPlanDeleteAction { <# .SYNOPSIS Saf. Plan GUID; Windows'un kisilik planlari (Dengeli, Yuksek performans, Guc tasarrufu) silinmez; Dosya tanimda yazilmaz. #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() try { $p = ConvertTo-KTGuidText ([string]$Action.Plan) if ($script:KT_GucKisilikleri.ContainsKey($p)) { $err.Add("GucPlaniSil: Windows'un kendi plani silinmez") } } catch { $err.Add("GucPlaniSil: Plan GUID olmali") } if (Get-KTField $Action Dosya) { $err.Add("GucPlaniSil: Dosya tanimda yazilmaz") } return $err.ToArray() } # ============================================================== # Guc modu islemi (GucModu): Ayarlar > Sistem > Guc > Guc modu, prizdeki deger (Y17, karar 59) # ============================================================== # Islem: @{ Tur = 'GucModu'; Mod = } ded574b5-... = En iyi performans, 00000000-... = Dengeli # Durum: @{ Exists (Dengeli plan var mi); Ac (prizdeki mod); Kaynak ('AC' / 'DC' / 'Bilinmiyor') } # Yazma PowerSetActiveOverlayScheme (belgesiz; Adem'in PC'sinde Dengeli planda dogrulandi, 22.10.2026). API yalniz o anki # guc kaynagina yazar; kayda dogrudan yazmaya yonetici izni yok (24.10.2026). Bu yuzden pildeyken veya kaynak bilinmiyorken islem atlanir. # Yuksek performansli planlarda guc modu kilitli: yazarken etkin plan Dengeli degilse gecici Dengeli yapilir, sonra geri alinir. # Okuma kayittan (ActiveOverlayAcPowerScheme; yazma sonrasi API ile ayni deger, 22.10.2026). Deger yoksa Dengeli (sifir GUID). $script:KT_DengeliPlan = '381b4222-f694-41f0-9685-ff5bb260df2e' $script:KT_PilMetni = '(Guc modu icin prize takip tekrar calistirin)' $script:KT_KaynakMetni = '(Guc kaynagi okunamadi; guc modu yazilmadi)' # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTPowerOverlayAc { <# .SYNOPSIS Salt okunur. Prizdeki guc modu GUID'i (yoksa sifir GUID). #> $k = Open-KTRegKey "HKLM:\SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes" if (-not $k) { return [Guid]::Empty.ToString("D") } try { $v = [string]$k.GetValue("ActiveOverlayAcPowerScheme") } finally { $k.Dispose() } if ([string]::IsNullOrWhiteSpace($v)) { return [Guid]::Empty.ToString("D") } return ConvertTo-KTGuidText $v } function Get-KTPowerSource { <# .SYNOPSIS Salt okunur. 'AC' (prizde), 'DC' (pilde) veya 'Bilinmiyor'. #> Initialize-KTPowerNative $st = New-Object KelTweaker.PowerNative+PowerStatus if (-not [KelTweaker.PowerNative]::GetSystemPowerStatus([ref]$st)) { return "Bilinmiyor" } switch ([int]$st.ACLineStatus) { 1 { return "AC" } 0 { return "DC" } default { return "Bilinmiyor" } } } function Invoke-KTPowerOverlay { <# .SYNOPSIS O anki guc kaynaginin guc modunu yazar. Basarisizsa hata. #> param([Parameter(Mandatory)][string]$Mod) Initialize-KTPowerNative $g = [Guid]$Mod $rc = [KelTweaker.PowerNative]::PowerSetActiveOverlayScheme([ref]$g) if ($rc -ne 0) { throw "Guc modu yazilamadi (rc=$rc)" } } # ---------------- GucModu ---------------- function Write-KTPowerOverlayAc { <# .SYNOPSIS Prizdeki guc modunu yazar; etkin plan Dengeli degilse gecici Dengeli yapar ve geri alir. API o anki kaynaga yazdigi icin yalniz kaynak kesin AC ise yazar; pilde veya bilinmiyorsa hata. #> param([Parameter(Mandatory)][string]$Mod) $kaynak = Get-KTPowerSource if ($kaynak -eq "DC") { throw $script:KT_PilMetni } if ($kaynak -ne "AC") { throw $script:KT_KaynakMetni } $once = Get-KTActivePowerScheme if ($once -ne $script:KT_DengeliPlan) { Invoke-KTPowerCfg @("/setactive", $script:KT_DengeliPlan) } try { Invoke-KTPowerOverlay -Mod $Mod } finally { if ($once -ne $script:KT_DengeliPlan) { Invoke-KTPowerCfg @("/setactive", $once) } } } function Get-KTPowerModeState { <# .SYNOPSIS Salt okunur. #> param([Parameter(Mandatory)] $Action) return [pscustomobject]@{ Exists = (Test-KTPowerSchemeExists $script:KT_DengeliPlan); Ac = (Get-KTPowerOverlayAc); Kaynak = (Get-KTPowerSource) } } function Test-KTPowerModeState { <# .SYNOPSIS Saf. Dengeli plan yoksa guc modu yazilamaz: NotApplicable. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if (-not $State.Exists) { return "NotApplicable" } if ($State.Ac -eq (ConvertTo-KTGuidText $Action.Mod)) { return "Applied" } return "NotApplied" } function Get-KTPowerModeSkip { <# .SYNOPSIS Saf. Pildeyken veya kaynak bilinmiyorken prizdeki deger yazilamaz: ekran metni; yoksa bos. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if ($State.Kaynak -eq "DC") { return $script:KT_PilMetni } if ($State.Kaynak -ne "AC") { return $script:KT_KaynakMetni } return "" } function Set-KTPowerMode { param([Parameter(Mandatory)] $Action) Write-KTPowerOverlayAc -Mod (ConvertTo-KTGuidText $Action.Mod) } function Undo-KTPowerMode { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) if ((Get-KTPowerOverlayAc) -eq [string]$Before.Ac) { return } Write-KTPowerOverlayAc -Mod ([string]$Before.Ac) } function Test-KTPowerModeRestored { param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) return ([string]$Now.Ac -eq [string]$Before.Ac) } function Test-KTPowerModeAction { param([Parameter(Mandatory)] $Action) try { ConvertTo-KTGuidText ([string]$Action.Mod) | Out-Null; return @() } catch { return @("GucModu: Mod GUID olmali") } } function Format-KTPowerModeText { <# .SYNOPSIS Saf. Bilinen modlarin adi, digerleri GUID. #> param([string]$Mod) switch ($Mod) { "ded574b5-45a0-4f42-8737-46345c09c238" { return "En iyi performans" } "00000000-0000-0000-0000-000000000000" { return "Dengeli" } default { return $Mod } } } # ============================================================== # Acilis yapilandirmasi islemi (Bcd): Get / Test / Set / Undo / Restored / Check # ============================================================== # Islem: @{ Tur = 'Bcd'; Oge = 'tscsyncpolicy'; Deger = 2 } Deger = $null -> ogeyi sil # Durum: @{ Exists; Value } # YALNIZ izin listesindeki ogeler: urundeki satirlar (SYS06, SYS07, SYS09, SYS18). testsigning, nointegritychecks, # debug, hypervisorlaunchtype vb. listede olmadigi icin tanimda reddedilir (anti-cheat ve guvenlik). # Erisim WMI BCD saglayicisiyla (root\WMI BcdObject): bcdedit ciktisi Windows diline gore degisir. # Yonetici gerekir (okumak dahil). Oge kimlikleri: Timeout, DisplayBootMenu MS BcdBootMgrElementTypes; # DisableDynamicTick 0x260000A5, TscSyncPolicy 0x250000A6 (0 varsayilan, 1 legacy, 2 enhanced) phnt ntbcd.h; # CI testinde bcdedit ciktisiyla karsilastirilir. # Dis cagrilar ince sarmalayicilarda (Get-KTBcdElementRaw, Set-KTBcdElementRaw, Remove-KTBcdElementRaw). $script:KT_BcdObjects = @{ "{bootmgr}" = "{9dea862c-5cdd-4e70-acc1-f32b344d4795}" "{current}" = "{fa926493-6f1c-4193-a414-58f0b2456d1e}" } $script:KT_BcdElements = @{ "timeout" = @{ Id = 0x25000004; Kind = "Integer"; Object = "{bootmgr}" } "displaybootmenu" = @{ Id = 0x26000020; Kind = "Boolean"; Object = "{bootmgr}" } "disabledynamictick" = @{ Id = 0x260000A5; Kind = "Boolean"; Object = "{current}" } "tscsyncpolicy" = @{ Id = 0x250000A6; Kind = "Integer"; Object = "{current}" } } function Get-KTBcdElementInfo { <# .SYNOPSIS Saf. Oge izin listesinde mi; degilse hata. @{ Id; Kind; Object; ObjectGuid } #> param([Parameter(Mandatory)] $Action) $el = $script:KT_BcdElements[([string]$Action.Oge).ToLowerInvariant()] if (-not $el) { throw "BCD ogesi izin listesinde degil: $($Action.Oge)" } return [pscustomobject]@{ Id = [uint32]$el.Id; Kind = $el.Kind; Object = $el.Object; ObjectGuid = $script:KT_BcdObjects[$el.Object] } } function ConvertTo-KTBcdValue { <# .SYNOPSIS Saf. Degeri ogenin turune cevirir (Boolean -> bool, Integer -> int64). #> param([Parameter(Mandatory)][string]$Kind, [AllowNull()] $Value) if ($null -eq $Value) { return $null } if ($Kind -eq "Boolean") { if ($Value -is [bool]) { return $Value } throw "BCD Boolean degeri `$true veya `$false olmali: $Value" } if ($Value -is [bool] -or $Value -is [string]) { throw "BCD Integer degeri sayi olmali: $Value" } return [int64]$Value } # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Open-KTBcdObject { <# .SYNOPSIS Sistem BCD deposundaki nesneyi WMI yoluyla dogrudan acar (ayricalik etkin). BcdStore.OpenStore/OpenObject gomulu veri nesnesi dondurur ve uzerinde metot cagrilamaz (eski calismada canli goruldu); bu yuzden BcdObject anahtarlariyla (Id + StoreFilePath; bos yol = sistem deposu) ManagementObject olusturulur. #> param([Parameter(Mandatory)][string]$ObjectGuid) $opt = New-Object System.Management.ConnectionOptions $opt.Impersonation = [System.Management.ImpersonationLevel]::Impersonate $opt.EnablePrivileges = $true $scope = New-Object System.Management.ManagementScope("\\.\root\WMI", $opt) $path = New-Object System.Management.ManagementPath("BcdObject.Id=`"$ObjectGuid`",StoreFilePath=`"`"") $obj = New-Object System.Management.ManagementObject($scope, $path, $null) try { $obj.Get() } catch { # Yonetici degilken saglayici 0xD0000022 (erisim engellendi) dondurur, mesaj bos gelir $b = $_.Exception.GetBaseException() throw ("BCD nesnesi acilamadi ({0}; yonetici gerekli): 0x{1:X8} {2}" -f $ObjectGuid, $b.HResult, $b.Message) } return $obj } function Get-KTBcdElementRaw { <# .SYNOPSIS Salt okunur: @{ Exists; Value }. Oge tanimli degilse Exists = $false. Okuma hatasi "yok" sayilmaz (yanlis yedek, geri almada var olan ogeyi silme): yalniz bulunamadi kodlari yok demektir (saglayicinin dondurdugu 0xD0000225 (CI'da goruldu), STATUS_NOT_FOUND 0xC0000225, ERROR_NOT_FOUND 0x80070490, ERROR_FILE_NOT_FOUND 0x80070002); diger hata istisnadir. #> param([Parameter(Mandatory)][string]$ObjectGuid, [Parameter(Mandatory)][uint32]$Id, [Parameter(Mandatory)][string]$Kind) $obj = Open-KTBcdObject $ObjectGuid try { $r = $obj.GetElement($Id) } catch { $hr = $_.Exception.GetBaseException().HResult if ($hr -in @(0xD0000225, 0xC0000225, 0x80070490, 0x80070002)) { return [pscustomobject]@{ Exists = $false; Value = $null } } throw ("BCD ogesi okunamadi (0x{0:X8}): 0x{1:X8} {2}" -f $Id, $hr, $_.Exception.GetBaseException().Message) } # Istisnasiz basarisiz sonuc da belirsizdir (bulunamadi CI'da istisnayla geliyor): yok sayilmaz if (-not $r.ReturnValue -or -not $r.Element) { throw ("BCD ogesi okunamadi (0x{0:X8}): basarisiz sonuc" -f $Id) } $v = if ($Kind -eq "Boolean") { [bool]$r.Element.Boolean } else { [int64]$r.Element.Integer } return [pscustomobject]@{ Exists = $true; Value = $v } } function Set-KTBcdElementRaw { param([Parameter(Mandatory)][string]$ObjectGuid, [Parameter(Mandatory)][uint32]$Id, [Parameter(Mandatory)][string]$Kind, [Parameter(Mandatory)] $Value) $obj = Open-KTBcdObject $ObjectGuid $r = if ($Kind -eq "Boolean") { $obj.SetBooleanElement($Id, [bool]$Value) } else { $obj.SetIntegerElement($Id, [uint64]$Value) } if (-not $r.ReturnValue) { throw "BCD ogesi yazilamadi: 0x$('{0:X8}' -f $Id)" } } function Remove-KTBcdElementRaw { param([Parameter(Mandatory)][string]$ObjectGuid, [Parameter(Mandatory)][uint32]$Id) $obj = Open-KTBcdObject $ObjectGuid $r = $obj.DeleteElement($Id) if (-not $r.ReturnValue) { throw "BCD ogesi silinemedi: 0x$('{0:X8}' -f $Id)" } } # ---------------- Get / Test / Set / Undo ---------------- function Get-KTBcdState { <# .SYNOPSIS Salt okunur (yonetici gerekir). #> param([Parameter(Mandatory)] $Action) $i = Get-KTBcdElementInfo $Action return Get-KTBcdElementRaw -ObjectGuid $i.ObjectGuid -Id $i.Id -Kind $i.Kind } function Test-KTBcdState { <# .SYNOPSIS Saf. Deger $null: oge yoksa Applied. Aksi halde oge var ve deger esitse Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) $i = Get-KTBcdElementInfo $Action $want = ConvertTo-KTBcdValue $i.Kind $Action.Deger if ($null -eq $want) { if ($State.Exists) { return "NotApplied" } else { return "Applied" } } if ($State.Exists -and [string](ConvertTo-KTBcdValue $i.Kind $State.Value) -eq [string]$want) { return "Applied" } return "NotApplied" } function Set-KTBcd { param([Parameter(Mandatory)] $Action) $i = Get-KTBcdElementInfo $Action $want = ConvertTo-KTBcdValue $i.Kind $Action.Deger if ($null -eq $want) { if ((Get-KTBcdElementRaw -ObjectGuid $i.ObjectGuid -Id $i.Id -Kind $i.Kind).Exists) { Remove-KTBcdElementRaw -ObjectGuid $i.ObjectGuid -Id $i.Id } return } Set-KTBcdElementRaw -ObjectGuid $i.ObjectGuid -Id $i.Id -Kind $i.Kind -Value $want } function Undo-KTBcd { <# .SYNOPSIS Oge vardiysa eski degeri yazar; yoktuysa (ve simdi varsa) siler. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) $i = Get-KTBcdElementInfo $Action if ($Before.Exists) { Set-KTBcdElementRaw -ObjectGuid $i.ObjectGuid -Id $i.Id -Kind $i.Kind -Value (ConvertTo-KTBcdValue $i.Kind $Before.Value); return } if ((Get-KTBcdElementRaw -ObjectGuid $i.ObjectGuid -Id $i.Id -Kind $i.Kind).Exists) { Remove-KTBcdElementRaw -ObjectGuid $i.ObjectGuid -Id $i.Id } } function Test-KTBcdRestored { param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) if ([bool]$Before.Exists -ne [bool]$Now.Exists) { return $false } if (-not $Before.Exists) { return $true } return ([string]$Before.Value -eq [string]$Now.Value) } function Test-KTBcdAction { <# .SYNOPSIS Saf. Oge izin listesinde, Deger ogenin turunde (veya $null = sil). #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() try { $i = Get-KTBcdElementInfo $Action } catch { $err.Add("Bcd: $($_.Exception.Message)"); return $err.ToArray() } if ((Get-KTField $Action Nesne)) { $err.Add("Bcd: Nesne yazilmaz; ogenin nesnesi sabit ($($i.Object))") } try { ConvertTo-KTBcdValue $i.Kind $Action.Deger | Out-Null } catch { $err.Add("Bcd: $($_.Exception.Message)") } return $err.ToArray() } # ============================================================== # Uygulama kaldirma islemi (Uygulama): Get / Test / Set / Check / Dogrula -- GERI ALINAMAZ # ============================================================== # Islem: @{ Tur = 'Uygulama'; Paket = 'Microsoft.BingWeather' } # Durum: @{ Exists; Kurulu (tum kullanicilarda PackageFullName listesi); Hazir (provisioned PackageName listesi) } # Yontem (CTT WinUtil ve Win11Debloat ile ayni): tum kullanicilardan Remove-AppxPackage -AllUsers, # yeni kullanicilara kurulmasin diye hazir paket (provisioned) Remove-AppxProvisionedPackage -Online -AllUsers. # Paket adi TAM eslesir, joker yok: '*Xbox*' gibi joker baska paketleri de siler (karar 20, DBL11). # Noktasiz ad (ucuncu taraf, Store katalogundaki kisa ad) icin iki bicim daha: "." (ornek # D5EA27B7.Duolingo-LearnLanguagesforFree) ve "." (ornek ActiproSoftwareLLC.562882FEEB491); diger parca noktasiz # olmali (Adem 24.10.2026). Noktali (Microsoft.X) adlar yalniz tam eslesir. Eslesme Test-KTAppxName ile. # Kurulu degilse NotApplicable: islem "Sistemde yok" diye atlanir. # Geri alinamaz: yedege yazilmaz, geri alma listesine girmez; ekranda "(Geri alinamaz)" yazar. # Dis komutlar ince sarmalayicilarda (Get-KTAppxRaw, Remove-KTAppxRaw); testler bunlari degistirir. # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Test-KTAppxName { <# .SYNOPSIS Saf. Paket adi (Name / DisplayName) tanimdaki ada uyuyor mu (buyuk/kucuk harf duyarsiz). #> param([AllowEmptyString()][string]$PaketAdi, [Parameter(Mandatory)][string]$Ad) if ([string]::Equals($PaketAdi, $Ad, [StringComparison]::OrdinalIgnoreCase)) { return $true } if ($Ad.Contains('.')) { return $false } $e = [regex]::Escape($Ad) return [regex]::IsMatch($PaketAdi, "^([^.]+\.$e|$e\.[^.]+)$", 'IgnoreCase, CultureInvariant') } # Hazir (provisioned) paket listesi bir kez okunur (her okuma birkac saniye; 140+ uygulama tweak'i); kaldirmadan sonra silinir. $script:KT_HazirListe = $null function Get-KTAppxHazirListe { <# .SYNOPSIS Salt okunur (yonetici). @(@{ DisplayName; PackageName }) onbellekten. #> if ($null -eq $script:KT_HazirListe) { $script:KT_HazirListe = @(Get-AppxProvisionedPackage -Online -ErrorAction Stop | ForEach-Object { [pscustomobject]@{ DisplayName = [string]$_.DisplayName; PackageName = [string]$_.PackageName } }) } return $script:KT_HazirListe } function Get-KTAppxRaw { <# .SYNOPSIS Salt okunur (yonetici). @{ Kurulu = PackageFullName listesi; Hazir = provisioned PackageName listesi } #> param([Parameter(Mandatory)][string]$Name) $adlar = if ($Name.Contains('.')) { @($Name) } else { @($Name, "*.$Name", "$Name.*") } $bulunan = @(foreach ($a in $adlar) { Get-AppxPackage -AllUsers -Name $a -ErrorAction Stop }) $inst = @($bulunan | Where-Object { Test-KTAppxName $_.Name $Name } | ForEach-Object { $_.PackageFullName } | Sort-Object -Unique) $prov = @(Get-KTAppxHazirListe | Where-Object { Test-KTAppxName $_.DisplayName $Name } | ForEach-Object { $_.PackageName }) return [pscustomobject]@{ Kurulu = $inst; Hazir = $prov } } function Remove-KTAppxRaw { <# .SYNOPSIS Paketleri tum kullanicilardan ve hazir paketlerden kaldirir. Remove-AppxPackage -AllUsers hazir paketi de kaldirabiliyor; sonraki Remove-AppxProvisionedPackage "The system cannot find the path specified" veriyor (21.10.2026 Windows CI). Hata gelince paket gercekten gitmisse yok sayilir (Win11Debloat'in Win10 0x80070002 yaklasimi); hala varsa hata firlatilir. #> param([AllowEmptyCollection()][string[]]$Kurulu = @(), [AllowEmptyCollection()][string[]]$Hazir = @()) $script:KT_HazirListe = $null foreach ($p in $Kurulu) { try { Remove-AppxPackage -Package $p -AllUsers -ErrorAction Stop } catch { if (@(Get-AppxPackage -AllUsers -ErrorAction Stop | Where-Object { $_.PackageFullName -eq $p }).Count -gt 0) { throw } } } foreach ($p in $Hazir) { $kalan = @(Get-AppxProvisionedPackage -Online -ErrorAction Stop | Where-Object { $_.PackageName -eq $p }) if ($kalan.Count -eq 0) { continue } # kurulu paketle birlikte kaldirildi try { Remove-AppxProvisionedPackage -Online -AllUsers -PackageName $p -ErrorAction Stop | Out-Null } catch { if (@(Get-AppxProvisionedPackage -Online -ErrorAction Stop | Where-Object { $_.PackageName -eq $p }).Count -gt 0) { throw } } } } # ---------------- Get / Test / Set ---------------- function Get-KTAppxState { <# .SYNOPSIS Salt okunur. #> param([Parameter(Mandatory)] $Action) $raw = Get-KTAppxRaw ([string]$Action.Paket) $k = @($raw.Kurulu | Where-Object { $_ }); $h = @($raw.Hazir | Where-Object { $_ }) return [pscustomobject]@{ Exists = ($k.Count + $h.Count -gt 0); Kurulu = $k; Hazir = $h } } function Test-KTAppxState { <# .SYNOPSIS Saf. Kurulu veya hazir paket yoksa NotApplicable (sistemde yok), varsa NotApplied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if ($State.Exists) { return "NotApplied" } else { return "NotApplicable" } } function Set-KTAppx { param([Parameter(Mandatory)] $Action) $st = Get-KTAppxState $Action Remove-KTAppxRaw -Kurulu $st.Kurulu -Hazir $st.Hazir } function Test-KTAppxVerify { <# .SYNOPSIS Saf. Set sonrasi: hicbir kullanicida ve hazir paketlerde kalmadiysa basarili. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) return [pscustomobject]@{ Ok = (-not $State.Exists); Not = ""; YenidenBaslat = "Yok" } } function Test-KTAppxAction { <# .SYNOPSIS Saf. Paket tam ad: joker ve yol karakteri yok. #> param([Parameter(Mandatory)] $Action) $p = [string]$Action.Paket if ([string]::IsNullOrEmpty($p) -or $p -match '[\*\?\\/\s]') { return @("Uygulama: Paket tam ad olmali (joker, bosluk, yol yok)") } return @() } # ============================================================== # Dosya temizligi (Temizlik) ve DNS onbellegi (DnsOnbellek): geri alinamaz islemler # ============================================================== # Temizlik: @{ Tur = 'Temizlik'; Yer = 'KullaniciTemp'; EskiGun = 1 } EskiGun istege bagli (verilmezse hepsi) # Yol tanimda yazilmaz: yalniz $script:KT_TemizlikYerleri listesindeki adlar kabul edilir (yanlis tanim baska klasoru # silemesin). Klasorun kendisi silinmez; junction/symlink alt klasorlerine girilmez; kullanimdaki dosya atlanir. # Klasorun kendisi baglanti (junction/symlink) ise temizlik yapilmaz; her silmeden once dosyanin klasorden itibaren yolunda # baglanti olmadigi yeniden denetlenir (klasor kullanicinin yazabildigi bir yer; yonetici yetkisiyle baska yer silinmesin). # Kullanici Temp'i TMP/TEMP ortam degiskeninden degil, profilin yerel uygulama verisi klasorunden alinir. # Durum: @{ Exists (klasor var); Yol; Sayi; Bayt } silinecek dosyalar. Klasor yoksa NotApplicable, dosya yoksa Applied. # Set sonrasi silinemeyen dosya (kullanimda, izin yok) hata sayilmaz, sayisi sonuca not yazilir (Dogrula). # DnsOnbellek: @{ Tur = 'DnsOnbellek' } Clear-DnsClientCache. Onbellek hemen yeniden dolar; her zaman NotApplied. # Ikisi de geri alinamaz (Irreversible): yedege yazilmaz, ekranda "(Geri alinamaz)". # Dis cagrilar ince sarmalayicilarda (Get-KTTemizlikDosyaRaw, Remove-KTTemizlikDosyaRaw, Remove-KTBosKlasorRaw, Clear-KTDnsOnbellekRaw). # Ad -> klasor. Shader yollari Adem'in PC'sinde goruldu (katalog N11); AMD/Intel yollari dogrulanmadigi icin yok. $script:KT_TemizlikYerleri = @{ KullaniciTemp = { Join-Path ([Environment]::GetFolderPath("LocalApplicationData")) "Temp" } WindowsTemp = { Join-Path ([Environment]::GetFolderPath("Windows")) "Temp" } D3DSCache = { Join-Path ([Environment]::GetFolderPath("LocalApplicationData")) "D3DSCache" } NvidiaDXCache = { Join-Path ([Environment]::GetFolderPath("LocalApplicationData")) "NVIDIA\DXCache" } NvidiaGLCache = { Join-Path ([Environment]::GetFolderPath("LocalApplicationData")) "NVIDIA\GLCache" } } function Resolve-KTTemizlikYolu { <# .SYNOPSIS Listedeki adi tam klasor yoluna cevirir. Listede yoksa, yol bossa veya surucu koku ise hata. #> param([Parameter(Mandatory)][string]$Yer) if (-not $script:KT_TemizlikYerleri.ContainsKey($Yer)) { throw "Listede olmayan temizlik yeri: $Yer" } $p = [string](& $script:KT_TemizlikYerleri[$Yer]) if ([string]::IsNullOrWhiteSpace($p)) { throw "Temizlik yolu bos: $Yer" } $tam = [IO.Path]::GetFullPath($p).TrimEnd('\') if ($tam.Length -le 3) { throw "Surucu kokunde temizlik yapilmaz: $tam" } if ([IO.Directory]::Exists($tam) -and ([IO.File]::GetAttributes($tam) -band [IO.FileAttributes]::ReparsePoint)) { throw "Temizlik klasoru baglanti (junction/symlink), temizlik yapilmaz: $tam" } return $tam } function Get-KTTemizlikSiniri { <# .SYNOPSIS Saf. Bu zamandan once degismis dosyalar silinir; EskiGun yoksa simdi (hepsi). #> param([Parameter(Mandatory)] $Action, [datetime]$Simdi = (Get-Date)) $g = Get-KTField $Action EskiGun if ($null -eq $g) { return $Simdi } return $Simdi.AddDays(-[int]$g) } # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTTemizlikDosyaRaw { <# .SYNOPSIS Salt okunur. Klasordeki, Sinir'dan eski dosyalar: @{ Dosyalar = @(@{ Yol; Boyut }) }; klasor yoksa $null. Liste nesne icinde doner (bos dizi $null'a donusup "klasor yok" sanilmasin). Junction/symlink alt klasorlerine girilmez. #> param([Parameter(Mandatory)][string]$Yol, [Parameter(Mandatory)][datetime]$Sinir) if (-not [IO.Directory]::Exists($Yol)) { return $null } $out = [System.Collections.Generic.List[object]]::new() $yigin = [System.Collections.Generic.Stack[IO.DirectoryInfo]]::new() $yigin.Push([IO.DirectoryInfo]::new($Yol)) while ($yigin.Count -gt 0) { $d = $yigin.Pop() try { foreach ($f in $d.EnumerateFiles()) { if ($f.LastWriteTime -lt $Sinir) { $out.Add([pscustomobject]@{ Yol = $f.FullName; Boyut = $f.Length }) } } foreach ($a in $d.EnumerateDirectories()) { if (-not ($a.Attributes -band [IO.FileAttributes]::ReparsePoint)) { $yigin.Push($a) } } } catch {} } return [pscustomobject]@{ Dosyalar = $out.ToArray() } } function Remove-KTTemizlikDosyaRaw { <# .SYNOPSIS Dosyayi siler. Kok verilirse once dosya Kok altinda mi ve aradaki klasorlerde baglanti var mi denetlenir. #> param([Parameter(Mandatory)][string]$Yol, [string]$Kok) if ($Kok) { $k = [IO.Path]::GetFullPath($Kok).TrimEnd('\') + '\' $d = [IO.Path]::GetDirectoryName([IO.Path]::GetFullPath($Yol)) if (-not ($d + '\').StartsWith($k, [StringComparison]::OrdinalIgnoreCase)) { throw "Temizlik klasoru disinda: $Yol" } while ($d.Length -ge $k.Length - 1) { if ([IO.File]::GetAttributes($d) -band [IO.FileAttributes]::ReparsePoint) { throw "Yolda baglanti var: $d" } $d = [IO.Path]::GetDirectoryName($d) } } [IO.File]::Delete($Yol) } function Remove-KTBosKlasorRaw { <# .SYNOPSIS Kok altindaki bos klasorleri derinden yukariya siler; kok kalir, junction'a girilmez, hata yutulur. #> param([Parameter(Mandatory)][string]$Kok) if (-not [IO.Directory]::Exists($Kok)) { return } $hepsi = [System.Collections.Generic.List[IO.DirectoryInfo]]::new() $yigin = [System.Collections.Generic.Stack[IO.DirectoryInfo]]::new() $yigin.Push([IO.DirectoryInfo]::new($Kok)) while ($yigin.Count -gt 0) { $d = $yigin.Pop() try { foreach ($a in $d.EnumerateDirectories()) { if (-not ($a.Attributes -band [IO.FileAttributes]::ReparsePoint)) { $hepsi.Add($a); $yigin.Push($a) } } } catch {} } for ($i = $hepsi.Count - 1; $i -ge 0; $i--) { try { if (-not $hepsi[$i].EnumerateFileSystemInfos().GetEnumerator().MoveNext()) { $hepsi[$i].Delete() } } catch {} } } function Clear-KTDnsOnbellekRaw { Clear-DnsClientCache -ErrorAction Stop } # ---------------- Temizlik ---------------- function Get-KTTemizlikState { param([Parameter(Mandatory)] $Action) $yol = Resolve-KTTemizlikYolu ([string]$Action.Yer) $raw = Get-KTTemizlikDosyaRaw -Yol $yol -Sinir (Get-KTTemizlikSiniri $Action) if ($null -eq $raw) { return [pscustomobject]@{ Exists = $false; Yol = $yol; Sayi = 0; Bayt = 0 } } $d = @($raw.Dosyalar); $b = 0L; foreach ($f in $d) { $b += [long]$f.Boyut } return [pscustomobject]@{ Exists = $true; Yol = $yol; Sayi = $d.Count; Bayt = $b } } function Test-KTTemizlikState { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if (-not $State.Exists) { return "NotApplicable" } if ($State.Sayi -eq 0) { return "Applied" } else { return "NotApplied" } } function Set-KTTemizlik { <# .SYNOPSIS Eski dosyalari siler (kullanimdaki atlanir), sonra bos alt klasorleri. #> param([Parameter(Mandatory)] $Action) $yol = Resolve-KTTemizlikYolu ([string]$Action.Yer) $raw = Get-KTTemizlikDosyaRaw -Yol $yol -Sinir (Get-KTTemizlikSiniri $Action) foreach ($f in @(if ($raw) { $raw.Dosyalar })) { try { Remove-KTTemizlikDosyaRaw -Yol $f.Yol -Kok $yol } catch {} } Remove-KTBosKlasorRaw $yol } function Test-KTTemizlikVerify { <# .SYNOPSIS Saf. Silinemeyen dosya hata degil; sayisi nota yazilir (neden: kullanimda veya izin yok). #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) $not = if ($State.Exists -and $State.Sayi -gt 0) { "($($State.Sayi) dosya silinemedi; kullanimda veya izin yok)" } else { "" } return [pscustomobject]@{ Ok = $true; Not = $not; YenidenBaslat = "Yok" } } function Test-KTTemizlikAction { <# .SYNOPSIS Saf. Yer listede olmali; EskiGun verilmisse 0 veya pozitif tam sayi. #> param([Parameter(Mandatory)] $Action) $err = [System.Collections.Generic.List[string]]::new() if (-not $script:KT_TemizlikYerleri.ContainsKey([string]$Action.Yer)) { $err.Add("Temizlik: Yer listede olmali ($(($script:KT_TemizlikYerleri.Keys | Sort-Object) -join ', '))") } $g = Get-KTField $Action EskiGun if ($null -ne $g -and -not ($g -is [int] -and $g -ge 0)) { $err.Add("Temizlik: EskiGun 0 veya pozitif tam sayi") } return $err.ToArray() } # ---------------- DnsOnbellek ---------------- function Get-KTDnsOnbellekState { param([Parameter(Mandatory)] $Action) return [pscustomobject]@{ Exists = $true } } function Test-KTDnsOnbellekState { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) return "NotApplied" } function Set-KTDnsOnbellek { param([Parameter(Mandatory)] $Action) Clear-KTDnsOnbellekRaw } function Test-KTDnsOnbellekVerify { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) return [pscustomobject]@{ Ok = $true; Not = ""; YenidenBaslat = "Yok" } } function Test-KTDnsOnbellekAction { param([Parameter(Mandatory)] $Action) return @() } # ============================================================== # Dosya erisim engeli (DosyaEngel) ve BitLocker sifre cozme (BitLocker) # ============================================================== # DosyaEngel: @{ Tur = 'DosyaEngel'; Yer = 'StoreDb' } # Dosyaya "Herkes (S-1-1-0): Tam denetim REDDET" kurali eklenir; Windows dosyayi okuyup yazamaz. Dosya yoksa bos olusturulur # (Windows sonradan olusturmasin). Yol tanimda yazilmaz: yalniz $script:KT_EngelYerleri listesindeki adlar; Expand somut # yolu Yol alanina yazar (yedege somut yol girer, geri alma ayni dosyaya yapilir). # Geri alma: once sahiplik Yoneticiler'e alinir (takeown /A) ve Yoneticiler'e tam izin verilir; reddet kurali kaldirilir; # dosya onceden yoksa silinir. Dosya onceden varsa ve guvenlik bilgisi okunabildiyse onceki sahip ve izinler geri kurulur. # Engel = $false (istege bagli, karar 97): engel kaldirilir (ayni yontemle; dosya silinmez). Geri alma onceki sahip ve # izinleri (reddet kurali dahil) geri kurar; okunamadiysa yalniz engeli yeniden ekler. # Durum: @{ Exists (dosya var); Engelli (Herkes reddet kurali var); Sahip (sahip SID); Dacl (izinler, SDDL) } # Sahip ve Dacl okunamazsa $null. # BitLocker: @{ Tur = 'BitLocker' } sistem surucusunun sifresi cozulur (Disable-BitLocker). Geri alinamaz: yeniden sifreleme # kurtarma anahtari ayari ister, elle yapilir. Sifre cozme arka planda surer; Set sonrasi "suruyor" hata degildir. # Durum: @{ Exists (BitLocker bilgisi okunabildi); Durum (VolumeStatus metni) }. FullyDecrypted -> Applied. # Dis cagrilar ince sarmalayicilarda; testler bunlari degistirir. $script:KT_EngelYerleri = @{ StoreDb = { Join-Path ([Environment]::GetFolderPath("LocalApplicationData")) "Packages\Microsoft.WindowsStore_8wekyb3d8bbwe\LocalState\store.db" } } $script:KT_Herkes = 'S-1-1-0' # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Test-KTDosyaEngelRaw { <# .SYNOPSIS Salt okunur. @{ Exists; Engelli; Sahip; Dacl } . #> param([Parameter(Mandatory)][string]$Yol) if (-not [IO.File]::Exists($Yol)) { return [pscustomobject]@{ Exists = $false; Engelli = $false; Sahip = $null; Dacl = $null } } $engel = $false; $sahip = $null; $dacl = $null try { $acl = Get-Acl -LiteralPath $Yol -ErrorAction Stop try { $sahip = $acl.GetOwner([Security.Principal.SecurityIdentifier]).Value; $dacl = $acl.GetSecurityDescriptorSddlForm('Access') } catch { $sahip = $null; $dacl = $null } foreach ($k in $acl.Access) { if ($k.AccessControlType -ne 'Deny') { continue } if (($k.FileSystemRights -band [Security.AccessControl.FileSystemRights]::FullControl) -ne [Security.AccessControl.FileSystemRights]::FullControl) { continue } try { if ($k.IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value -eq $script:KT_Herkes) { $engel = $true } } catch {} } } catch { $engel = $true } # ACL okunamiyorsa engel var sayilir (reddet kurali okumayi da engeller) return [pscustomobject]@{ Exists = $true; Engelli = $engel; Sahip = $sahip; Dacl = $dacl } } function Set-KTDosyaGuvenlikRaw { <# .SYNOPSIS Onceki sahibi (icacls /setowner) ve izinleri (SDDL) geri kurar. Once sahip: o sirada Yoneticiler'in tam izni var. #> param([Parameter(Mandatory)][string]$Yol, [Parameter(Mandatory)][string]$Sahip, [Parameter(Mandatory)][string]$Dacl) $o = & icacls.exe $Yol /setowner "*$Sahip" /C 2>&1 if ($LASTEXITCODE -ne 0) { throw "icacls /setowner basarisiz ($LASTEXITCODE): $(($o | Out-String).Trim())" } $acl = Get-Acl -LiteralPath $Yol -ErrorAction Stop $acl.SetSecurityDescriptorSddlForm($Dacl, 'Access') Set-Acl -LiteralPath $Yol -AclObject $acl -ErrorAction Stop } function Add-KTDosyaEngelRaw { param([Parameter(Mandatory)][string]$Yol) if (-not [IO.File]::Exists($Yol)) { $kl = Split-Path -Path $Yol -Parent if (-not [IO.Directory]::Exists($kl)) { New-Item -Path $kl -ItemType Directory -Force -ErrorAction Stop | Out-Null } New-Item -Path $Yol -ItemType File -Force -ErrorAction Stop | Out-Null } $acl = Get-Acl -LiteralPath $Yol -ErrorAction Stop $acl.SetAccessRule([Security.AccessControl.FileSystemAccessRule]::new([Security.Principal.SecurityIdentifier]::new($script:KT_Herkes), 'FullControl', 'Deny')) | Out-Null Set-Acl -LiteralPath $Yol -AclObject $acl -ErrorAction Stop } function Remove-KTDosyaEngelRaw { <# .SYNOPSIS Herkes reddet kuralini kaldirir; Sil ise dosyayi da siler. #> param([Parameter(Mandatory)][string]$Yol, [bool]$Sil = $false) if (-not [IO.File]::Exists($Yol)) { return } $o = & takeown.exe /F $Yol /A 2>&1 if ($LASTEXITCODE -ne 0) { throw "takeown basarisiz ($LASTEXITCODE): $(($o | Out-String).Trim())" } $o = & icacls.exe $Yol /grant '*S-1-5-32-544:F' /C 2>&1 if ($LASTEXITCODE -ne 0) { throw "icacls basarisiz ($LASTEXITCODE): $(($o | Out-String).Trim())" } $acl = Get-Acl -LiteralPath $Yol -ErrorAction Stop foreach ($k in @($acl.Access | Where-Object { $_.AccessControlType -eq 'Deny' })) { try { if ($k.IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value -eq $script:KT_Herkes) { [void]$acl.RemoveAccessRuleSpecific($k) } } catch {} } Set-Acl -LiteralPath $Yol -AclObject $acl -ErrorAction Stop if ($Sil) { Remove-Item -LiteralPath $Yol -Force -ErrorAction Stop } } function Get-KTBitLockerRaw { <# .SYNOPSIS Salt okunur (yonetici). Sistem surucusunun VolumeStatus metni veya $null (BitLocker modulu/bilgi yok). #> if (-not (Get-Command Get-BitLockerVolume -ErrorAction SilentlyContinue)) { return $null } $v = Get-BitLockerVolume -MountPoint $env:SystemDrive -ErrorAction SilentlyContinue if ($null -eq $v) { return $null } return [string]$v.VolumeStatus } function Disable-KTBitLockerRaw { Disable-BitLocker -MountPoint $env:SystemDrive -ErrorAction Stop | Out-Null } # ---------------- DosyaEngel ---------------- function Expand-KTDosyaEngel { <# .SYNOPSIS Listedeki yeri somut yola cevirir (Yol). Yol verilmisse (yedekten gelen) oldugu gibi. #> param([Parameter(Mandatory)] $Action, $Sistem = $null) if (Get-KTField $Action Yol) { return $Action } $c = Copy-KTAction $Action $c.Yol = [string](& $script:KT_EngelYerleri[[string]$Action.Yer]) return $c } function Get-KTDosyaEngelState { param([Parameter(Mandatory)] $Action) Test-KTDosyaEngelRaw ([string]$Action.Yol) } function Get-KTDosyaEngelHedef { param([Parameter(Mandatory)] $Action) return ((Get-KTField $Action Engel) -ne $false) } function Test-KTDosyaEngelState { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if ([bool]$State.Engelli -eq (Get-KTDosyaEngelHedef $Action)) { return "Applied" } else { return "NotApplied" } } function Set-KTDosyaEngel { param([Parameter(Mandatory)] $Action) if (Get-KTDosyaEngelHedef $Action) { Add-KTDosyaEngelRaw ([string]$Action.Yol) } else { Remove-KTDosyaEngelRaw -Yol ([string]$Action.Yol) -Sil $false } } function Undo-KTDosyaEngel { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) $hedef = Get-KTDosyaEngelHedef $Action if ([bool]$Before.Engelli -eq $hedef) { return } $guvenlik = [bool]$Before.Exists -and (Get-KTField $Before Sahip) -and (Get-KTField $Before Dacl) if ($hedef) { Remove-KTDosyaEngelRaw -Yol ([string]$Action.Yol) -Sil (-not $Before.Exists) } elseif (-not $guvenlik) { Add-KTDosyaEngelRaw ([string]$Action.Yol) } if ($guvenlik) { Set-KTDosyaGuvenlikRaw -Yol ([string]$Action.Yol) -Sahip ([string]$Before.Sahip) -Dacl ([string]$Before.Dacl) } } function Test-KTDosyaEngelRestored { param([Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) $ok = if ($Before.Engelli) { [bool]$Now.Engelli } else { (-not $Now.Engelli -and [bool]$Now.Exists -eq [bool]$Before.Exists) } # Sahip okunabiliyorsa onceki sahiple ayni olmali (reddet kurali okumayi engellediyse yalniz engel denetlenir) if ($ok -and $Before.Exists -and (Get-KTField $Before Sahip) -and (Get-KTField $Now Sahip)) { $ok = ([string]$Now.Sahip -eq [string]$Before.Sahip) } return $ok } function Test-KTDosyaEngelAction { param([Parameter(Mandatory)] $Action) if (-not $script:KT_EngelYerleri.ContainsKey([string]$Action.Yer)) { return @("DosyaEngel: Yer listede olmali ($(($script:KT_EngelYerleri.Keys | Sort-Object) -join ', '))") } if (Get-KTField $Action Yol) { return @("DosyaEngel: Yol tanimda yazilmaz") } $e = Get-KTField $Action Engel if ($null -ne $e -and $e -isnot [bool]) { return @("DosyaEngel: Engel `$true veya `$false olmali") } return @() } # ---------------- BitLocker ---------------- function Get-KTBitLockerState { param([Parameter(Mandatory)] $Action) $d = Get-KTBitLockerRaw return [pscustomobject]@{ Exists = ($null -ne $d); Durum = $d } } function Test-KTBitLockerState { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if (-not $State.Exists) { return "NotApplicable" } if ([string]$State.Durum -in "FullyDecrypted", "DecryptionInProgress") { return "Applied" } return "NotApplied" } function Set-KTBitLocker { param([Parameter(Mandatory)] $Action) Disable-KTBitLockerRaw } function Test-KTBitLockerVerify { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) $ok = [string]$State.Durum -in "FullyDecrypted", "DecryptionInProgress" $not = if ([string]$State.Durum -eq "DecryptionInProgress") { "(Sifre cozme arka planda suruyor)" } else { "" } return [pscustomobject]@{ Ok = $ok; Not = $not; YenidenBaslat = "Yok" } } function Test-KTBitLockerAction { param([Parameter(Mandatory)] $Action) return @() } # ============================================================== # OneDrive kaldirma (OneDrive): Get / Test / Set / Check / Dogrula -- GERI ALINAMAZ # ============================================================== # Islem: @{ Tur = 'OneDrive' } # OneDrive Store paketi degil, kendi kaldiricisi var (CTT WinUtil ve Atlas yontemi: OneDriveSetup.exe /uninstall). # Kaldirici komutu once kayittaki UninstallString'den (kullanici: HKCU, makine: HKLM ve WOW6432Node), yoksa # System32 / SysWOW64\OneDriveSetup.exe + /uninstall. Kullanicinin OneDrive klasorune ve dosyalarina dokunulmaz. # Kayit degeri kullanicinin degistirebildigi bir yerde (HKCU): yalniz adi OneDriveSetup.exe olan ve Microsoft imzasi # gecerli dosya calistirilir; degilse Set hata verir (yonetici yetkisiyle baska program calismasin). # Durum: @{ Exists (OneDrive kurulu); Komut; Arg }. Kurulu degilse NotApplicable. # Yeniden kurulum elle (Store veya winget); geri alinamaz, yedege yazilmaz. # Dis cagrilar ince sarmalayicilarda (Get-KTOneDriveRaw, Invoke-KTOneDriveUninstallRaw); testler bunlari degistirir. function Split-KTKomut { <# .SYNOPSIS Saf. '"C:\yol\a.exe" /x /y' veya 'C:\yol\a.exe /x' -> @{ Dosya; Arg }. #> param([AllowEmptyString()][string]$Satir) $t = ([string]$Satir).Trim() $m = [regex]::Match($t, '^"([^"]+)"\s*(.*)$') if ($m.Success) { return [pscustomobject]@{ Dosya = $m.Groups[1].Value; Arg = $m.Groups[2].Value.Trim() } } $m = [regex]::Match($t, '^(.+?\.exe)\s*(.*)$', 'IgnoreCase') if ($m.Success) { return [pscustomobject]@{ Dosya = $m.Groups[1].Value; Arg = $m.Groups[2].Value.Trim() } } return [pscustomobject]@{ Dosya = $t; Arg = "" } } # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTOneDriveRaw { <# .SYNOPSIS Salt okunur. Kaldirici komut satiri veya $null (OneDrive kurulu degil). #> foreach ($k in 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\OneDriveSetup.exe', 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\OneDriveSetup.exe', 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\OneDriveSetup.exe') { $p = Get-ItemProperty -LiteralPath $k -Name UninstallString -ErrorAction SilentlyContinue if ($p -and $p.UninstallString) { return [string]$p.UninstallString } } $exe = @((Join-Path ([Environment]::GetFolderPath("LocalApplicationData")) "Microsoft\OneDrive\OneDrive.exe"), (Join-Path ([Environment]::GetFolderPath("ProgramFiles")) "Microsoft OneDrive\OneDrive.exe")) | Where-Object { [IO.File]::Exists($_) } if (-not $exe) { return $null } foreach ($s in "System32", "SysWOW64") { $k = Join-Path ([Environment]::GetFolderPath("Windows")) "$s\OneDriveSetup.exe" if ([IO.File]::Exists($k)) { return "`"$k`" /uninstall" } } return $null } function Test-KTOneDriveImzaRaw { <# .SYNOPSIS Salt okunur. Dosyanin Authenticode imzasi gecerli ve imzalayan Microsoft Corporation mi. #> param([Parameter(Mandatory)][string]$Dosya) if (-not [IO.File]::Exists($Dosya)) { return $false } try { $i = Get-AuthenticodeSignature -LiteralPath $Dosya -ErrorAction Stop } catch { return $false } if ($i.Status -ne 'Valid' -or -not $i.SignerCertificate) { return $false } return ($i.SignerCertificate.Subject.Contains('O=Microsoft Corporation')) } function Invoke-KTOneDriveUninstallRaw { param([Parameter(Mandatory)][string]$Dosya, [string]$Arg) Get-Process -Name OneDrive -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue $prm = @{ FilePath = $Dosya; Wait = $true; PassThru = $true; ErrorAction = "Stop" } if ($Arg) { $prm.ArgumentList = $Arg } $p = Start-Process @prm if ($p.ExitCode -ne 0) { throw "OneDrive kaldirici cikis kodu $($p.ExitCode)" } } # ---------------- Get / Test / Set ---------------- function Get-KTOneDriveState { param([Parameter(Mandatory)] $Action) $k = Get-KTOneDriveRaw if (-not $k) { return [pscustomobject]@{ Exists = $false; Komut = $null; Arg = $null } } $s = Split-KTKomut $k return [pscustomobject]@{ Exists = $true; Komut = $s.Dosya; Arg = $s.Arg } } function Test-KTOneDriveState { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) if ($State.Exists) { return "NotApplied" } else { return "NotApplicable" } } function Set-KTOneDrive { param([Parameter(Mandatory)] $Action) $st = Get-KTOneDriveState $Action if (-not $st.Exists) { return } $ad = [IO.Path]::GetFileName([string]$st.Komut) if (-not [string]::Equals($ad, "OneDriveSetup.exe", [StringComparison]::OrdinalIgnoreCase) -or -not (Test-KTOneDriveImzaRaw -Dosya $st.Komut)) { throw "OneDrive kaldiricisi dogrulanamadi (Microsoft imzali OneDriveSetup.exe degil): $($st.Komut)" } $arg = if ($st.Arg) { $st.Arg } else { "/uninstall" } Invoke-KTOneDriveUninstallRaw -Dosya $st.Komut -Arg $arg } function Test-KTOneDriveVerify { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) return [pscustomobject]@{ Ok = (-not $State.Exists); Not = ""; YenidenBaslat = "Yok" } } function Test-KTOneDriveAction { param([Parameter(Mandatory)] $Action) return @() } # ============================================================== # Islem turleri: tur adina gore Get / Test / Set / Undo / Restored / Check yonlendirmesi # ============================================================== # Get : salt okunur durum. Test : saf; Applied / NotApplied / NotApplicable (sistemde yok). # Set : hedefi yazar. Undo : yedekteki onceki durumu kurar. # Restored: saf; geri almadan sonra durum onceki durumla uyusuyor mu. # Irreversible: geri alinamaz tur (uygulama kaldirma); Undo/Restored yok, yedege yazilmaz. Islemde GeriAlinamaz = $true da ayni. # Expand : saf; tanimdaki islemi sistemdeki her uygun cihaza acar (ag karti, aygit; RegValue yolunda {AgKarti}); motor Get'ten once cagirir. # Check : saf; tanimdaki islem alanlarini denetler (hata metinleri). # Atla : saf; Get'ten sonra, yazilacak islem o an yazilamiyorsa ekran metni (ornek: guc modu pildeyken); islem atlanir. # Yeni tur eklemek: fonksiyonlari yazip bu tabloya bir satir eklemek. $script:KT_ActionHandlers = @{ RegValue = @{ Get = "Get-KTRegValueState"; Test = "Test-KTRegValueState"; Set = "Set-KTRegValue" Undo = "Undo-KTRegValue"; Restored = "Test-KTRegRestored"; Check = "Test-KTRegValueAction" Expand = "Expand-KTRegValue" } RegValueDelete = @{ Get = "Get-KTRegValueState"; Test = "Test-KTRegValueDeleteState"; Set = "Set-KTRegValueDelete" Undo = "Undo-KTRegValue"; Restored = "Test-KTRegRestored"; Check = "Test-KTRegValueDeleteAction" Expand = "Expand-KTRegValue" } RegKeyDelete = @{ Get = "Get-KTRegKeyState"; Test = "Test-KTRegKeyState"; Set = "Set-KTRegKeyDelete" Undo = "Undo-KTRegKeyDelete"; Restored = "Test-KTRegKeyRestored"; Check = "Test-KTRegKeyDeleteAction" } AyarSayfasi = @{ Get = "Get-KTPageState"; Test = "Test-KTPageState"; Set = "Set-KTPage" Undo = "Undo-KTPage"; Restored = "Test-KTPageRestored"; Check = "Test-KTPageAction" } DizeAlani = @{ Get = "Get-KTDizeState"; Test = "Test-KTDizeState"; Set = "Set-KTDize" Undo = "Undo-KTDize"; Restored = "Test-KTDizeRestored"; Check = "Test-KTDizeAction" } Servis = @{ Get = "Get-KTServiceState"; Test = "Test-KTServiceState"; Set = "Set-KTService" Undo = "Undo-KTService"; Restored = "Test-KTServiceRestored"; Check = "Test-KTServiceAction"; Dogrula = "Test-KTServiceVerify" } HazirdaBekletme = @{ Get = "Get-KTHibernateState"; Test = "Test-KTOnOffState"; Set = "Set-KTHibernate" Undo = "Undo-KTHibernate"; Restored = "Test-KTOnOffRestored"; Check = "Test-KTOnOffAction" } AyrilmisDepolama = @{ Get = "Get-KTReservedStorageState"; Test = "Test-KTOnOffState"; Set = "Set-KTReservedStorage" Undo = "Undo-KTReservedStorage"; Restored = "Test-KTOnOffRestored"; Check = "Test-KTOnOffAction" } Ozellik = @{ Get = "Get-KTFeatureState"; Test = "Test-KTFeatureState"; Set = "Set-KTFeature" Undo = "Undo-KTFeature"; Restored = "Test-KTFeatureRestored"; Check = "Test-KTFeatureAction"; Dogrula = "Test-KTFeatureVerify" } Gorev = @{ Get = "Get-KTTaskState"; Test = "Test-KTTaskState"; Set = "Set-KTTask" Undo = "Undo-KTTask"; Restored = "Test-KTTaskRestored"; Check = "Test-KTTaskAction" } GucAyari = @{ Get = "Get-KTPowerSettingState"; Test = "Test-KTPowerSettingState"; Set = "Set-KTPowerSetting" Undo = "Undo-KTPowerSetting"; Restored = "Test-KTPowerSettingRestored"; Check = "Test-KTPowerSettingAction" Resolve = "Resolve-KTPowerSetting"; Atla = "Get-KTPowerSettingSkip" } GucPlaniSil = @{ Get = "Get-KTPowerPlanDeleteState"; Test = "Test-KTPowerPlanDeleteState"; Set = "Set-KTPowerPlanDelete" Undo = "Undo-KTPowerPlanDelete"; Restored = "Test-KTPowerPlanInstallRestored"; Check = "Test-KTPowerPlanDeleteAction" Resolve = "Resolve-KTPowerPlanDelete" } GucPlani = @{ Get = "Get-KTPowerPlanInstallState"; Test = "Test-KTPowerPlanInstallState"; Set = "Set-KTPowerPlanInstall" Undo = "Undo-KTPowerPlanInstall"; Restored = "Test-KTPowerPlanInstallRestored"; Check = "Test-KTPowerPlanInstallAction" } GucModu = @{ Get = "Get-KTPowerModeState"; Test = "Test-KTPowerModeState"; Set = "Set-KTPowerMode" Undo = "Undo-KTPowerMode"; Restored = "Test-KTPowerModeRestored"; Check = "Test-KTPowerModeAction" Atla = "Get-KTPowerModeSkip" } AktifPlan = @{ Get = "Get-KTActivePlanState"; Test = "Test-KTActivePlanState"; Set = "Set-KTActivePlan" Undo = "Undo-KTActivePlan"; Restored = "Test-KTActivePlanRestored"; Check = "Test-KTActivePlanAction" } Bcd = @{ Get = "Get-KTBcdState"; Test = "Test-KTBcdState"; Set = "Set-KTBcd" Undo = "Undo-KTBcd"; Restored = "Test-KTBcdRestored"; Check = "Test-KTBcdAction" } AgKarti = @{ Get = "Get-KTNicPropertyState"; Test = "Test-KTNicPropertyState"; Set = "Set-KTNicProperty" Undo = "Undo-KTNicProperty"; Restored = "Test-KTNicPropertyRestored"; Check = "Test-KTNicPropertyAction" Expand = "Expand-KTNicProperty" } CihazGuc = @{ Get = "Get-KTDevicePowerState"; Test = "Test-KTDevicePowerState"; Set = "Set-KTDevicePower" Undo = "Undo-KTDevicePower"; Restored = "Test-KTDevicePowerRestored"; Check = "Test-KTDevicePowerAction" Expand = "Expand-KTDevicePower" } Hdr = @{ Get = "Get-KTHdrState"; Test = "Test-KTOnOffState"; Set = "Set-KTHdr" Undo = "Undo-KTHdr"; Restored = "Test-KTOnOffRestored"; Check = "Test-KTHdrAction" Expand = "Expand-KTHdr" } Dns = @{ Get = "Get-KTDnsState"; Test = "Test-KTDnsState"; Set = "Set-KTDns" Undo = "Undo-KTDns"; Restored = "Test-KTDnsRestored"; Check = "Test-KTDnsAction" Expand = "Expand-KTNicProperty" } AgGenel = @{ Get = "Get-KTNetGlobalState"; Test = "Test-KTNetGlobalState"; Set = "Set-KTNetGlobal" Undo = "Undo-KTNetGlobal"; Restored = "Test-KTNetGlobalRestored"; Check = "Test-KTNetGlobalAction" } AgBaglama = @{ Get = "Get-KTNetBindingState"; Test = "Test-KTNetBindingState"; Set = "Set-KTNetBinding" Undo = "Undo-KTNetBinding"; Restored = "Test-KTNetBindingRestored"; Check = "Test-KTNetBindingAction" Expand = "Expand-KTNicProperty" } ZamanSunucusu = @{ Get = "Get-KTTimeState"; Test = "Test-KTTimeState"; Set = "Set-KTTime" Undo = "Undo-KTTime"; Restored = "Test-KTTimeRestored"; Check = "Test-KTTimeAction" Dogrula = "Test-KTTimeVerify"; Atla = "Get-KTTimeSkip" } Aygit = @{ Get = "Get-KTDeviceState"; Test = "Test-KTDeviceState"; Set = "Set-KTDevice" Undo = "Undo-KTDevice"; Restored = "Test-KTDeviceRestored"; Check = "Test-KTDeviceAction" Dogrula = "Test-KTDeviceVerify" } DosyaEngel = @{ Get = "Get-KTDosyaEngelState"; Test = "Test-KTDosyaEngelState"; Set = "Set-KTDosyaEngel" Undo = "Undo-KTDosyaEngel"; Restored = "Test-KTDosyaEngelRestored"; Check = "Test-KTDosyaEngelAction" Expand = "Expand-KTDosyaEngel" } # Geri alinamaz: Undo/Restored yok; motor yedege yazmaz ve geri almaya katmaz Temizlik = @{ Get = "Get-KTTemizlikState"; Test = "Test-KTTemizlikState"; Set = "Set-KTTemizlik" Check = "Test-KTTemizlikAction"; Dogrula = "Test-KTTemizlikVerify"; Irreversible = $true } DnsOnbellek = @{ Get = "Get-KTDnsOnbellekState"; Test = "Test-KTDnsOnbellekState"; Set = "Set-KTDnsOnbellek" Check = "Test-KTDnsOnbellekAction"; Dogrula = "Test-KTDnsOnbellekVerify"; Irreversible = $true } Trim = @{ Get = "Get-KTTrimState"; Test = "Test-KTTrimState"; Set = "Set-KTTrim" Check = "Test-KTTrimAction"; Dogrula = "Test-KTTrimVerify"; Irreversible = $true } OneDrive = @{ Get = "Get-KTOneDriveState"; Test = "Test-KTOneDriveState"; Set = "Set-KTOneDrive" Check = "Test-KTOneDriveAction"; Dogrula = "Test-KTOneDriveVerify"; Irreversible = $true } BitLocker = @{ Get = "Get-KTBitLockerState"; Test = "Test-KTBitLockerState"; Set = "Set-KTBitLocker" Check = "Test-KTBitLockerAction"; Dogrula = "Test-KTBitLockerVerify"; Irreversible = $true } Uygulama = @{ Get = "Get-KTAppxState"; Test = "Test-KTAppxState"; Set = "Set-KTAppx" Check = "Test-KTAppxAction"; Dogrula = "Test-KTAppxVerify"; Irreversible = $true } } function Test-KTActionIrreversible { <# .SYNOPSIS Saf. Islem geri alinamaz mi: turu geri alinamaz (uygulama kaldirma, temizlik) veya islemde GeriAlinamaz = $true (eski degeri yazmak etkiyi geri getirmiyor; ornek Akilli Uygulama Denetimi). Geri alinamaz islem yedege yazilmaz. #> param([Parameter(Mandatory)] $Action) return ([bool](Get-KTActionHandler $Action).Irreversible -or [bool](Get-KTField $Action GeriAlinamaz)) } function Test-KTTweakIrreversible { <# .SYNOPSIS Saf. Tweak'te geri alinamaz islem var mi (ekran "(Geri alinamaz)" yazar). #> param([Parameter(Mandatory)] $Tweak) foreach ($a in @($Tweak.Islemler)) { if (Test-KTActionIrreversible $a) { return $true } } return $false } function Resolve-KTAction { <# .SYNOPSIS Turun Resolve'u varsa islemin somut kopyasini, yoksa islemi oldugu gibi dondurur. Motor yedekten ONCE cagirir; yedege somut islem yazilir (ornek: 'Aktif' plan -> GUID). #> param([Parameter(Mandatory)] $Action) $h = Get-KTActionHandler $Action if ($h.Resolve) { return (& $h.Resolve -Action $Action) } return $Action } function Test-KTActionVerify { <# .SYNOPSIS Set sonrasi dogrulama: @{ Ok; Not; YenidenBaslat }. Turun Dogrula'si varsa o (ornek: servis durdurulamadi ama baslangic turu yazildi); yoksa Test = Applied. #> param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) $h = Get-KTActionHandler $Action if ($h.Dogrula) { return (& $h.Dogrula -Action $Action -State $State) } return [pscustomobject]@{ Ok = ((& $h.Test -Action $Action -State $State) -eq "Applied"); Not = ""; YenidenBaslat = "Yok" } } function Get-KTActionHandler { param([Parameter(Mandatory)] $Action) $h = $script:KT_ActionHandlers[[string](Get-KTField $Action Tur)] if (-not $h) { throw "Bilinmeyen islem turu: $(Get-KTField $Action Tur)" } return $h } function Get-KTActionState { param([Parameter(Mandatory)] $Action) & (Get-KTActionHandler $Action).Get -Action $Action } function Test-KTActionState { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $State) & (Get-KTActionHandler $Action).Test -Action $Action -State $State } function Invoke-KTActionSet { param([Parameter(Mandatory)] $Action) & (Get-KTActionHandler $Action).Set -Action $Action } function Undo-KTAction { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) & (Get-KTActionHandler $Action).Undo -Action $Action -Before $Before } function Test-KTActionRestored { param([Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before, [Parameter(Mandatory)] $Now) & (Get-KTActionHandler $Action).Restored -Before $Before -Now $Now } function Get-KTActionScope { <# .SYNOPSIS Saf. HKCU'ya yazan islem 'User' (o kullanicinin oturumunda geri alinir), digerleri 'Machine'. #> param([Parameter(Mandatory)] $Action) $path = [string](Get-KTField $Action Yol) # Yalniz kayit yolu (dosya yolu tasiyan turler Machine): HKCU: veya HKEY_CURRENT_USER ile baslayan if ($path -and [regex]::IsMatch($path, '^(HKCU:?|HKEY_CURRENT_USER)\\', 'IgnoreCase, CultureInvariant')) { return "User" } return "Machine" } function Format-KTActionTarget { <# .SYNOPSIS Saf. Islemin hedefi (log ve ekran icin). #> param([Parameter(Mandatory)] $Action) switch ([string](Get-KTField $Action Tur)) { { $_ -in "RegValue", "RegValueDelete" } { $h = ([string](Get-KTField $Action Yol) -replace ':', '') + "\" + [string](Get-KTField $Action Ad) $kart = [string](Get-KTField $Action KartAdi); if ($kart) { $h += " ($kart)" } return $h } "RegKeyDelete" { return ([string](Get-KTField $Action Yol) -replace ':', '') } "Servis" { return "Servis " + [string](Get-KTField $Action Ad) } "Ozellik" { return "Ozellik " + [string](Get-KTField $Action Ad) } "AyarSayfasi" { return "Ayarlar sayfasi " + [string](Get-KTField $Action Sayfa) } "DizeAlani" { return ([string](Get-KTField $Action Yol) -replace ':', '') + "\" + [string](Get-KTField $Action Ad) + " " + [string](Get-KTField $Action Alan) } "HazirdaBekletme" { return "Hazirda bekletme" } "AyrilmisDepolama" { return "Ayrilmis depolama" } "Gorev" { return "Gorev " + (ConvertTo-KTTaskPath ([string](Get-KTField $Action Klasor))) + [string](Get-KTField $Action Ad) } "GucAyari" { return "Guc " + [string](Get-KTField $Action Plan) + "\" + [string](Get-KTField $Action Ayar) } "GucPlani" { return "Guc plani " + [string](Get-KTField $Action Plan) + " (" + [string](Get-KTField $Action Ad) + ")" } "GucPlaniSil" { return "Guc plani sil " + [string](Get-KTField $Action Plan) } "ZamanSunucusu" { return "Zaman kaynagi " + ((@(Get-KTField $Action Sunucular)) -join ",") } "Aygit" { return "Aygit " + [string](Get-KTField $Action Kimlik) } "AgBaglama" { $kart = [string](Get-KTField $Action KartAdi); if (-not $kart) { $kart = [string](Get-KTField $Action Kart) } if (-not $kart) { $kart = "(kart)" } return "Ag baglamasi $kart " + [string](Get-KTField $Action Bilesen) } "GucModu" { return "Guc modu (prizde)" } "AktifPlan" { return "Etkin guc plani " + [string](Get-KTField $Action Plan) } "Bcd" { return "BCD " + [string](Get-KTField $Action Oge) } "Uygulama" { return "Uygulama " + [string](Get-KTField $Action Paket) } "Temizlik" { return "Temizlik " + [string](Get-KTField $Action Yer) } "DnsOnbellek" { return "DNS onbellegi" } "Trim" { return "TRIM (sistem surucusu)" } "BitLocker" { return "BitLocker (sistem surucusu)" } "OneDrive" { return "OneDrive" } "DosyaEngel" { $y = [string](Get-KTField $Action Yol); if (-not $y) { $y = [string](Get-KTField $Action Yer) }; return "Dosya erisim engeli $y" } "AgKarti" { $kart = [string](Get-KTField $Action KartAdi); if (-not $kart) { $kart = [string](Get-KTField $Action Kart) } if (-not $kart) { $kart = "(kart)" } return "Ag karti $kart " + [string](Get-KTField $Action Ozellik) } "AgGenel" { return "Ag (Windows geneli) " + [string](Get-KTField $Action Ozellik) } "Dns" { $kart = [string](Get-KTField $Action KartAdi); if (-not $kart) { $kart = [string](Get-KTField $Action Kart) } if (-not $kart) { $kart = "(kart)" } return "DNS $kart" } "CihazGuc" { $ad = [string](Get-KTField $Action AygitAdi); if (-not $ad) { $ad = [string](Get-KTField $Action Aygit) } if (-not $ad) { $ad = "(" + [string](Get-KTField $Action Cihaz) + ")" } return "Aygit guc yonetimi $ad" } "Hdr" { $ad = [string](Get-KTField $Action EkranAdi); if (-not $ad) { $ad = "(ekran)" } return "HDR $ad" } default { return "-" } } } function Format-KTStateText { <# .SYNOPSIS Saf. Durumu kisa metne cevirir (log ve ekran icin). #> param([Parameter(Mandatory)] $Action, $State) if ($null -eq $State) { return "-" } switch ([string](Get-KTField $Action Tur)) { { $_ -in "RegValue", "RegValueDelete" } { if ($State.Exists) { return ((@($State.Data) | ForEach-Object { [string]$_ }) -join ",") } else { return "(yok)" } } "RegKeyDelete" { if (-not $State.Exists) { return "(yok)" } return "(var: $(@($State.Tree.Values).Count) deger, $(@($State.Tree.SubKeys).Count) alt anahtar)" } "DizeAlani" { if ($null -ne $State.Alan) { return [string]$State.Alan } elseif ($State.Exists) { return "(alan yok)" } else { return "(yok)" } } "Servis" { if ($State.Exists) { return "$($State.StartType)/$($State.Status)" } else { return "(yok)" } } "Ozellik" { if ($State.Exists) { return [string]$State.State } else { return "(yok)" } } { $_ -in "HazirdaBekletme", "AyrilmisDepolama" } { if (-not $State.Exists) { return "(yok)" } elseif ($State.Enabled) { return "Acik" } else { return "Kapali" } } "AyarSayfasi" { if ($State.Gizli) { return "Gizli ($($State.Raw))" } elseif ($State.Exists) { return "Gorunur ($($State.Raw))" } else { return "Gorunur" } } "Gorev" { if (-not $State.Exists) { return "(yok)" } elseif ($State.Enabled) { return "Etkin" } else { return "Devre disi" } } "GucAyari" { if ($State.Exists) { return "AC $($State.AC) / DC $($State.DC)" } else { return "(yok)" } } { $_ -in "GucPlani", "GucPlaniSil" } { if ($State.Exists) { return "(var)" } else { return "(yok)" } } "AgBaglama" { if (-not $State.Exists) { return "(yok)" } elseif ($State.Enabled) { return "Acik" } else { return "Kapali" } } "ZamanSunucusu" { if (-not $State.Exists) { return "(yok)" } $t = "$($State.Type) $($State.NtpServer)".Trim(); if ($State.Domain) { $t += " (etki alani)" } return $t } "Aygit" { if (-not $State.Exists) { return "(yok)" } $d = @(ConvertFrom-KTDeviceList ([string]$State.Liste)) return "$(@($d | Where-Object { $_.Enabled }).Count) etkin / $(@($d | Where-Object { -not $_.Enabled }).Count) devre disi" } "GucModu" { return (Format-KTPowerModeText ([string]$State.Ac)) } "AktifPlan" { return [string]$State.Active } "Bcd" { if ($State.Exists) { return [string]$State.Value } else { return "(yok)" } } { $_ -in "AgKarti", "AgGenel" } { if ($State.Exists) { return [string]$State.Value } else { return "(yok)" } } "Dns" { if (-not $State.Exists) { return "(yok)" } elseif (-not $State.V4 -and -not $State.V6) { return "DHCP" } else { return (@($State.V4, $State.V6) | Where-Object { $_ }) -join "," } } { $_ -in "CihazGuc", "Hdr" } { if (-not $State.Exists) { return "(yok)" } elseif ($State.Enabled) { return "Acik" } else { return "Kapali" } } "Temizlik" { if ($State.Exists) { return "$($State.Sayi) dosya ($([math]::Round($State.Bayt / 1MB, 1)) MB)" } else { return "(yok)" } } "DnsOnbellek" { return "-" } "Trim" { if ($State.Exists) { return "$($State.Surucu):" } else { return "(yok)" } } "BitLocker" { if ($State.Exists) { return [string]$State.Durum } else { return "(yok)" } } "OneDrive" { if ($State.Exists) { return "(kurulu)" } else { return "(yok)" } } "DosyaEngel" { if (-not $State.Exists) { return "(yok)" } elseif ($State.Engelli) { return "Engelli" } else { return "Acik" } } "Uygulama" { if ($State.Exists) { return "(kurulu: $(@($State.Kurulu).Count) paket, hazir: $(@($State.Hazir).Count))" } else { return "(yok)" } } default { return (ConvertTo-Json -InputObject $State -Depth 5 -Compress) } } } # ============================================================== # Tweak tanimi (karar 40): kayit, denetim, kosullar, sablon # ============================================================== # Tanimlar src/tweaks/.ps1 dosyalarinda Register-KTTweak @{ ... } ile yazilir. 9 alan: # Id katalog id (KATALOG.csv). Kanit, test ve arastirma katalogda; koda kaynak yazilmaz. # Ad ekran adi, ASCII, en fazla 50 karakter # Aciklama ne yaptigi, ASCII # Kategori katalog kategorisi (CPU, SYS, ...) # Sablon hangi sablonda secili gelir: Masaustu / Laptop / Masaustu+Laptop / Yok / Kisisellestirme # IstegeBagli istege bagli; $true ise sablonda secili gelse de pencerede Istege bagli sayfasinda durur (karar 102; CPU01, CPU11) # Kosullar tweak'in uygulanabilmesi icin sistemde aranan (bos olabilir): bkz. $KT_KosulAnahtarlari. # Bir isleme de Kosullar yazilabilir: tutmazsa yalniz o islem atlanir (ornek Y18: plan etkinlestirme yalniz Masaustu sablonunda). # Islemler islem listesi (en az bir); turler 30_Islem.ps1 tablosunda # Uyari istege bagli ekran uyarisi, ASCII # LaptopUyari istege bagli; yalniz kasa Laptop tespit edilirse gosterilir (karar 66; ornek pil omru). $true = standart pil uyarisi # YenidenBaslat etkinin baslamasi icin: Yok / Explorer / Oturum / Bilgisayar # Secenekler istege bagli; kullanicinin arayuzde sectigi secenekler: @( @{ Ad; Islemler }, ... ) (ornek NET06 DNS saglayicisi). # Secenekli tanimda Islemler yazilmaz; varsayilan ilk secenektir. Select-KTSecenek secilen secenegin islemleriyle kopya verir. # Varsayilan istege bagli; Windows varsayilanini kuran islemler (karar 95 2. durum, 97). Baskasinin uyguladigi ayari pencereden # varsayilana dondurmek icin (Invoke-KTVarsayilan). Yalniz belgeyle veya temiz Windows dokumuyle dogrulanan degerler. # Profil, sinif, risk, kaynak alani yok (karar 40). $script:KT_Tweaks = [ordered]@{} $script:KT_TweakAlanlari = "Id", "Ad", "Aciklama", "Kategori", "Sablon", "Kosullar", "Islemler", "Uyari", "LaptopUyari", "YenidenBaslat", "Secenekler", "Varsayilan", "IstegeBagli" $script:KT_PilUyarisi = "(Pil omrunu ve pilde kullanim suresini kisaltir)" $script:KT_TweakZorunlu = "Id", "Ad", "Aciklama", "Kategori", "Sablon", "Islemler", "YenidenBaslat" $script:KT_Sablonlar = "Masaustu", "Laptop", "Masaustu+Laptop", "Yok", "Kisisellestirme" $script:KT_YenidenBaslatma = "Yok", "Explorer", "Oturum", "Bilgisayar" # agirlik sirasiyla # Kosul anahtari -> izin verilen degerler ($null: serbest metin listesi) $script:KT_KosulAnahtarlari = @{ Os = @("25H2", "26H2") Cpu = @("Intel", "AMD") Gpu = @("NVIDIA", "AMD", "Intel") Disk = @("SSD", "HDD") Ag = @("Ethernet", "Wi-Fi") Kurulu = $null KuruluDegil = $null # Kurulu listesinde bunlarin hicbiri yoksa (SVC10: Telefon Baglantisi yoksa) Bekleme = @("S0", "S3") # desteklenen uyku: S0 Modern Standby, S3 klasik (sistem analizi S0/S3'ten) Kasa = @("Masaustu", "Laptop") # analizin buldugu kasa (sablondan bagimsiz; ornek SYS20 laptopta hic gosterilmez) Sablon = @("Masaustu", "Laptop") # kullanicinin sectigi sablon (islem kosulu: Y18 plani yalniz Masaustu sablonunda etkinlesir) Hags = @("Acik", "Kapali") # HwSchMode okunduysa (destek biliniyor; karar 53, 90) Hdr = @($true) # HDR destekli ekran var (karar 52, 90) } function Test-KTKosulTanimi { <# .SYNOPSIS Saf. Kosullar tablosunu denetler (tweak ve islem kosullari icin ortak); hata metinleri. #> param($Kosullar, [string]$Yer = "Kosullar") $err = [System.Collections.Generic.List[string]]::new() if ($null -eq $Kosullar) { return $err.ToArray() } if ($Kosullar -isnot [hashtable]) { $err.Add("$Yer hashtable olmali"); return $err.ToArray() } foreach ($k in $Kosullar.Keys) { if (-not $script:KT_KosulAnahtarlari.ContainsKey($k)) { $err.Add("Bilinmeyen kosul: $k"); continue } $izin = $script:KT_KosulAnahtarlari[$k] $vals = @($Kosullar[$k]) if ($vals.Count -eq 0) { $err.Add("Kosul bos: $k") } if ($izin) { foreach ($v in $vals) { if ($izin -cnotcontains $v) { $err.Add("Gecersiz $k kosulu: $v") } } } } return $err.ToArray() } function Test-KTTweakDefinition { <# .SYNOPSIS Saf. Tanimi denetler; hata metinleri listesi (bossa gecerli). #> param([Parameter(Mandatory)][hashtable]$Tweak) $err = [System.Collections.Generic.List[string]]::new() foreach ($k in $Tweak.Keys) { if ($script:KT_TweakAlanlari -notcontains $k) { $err.Add("Bilinmeyen alan: $k") } } $secenekli = $null -ne $Tweak["Secenekler"] if ($secenekli -and $null -ne $Tweak["Islemler"]) { $err.Add("Secenekli tanimda Islemler yazilmaz (varsayilan ilk secenek)") } foreach ($f in $script:KT_TweakZorunlu) { if ($f -eq "Islemler" -and $secenekli) { continue } if ($null -eq $Tweak[$f] -or ($Tweak[$f] -is [string] -and $Tweak[$f] -eq "")) { $err.Add("$f alani zorunlu") } } if ($err.Count -gt 0) { return $err.ToArray() } if (-not (Test-KTAscii $Tweak.Ad) -or $Tweak.Ad.Length -gt 50) { $err.Add("Ad ASCII ve en fazla 50 karakter olmali") } foreach ($f in "Aciklama", "Uyari") { if ($Tweak[$f] -and -not (Test-KTAscii $Tweak[$f])) { $err.Add("$f ASCII olmali") } } $lu = $Tweak["LaptopUyari"] if ($null -ne $lu -and $lu -isnot [bool] -and -not ($lu -is [string] -and (Test-KTAscii $lu))) { $err.Add("LaptopUyari `$true veya ASCII metin olmali") } if ($script:KT_Sablonlar -cnotcontains $Tweak.Sablon) { $err.Add("Gecersiz Sablon: $($Tweak.Sablon)") } if ($null -ne $Tweak["IstegeBagli"] -and $Tweak["IstegeBagli"] -isnot [bool]) { $err.Add("IstegeBagli `$true veya `$false olmali") } if ($script:KT_YenidenBaslatma -cnotcontains $Tweak.YenidenBaslat) { $err.Add("Gecersiz YenidenBaslat: $($Tweak.YenidenBaslat)") } foreach ($e in @(Test-KTKosulTanimi $Tweak.Kosullar)) { $err.Add($e) } $actions = @($Tweak.Islemler) if ($secenekli) { $adlar = @{} $actions = @() $sec = @($Tweak.Secenekler) if ($sec.Count -lt 2) { $err.Add("Secenekler en az iki secenek olmali") } foreach ($o in $sec) { $oad = [string](Get-KTField $o Ad) if (-not $oad -or -not (Test-KTAscii $oad) -or $oad.Length -gt 40) { $err.Add("Secenek Ad ASCII ve en fazla 40 karakter olmali") } elseif ($adlar.ContainsKey($oad)) { $err.Add("Secenek Ad tekrar ediyor: $oad") } $adlar[$oad] = $true $oi = @(Get-KTField $o Islemler) if ($oi.Count -eq 0) { $err.Add("Secenek Islemler bos olamaz: $oad") } $actions += $oi } } if ($actions.Count -eq 0) { $err.Add("Islemler bos olamaz") } if ($null -ne $Tweak["Varsayilan"]) { $vi = @($Tweak.Varsayilan | Where-Object { $_ }) if ($vi.Count -eq 0) { $err.Add("Varsayilan bos olamaz") } $actions += $vi } foreach ($a in $actions) { $h = $script:KT_ActionHandlers[[string](Get-KTField $a Tur)] if (-not $h) { $err.Add("Bilinmeyen islem turu: $(Get-KTField $a Tur)"); continue } foreach ($e in @(& $h.Check -Action $a)) { $err.Add($e) } # Islem kosulu (istege bagli): tutmazsa yalniz bu islem atlanir foreach ($e in @(Test-KTKosulTanimi (Get-KTField $a Kosullar) "Islem Kosullar")) { $err.Add($e) } } return $err.ToArray() } function Register-KTTweak { <# .SYNOPSIS Tanimi denetleyip kayda ekler. Hata veya tekrar eden Id varsa istisna (program baslamaz). #> param([Parameter(Mandatory, Position = 0)][hashtable]$Tweak) $id = [string]$Tweak.Id $err = @(Test-KTTweakDefinition $Tweak) if ($id -and $script:KT_Tweaks.Contains($id)) { $err += "Id tekrar ediyor" } if ($err.Count -gt 0) { throw "Tweak tanimi gecersiz ($id): $($err -join '; ')" } $t = [ordered]@{} foreach ($f in $script:KT_TweakAlanlari) { $t[$f] = $Tweak[$f] } if ($null -eq $t.Kosullar) { $t.Kosullar = @{} } if ($null -ne $Tweak.Secenekler) { $t.Secenekler = @($Tweak.Secenekler | ForEach-Object { [pscustomobject]@{ Ad = [string]$_.Ad; Islemler = @($_.Islemler) } }) $t.Islemler = @($t.Secenekler[0].Islemler) } else { $t.Islemler = @($Tweak.Islemler) } if ($null -ne $Tweak.Varsayilan) { $t.Varsayilan = @($Tweak.Varsayilan) } $script:KT_Tweaks[$id] = [pscustomobject]$t } function Get-KTTweak { <# .SYNOPSIS Id verilirse o tanim, verilmezse kayit sirasiyla hepsi. #> param([string]$Id) if ($Id) { return $script:KT_Tweaks[$Id] } return @($script:KT_Tweaks.Values) } function Clear-KTTweaks { $script:KT_Tweaks.Clear() } function Select-KTSecenek { <# .SYNOPSIS Saf. Secenekli tweak'in, secilen secenegin islemleriyle kopyasi (tanim degismez). Bilinmeyen secenek hata. #> param([Parameter(Mandatory)] $Tweak, [Parameter(Mandatory)][string]$Ad) $o = @($Tweak.Secenekler) | Where-Object { $_ -and [string]$_.Ad -ceq $Ad } | Select-Object -First 1 if (-not $o) { throw "Bilinmeyen secenek ($($Tweak.Id)): $Ad" } $c = [ordered]@{} foreach ($p in $Tweak.PSObject.Properties) { $c[$p.Name] = $p.Value } $c.Islemler = @($o.Islemler) return [pscustomobject]$c } # ---------------- Kosul ve sablon (saf) ---------------- function Test-KTKosul { <# .SYNOPSIS Saf. Tweak bu sistemde uygulanabilir mi. Donus @{ Uygun; Neden } (Neden ekran metni, ASCII). Sistem: sistem analizinin ciktisi. Kullanilan alanlar: Os (metin), Cpu (metin), Gpu (liste), Disk (metin), Ag (liste), Kurulu (liste), Bekleme (liste: S0 / S3), Sablon (kullanicinin sectigi sablon). Kosulun istedigi alan bossa karar verilemez -> uygun degil (yanlis yere yazilmaz). Liste kosulunda (Gpu, Ag, Kurulu) sistemde degerlerden biri varsa yeter. KuruluDegil: Kurulu listesi okunmus ve degerlerin hicbiri listede degilse tutar (liste bossa karar verilemez). #> param([Parameter(Mandatory)] $Tweak, $Sistem = $null) $ok = [pscustomobject]@{ Uygun = $true; Neden = "" } $k = Get-KTField $Tweak Kosullar if (-not $k -or $k.Count -eq 0) { return $ok } foreach ($ad in @($k.Keys | Sort-Object)) { $ist = @($k[$ad]) if ($ad -eq "KuruluDegil") { $kur = @(Get-KTField $Sistem Kurulu | Where-Object { $_ }) if ($kur.Count -eq 0) { return [pscustomobject]@{ Uygun = $false; Neden = "(Sistem bilgisi yok: Kurulu)" } } foreach ($v in $ist) { if ($kur -contains $v) { return [pscustomobject]@{ Uygun = $false; Neden = "(Kurulu: $v)" } } } continue } $var = @(Get-KTField $Sistem $ad | Where-Object { $_ }) if ($var.Count -eq 0) { return [pscustomobject]@{ Uygun = $false; Neden = "(Sistem bilgisi yok: $ad)" } } $hit = $false foreach ($v in $var) { if ($ist -contains $v) { $hit = $true; break } } if (-not $hit) { $neden = switch ($ad) { "Os" { "(Bu Windows surumunde yok)" } "Kurulu" { "(Kurulu degil: $($ist -join ', '))" } "Bekleme" { "(Yalniz $($ist -join ' / ') uyku destekli cihazlarda)" } default { "(Yalniz $($ist -join ' / ') icin)" } } return [pscustomobject]@{ Uygun = $false; Neden = $neden } } } return $ok } function Get-KTUyari { <# .SYNOPSIS Saf. Ekranda gosterilecek uyari metni: Uyari + (kasa Laptop ise) LaptopUyari. Yoksa bos metin. LaptopUyari $true ise standart pil uyarisi (karar 66). #> param([Parameter(Mandatory)] $Tweak, $Sistem = $null) $parca = [System.Collections.Generic.List[string]]::new() $u = [string](Get-KTField $Tweak Uyari); if ($u) { $parca.Add($u) } $lu = Get-KTField $Tweak LaptopUyari if ($lu -and [string](Get-KTField $Sistem Kasa) -eq "Laptop") { $parca.Add($(if ($lu -is [bool]) { $script:KT_PilUyarisi } else { [string]$lu })) } return ($parca -join " ") } function Test-KTSecili { <# .SYNOPSIS Saf. Tweak verilen sablonda (Masaustu / Laptop) secili gelir mi. Yok ve Kisisellestirme hicbir sablonda secili gelmez; kullanici isterse secer. #> param([Parameter(Mandatory)] $Tweak, [Parameter(Mandatory)][ValidateSet("Masaustu", "Laptop")][string]$Sablon) switch ([string]$Tweak.Sablon) { "Masaustu+Laptop" { return $true } "Masaustu" { return ($Sablon -eq "Masaustu") } "Laptop" { return ($Sablon -eq "Laptop") } default { return $false } } } function Import-KTTweakFiles { <# .SYNOPSIS Gelistirme: klasordeki tanim dosyalarini (ad sirasiyla) yukler. Tek dosya surumunde tanimlar gomulu. #> param([Parameter(Mandatory)][string]$Dir) $names = [string[]]@(Get-ChildItem -LiteralPath $Dir -Filter "*.ps1" -File | ForEach-Object { $_.FullName }) [Array]::Sort($names, [StringComparer]::Ordinal) foreach ($f in $names) { . $f } } # ============================================================== # Backup: deger bazli yedek dosyasi ve geri alma # ============================================================== # - Her uygulama calistirmasi bir dosya: C:\KelTweaker\Backup\KelTweaker_Backup_.json # - Girdi, deger degismeden ONCE diske yazilir. # - Geri alma onceki calistirmalarin dosyalarini da okur (Adem 21.10.2026), yalniz ayni Format'taki dosyalari. # Farkli Format eski veya baska surumundur; okunmaz, atlanir (surumler birbirinden sorumlu degil). # - Geri alma: dosyalar en yeniden eskiye, girdiler ters sirayla. Her geri yazmadan sonra durum okunup onceki durumla karsilastirilir. # - HKCU girdileri yalniz yedegi alan kullanicinin oturumunda geri alinir. # - Geri alinan dosya silinmez; Restored alanina tarih yazilir (yalniz hatasiz geri almada). # - Baska bilgisayarda alinmis yedek (Computer farkli) geri alinmaz. # - Varsayilan klasorun izinleri korunur: yalniz Yoneticiler ve SYSTEM yazar, Kullanicilar okur (C:\ altindaki klasor # varsayilan olarak standart kullaniciya degistirme izni verir; degistirilmis yedek yonetici yetkisiyle geri yazilmasin). $script:KT_BackupDir = Join-Path $env:SystemDrive "KelTweaker\Backup" # Windows'un kurulu oldugu surucu (cogunlukla C:) $script:KT_BackupFormat = 1 $script:KT_Utf8NoBom = New-Object System.Text.UTF8Encoding $false function Get-KTCurrentUserSid { [Security.Principal.WindowsIdentity]::GetCurrent().User.Value } function Get-KTNowText { param([string]$Format = "yyyy-MM-ddTHH:mm:ss") return (Get-Date).ToString($Format, [Globalization.CultureInfo]::InvariantCulture) } function Save-KTBackupFile { <# .SYNOPSIS Once gecici dosyaya yazar, sonra yerine koyar (yarim dosya kalmasin). #> param([Parameter(Mandatory)][string]$Path, [Parameter(Mandatory)] $Data) $json = ConvertTo-Json -InputObject $Data -Depth 30 # PS 5.1 varsayilan derinlik 2 $tmp = "$Path.tmp" [System.IO.File]::WriteAllText($tmp, $json, $script:KT_Utf8NoBom) # [NullString]: PowerShell $null'u string parametreye "" gecirir, Replace bunu gecersiz yol sayar if (Test-Path -LiteralPath $Path) { [System.IO.File]::Replace($tmp, $Path, [NullString]::Value) } else { [System.IO.File]::Move($tmp, $Path) } } function Protect-KTBackupDirRaw { <# .SYNOPSIS Klasoru devralmasiz yapar: SYSTEM ve Yoneticiler tam, Kullanicilar okuma. Zaten oyleyse dokunmaz. #> param([Parameter(Mandatory)][string]$Dir) $sddl = 'O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;BU)' $acl = Get-Acl -LiteralPath $Dir -ErrorAction Stop if ($acl.Sddl -eq $sddl) { return } $acl.SetSecurityDescriptorSddlForm($sddl) Set-Acl -LiteralPath $Dir -AclObject $acl -ErrorAction Stop } function New-KTBackupSession { <# .SYNOPSIS Yeni yedek dosyasini (bos girdi listesiyle) olusturur; oturum nesnesini dondurur. #> param([string]$Dir = $script:KT_BackupDir, [string]$UserSid = (Get-KTCurrentUserSid)) New-Item -ItemType Directory -Path $Dir -Force -ErrorAction Stop | Out-Null if ($Dir -eq $script:KT_BackupDir) { Protect-KTBackupDirRaw $Dir } $base = "KelTweaker_Backup_" + (Get-KTNowText "yyyyMMdd_HHmmss") $path = Join-Path $Dir "$base.json"; $i = 2 while (Test-Path -LiteralPath $path) { $path = Join-Path $Dir "${base}_$i.json"; $i++ } $data = [pscustomobject][ordered]@{ Format = $script:KT_BackupFormat Surum = $script:KT_Surum Created = Get-KTNowText Computer = [Environment]::MachineName UserSid = $UserSid Restored = $null Entries = [System.Collections.Generic.List[object]]::new() } Save-KTBackupFile -Path $path -Data $data return [pscustomobject]@{ Path = $path; Data = $data } } function Add-KTBackupEntry { <# .SYNOPSIS Islemin onceki durumunu yedege ekler ve dosyayi hemen diske yazar. #> param([Parameter(Mandatory)] $Session, [Parameter(Mandatory)][string]$TweakId, [Parameter(Mandatory)][string]$YenidenBaslat, [Parameter(Mandatory)] $Action, [Parameter(Mandatory)] $Before) $entry = [pscustomobject][ordered]@{ TweakId = $TweakId Order = $Session.Data.Entries.Count + 1 YenidenBaslat = $YenidenBaslat Action = $Action Before = $Before } $Session.Data.Entries.Add($entry) Save-KTBackupFile -Path $Session.Path -Data $Session.Data return $entry } function Read-KTBackup { <# .SYNOPSIS Yedek dosyasini okur; Path alani eklenir. Bilinmeyen Format istisna. #> param([Parameter(Mandatory)][string]$Path) $data = [System.IO.File]::ReadAllText($Path, $script:KT_Utf8NoBom) | ConvertFrom-Json if ($data.Format -ne $script:KT_BackupFormat) { throw "Bilinmeyen yedek bicimi ($($data.Format))" } $data | Add-Member -NotePropertyName Path -NotePropertyValue $Path -Force return $data } function Get-KTBackupList { <# .SYNOPSIS Klasordeki yedek dosyalari, en yeniden eskiye (ad zaman damgasi icerir). #> param([string]$Dir = $script:KT_BackupDir) if (-not (Test-Path -LiteralPath $Dir)) { return @() } $names = [string[]]@(Get-ChildItem -LiteralPath $Dir -Filter "KelTweaker_Backup_*.json" -File | ForEach-Object { $_.FullName }) [Array]::Sort($names, [StringComparer]::Ordinal); [Array]::Reverse($names) return $names } function Test-KTYedekBuBilgisayar { <# .SYNOPSIS Saf. Yedek bu bilgisayarda mi alinmis (Computer = makine adi). #> param([Parameter(Mandatory)] $Backup, [string]$Makine = [Environment]::MachineName) return [string]::Equals([string]$Backup.Computer, $Makine, [StringComparison]::OrdinalIgnoreCase) } function Get-KTYedekliTweakler { <# .SYNOPSIS Geri alinmamis yedek girdisi olan tweak kimlikleri (hashtable Id -> $true). Okunamayan dosya atlanir. #> param([string]$Dir = $script:KT_BackupDir) $h = @{} foreach ($p in @(Get-KTBackupList -Dir $Dir)) { try { $bk = Read-KTBackup $p } catch { continue } if ($bk.Restored -or -not (Test-KTYedekBuBilgisayar $bk)) { continue } foreach ($e in @($bk.Entries)) { if (-not (Get-KTField $e GeriAlindi)) { $h[[string]$e.TweakId] = $true } } } return $h } function Invoke-KTRestore { <# .SYNOPSIS Verilen yedekleri (en yeniden eskiye sirali verilmeli) geri alir. TweakId verilirse yalniz o tweak'in girdileri geri alinir (pencerede tek tweak geri alma, Adem 25.10.2026). Geri alinan her girdiye GeriAlindi tarihi yazilir (kismi basarida ikinci deneme onu yeniden yazmasin); dosyanin tum girdileri geri alindiysa dosya da Restored olur. GeriAlindi girdisi atlanir. .OUTPUTS Girdi basina @{ File; TweakId; Order; YenidenBaslat; Status (GeriAlindi / Atlandi / Hata); Detail } #> param([Parameter(Mandatory)][AllowEmptyCollection()][string[]]$Paths, [string]$UserSid = (Get-KTCurrentUserSid), [string]$TweakId) $results = [System.Collections.Generic.List[object]]::new() $row = { param($file, $e, $status, $detail) [pscustomobject]@{ File = $file; TweakId = [string](Get-KTField $e TweakId); Order = [int](Get-KTField $e Order) YenidenBaslat = [string](Get-KTField $e YenidenBaslat); Status = $status; Detail = $detail } } foreach ($p in $Paths) { try { $bk = Read-KTBackup $p } catch { $results.Add((& $row $p $null "Atlandi" "Okunamadi: $($_.Exception.Message)")); continue } if ($bk.Restored) { $results.Add((& $row $p $null "Atlandi" "Daha once geri alindi: $($bk.Restored)")); continue } if (-not (Test-KTYedekBuBilgisayar $bk)) { $results.Add((& $row $p $null "Atlandi" "Baska bilgisayarda alinmis yedek: $($bk.Computer)")); continue } $degisti = $false $bk.PSObject.Properties.Remove("Path") foreach ($e in @(@($bk.Entries) | Sort-Object -Property Order -Descending)) { if (Get-KTField $e GeriAlindi) { continue } if ($TweakId -and [string]$e.TweakId -ne $TweakId) { continue } if ((Get-KTActionScope $e.Action) -eq "User" -and $bk.UserSid -ne $UserSid) { $results.Add((& $row $p $e "Atlandi" "Kullanici ayari baska bir hesapta yedeklendi")); continue } try { Undo-KTAction -Action $e.Action -Before $e.Before $now = Get-KTActionState -Action $e.Action if (Test-KTActionRestored -Action $e.Action -Before $e.Before -Now $now) { $results.Add((& $row $p $e "GeriAlindi" "")) # Her basarili girdiden sonra hemen kaydedilir (islem yarida kesilirse sonraki geri alma onu yeniden yazmasin) $e | Add-Member -NotePropertyName GeriAlindi -NotePropertyValue (Get-KTNowText) -Force Save-KTBackupFile -Path $p -Data $bk } else { $results.Add((& $row $p $e "Hata" "Geri yazildi ama dogrulanamadi")) } } catch { $results.Add((& $row $p $e "Hata" $_.Exception.Message)) } } $hepsi = -not @(@($bk.Entries) | Where-Object { -not (Get-KTField $_ GeriAlindi) }).Count if ($hepsi) { $bk.Restored = Get-KTNowText; $degisti = $true } if ($degisti) { Save-KTBackupFile -Path $p -Data $bk } } return $results.ToArray() } # ============================================================== # Motor: tweak uygulama akisi ve yeniden baslatma bilgisi # ============================================================== # Akis: kosul -> Get -> Test -> [-Oku burada durur] -> yedek -> Set -> dogrulama. # Sonuclar (ekran metni, ASCII): Uygulandi / Zaten uygulanmis / Uygun degil / Okunamadi / Hata; -Oku ile Uygulanacak. # Hedef degerle ayni olan islem yazilmaz ve yedege girmez (deger zaten hedefte). # Sistemde olmayan servis/gorev islemi atlanir, digerleri uygulanir; hepsi yoksa Uygun degil (karar 43.11). # Set hatasinda bu tweak'te o ana kadar yazilanlar (hata veren dahil) geri alinir; geri alinamaz islemler haric. function Invoke-KTTweak { <# .SYNOPSIS Bir tweak'i uygular, -Oku ile yalniz durumunu okur. Sonuc nesnesini dondurur. .OUTPUTS @{ Id; Ad; Sonuc; Neden; YenidenBaslat; Islemler = @{ Tur; Hedef; Once; Sonra; Durum } } Sistem: Test-KTKosul icin sistem analizi ciktisi; verilmezse kosul denetlenmez (kosulsuz tweak'ler ve testler). Session: New-KTBackupSession (-Oku disinda zorunlu). #> param([Parameter(Mandatory)] $Tweak, $Sistem = $null, $Session = $null, [switch]$Oku) $r = [pscustomobject]@{ Id = $Tweak.Id; Ad = $Tweak.Ad; Sonuc = ""; Neden = ""; YenidenBaslat = "Yok"; Islemler = @() } $done = { param($res, $why) $r.Sonuc = $res; $r.Neden = $why; return $r } # 1. Kosul if ($null -ne $Sistem) { $k = Test-KTKosul -Tweak $Tweak -Sistem $Sistem if (-not $k.Uygun) { return (& $done "Uygun degil" $k.Neden) } } # 2-3. Genisletme + Get + Test # Turun Expand'i varsa islem sistemdeki her uygun cihaza acilir (ornek: her fiziksel ag karti); # uygun cihaz yoksa islem atlanir. $acts = [System.Collections.Generic.List[object]]::new() foreach ($a0 in @($Tweak.Islemler)) { $h = Get-KTActionHandler $a0 if (-not $h.Expand) { $acts.Add([pscustomobject]@{ A = $a0; Skip = "" }); continue } $ex = @(& $h.Expand -Action $a0 -Sistem $Sistem) if ($ex.Count -eq 0) { $acts.Add([pscustomobject]@{ A = $a0; Skip = "(Uygun cihaz yok veya sistem bilgisi yok)" }) } foreach ($e in $ex) { $acts.Add([pscustomobject]@{ A = $e; Skip = "" }) } } $items = [System.Collections.Generic.List[object]]::new() $atla = [System.Collections.Generic.List[string]]::new() foreach ($ac in $acts) { $a = $ac.A $row = [pscustomobject]@{ Tur = [string](Get-KTField $a Tur); Hedef = (Format-KTActionTarget $a); Once = "-"; Sonra = "-"; Durum = "" } $it = [pscustomobject]@{ Action = $a; State = $null; Test = $null; Row = $row; Entry = $null } $items.Add($it) if ($ac.Skip) { $it.Test = "Skip"; $row.Durum = "Kosul disi $($ac.Skip)"; continue } # Islem kosulu: tutmazsa (veya sistem bilgisi yoksa) yalniz bu islem atlanir $ak = Get-KTField $a Kosullar if ($ak -and $ak.Count -gt 0) { $kk = if ($null -ne $Sistem) { Test-KTKosul -Tweak ([pscustomobject]@{ Kosullar = $ak }) -Sistem $Sistem } else { [pscustomobject]@{ Uygun = $false; Neden = "(Sistem bilgisi yok)" } } if (-not $kk.Uygun) { $it.Test = "Skip"; $row.Durum = "Kosul disi $($kk.Neden)"; continue } } try { # Somutlastirma yedekten once: yedege ve geri almaya somut islem gider ('Aktif' plan -> GUID) $a = Resolve-KTAction $a $it.Action = $a; $row.Hedef = Format-KTActionTarget $a $s = Get-KTActionState $a $it.State = $s; $it.Test = Test-KTActionState $a $s $row.Once = Format-KTStateText $a $s # Islem o an yazilamiyorsa (guc modu pildeyken) atlanir, nedeni sonuca yazilir $ah = (Get-KTActionHandler $a).Atla if ($it.Test -eq "NotApplied" -and $ah) { $why = [string](& $ah -Action $a -State $s) if ($why) { $it.Test = "Skip"; $row.Durum = "Atlandi $why"; $atla.Add($why) } } } catch { $row.Durum = "Okunamadi" $r.Islemler = @($items | ForEach-Object { $_.Row }) return (& $done "Okunamadi" "($(ConvertTo-KTAsciiText $_.Exception.Message))") } } foreach ($it in $items) { if ($it.Test -eq "Skip") { continue } $it.Row.Durum = switch ($it.Test) { "Applied" { "Zaten uygulanmis" } "NotApplicable" { "Sistemde yok" } default { "Uygulanacak" } } } $r.Islemler = @($items | ForEach-Object { $_.Row }) # Sistemde olmayan servis/gorev atlanir (karar 43.11); hepsi yoksa tweak uygun degil $missing = @($items | Where-Object { $_.Test -eq "NotApplicable" }) $skipNote = if ($missing.Count -gt 0) { "(Sistemde yok, atlandi: $(($missing | ForEach-Object { $_.Row.Hedef }) -join ', '))" } else { "" } if ($atla.Count -gt 0) { $skipNote = (@($skipNote) + @($atla | Select-Object -Unique) | Where-Object { $_ }) -join " " } $skipped = @($items | Where-Object { $_.Test -eq "Skip" }) if ($missing.Count + $skipped.Count -eq $items.Count) { $why = if ($missing.Count -gt 0) { "(Sistemde yok: $(($missing | ForEach-Object { $_.Row.Hedef }) -join ', '))" } elseif ($atla.Count -gt 0) { (@($atla | Select-Object -Unique)) -join " " } else { "(Islem kosullari tutmadi)" } return (& $done "Uygun degil" $why) } $todo = @($items | Where-Object { $_.Test -eq "NotApplied" }) if ($todo.Count -eq 0) { return (& $done "Zaten uygulanmis" $skipNote) } if ($Oku) { return (& $done "Uygulanacak" $skipNote) } if (-not $Session) { throw "Yedek oturumu verilmedi ($($Tweak.Id))" } # 4-5. Yedek + Set. Geri alinamaz islemler sona: onceki bir yazma hata verirse geri alinir ve kalici degisiklik # (sifre cozme, uygulama kaldirma) hic baslamaz. Kendi aralarindaki sira korunur. $todo = @(@($todo | Where-Object { -not (Test-KTActionIrreversible $_.Action) }) + @($todo | Where-Object { Test-KTActionIrreversible $_.Action })) $written = [System.Collections.Generic.List[object]]::new() # Hata sonucunda geri alinamaz islem baslamissa bu da yazilir (kalici degisiklik kalmis olabilir) $kalici = "" foreach ($it in $todo) { try { # Geri alinamaz islem (uygulama kaldirma, GeriAlinamaz alani) yedege yazilmaz ve geri alma listesine girmez if (-not (Test-KTActionIrreversible $it.Action)) { $it.Entry = Add-KTBackupEntry -Session $Session -TweakId $Tweak.Id -YenidenBaslat $Tweak.YenidenBaslat -Action $it.Action -Before $it.State $written.Add($it) } else { $kalici = "; geri alinamaz islem kismen yapilmis olabilir" } Invoke-KTActionSet $it.Action } catch { $msg = ConvertTo-KTAsciiText $_.Exception.Message $it.Row.Durum = "Hata" $rb = Undo-KTTweakWritten $written.ToArray() -Session $Session return (& $done "Hata" "($msg; $rb$kalici)") } } # 6. Dogrulama: yazildi ama tutmadiysa hata (baska bir ilke veya program eziyor olabilir); yazilanlar geri alinir # Turun Dogrula'si not ve ek yeniden baslatma dondurebilir (servis durdurulamadi gibi) $bad = 0 $notes = [System.Collections.Generic.List[string]]::new() $restart = [System.Collections.Generic.List[string]]::new() $restart.Add([string]$Tweak.YenidenBaslat) foreach ($it in $todo) { try { $now = Get-KTActionState $it.Action $v = Test-KTActionVerify $it.Action $now $ok = [bool]$v.Ok $it.Row.Sonra = Format-KTStateText $it.Action $now if ($ok -and $v.Not) { $notes.Add([string]$v.Not); $restart.Add([string]$v.YenidenBaslat) } } catch { $ok = $false } $it.Row.Durum = if ($ok) { "Uygulandi" } else { "Hata" } if (-not $ok) { $bad++ } } if ($bad -gt 0) { $rb = Undo-KTTweakWritten $written.ToArray() -Session $Session $kalici = if ($kalici) { "; geri alinamaz islem yapildi" } else { "" } return (& $done "Hata" "(Yazildi ama dogrulanamadi; $rb$kalici)") } $r.YenidenBaslat = Get-KTYenidenBaslatma $restart.ToArray() if ($skipNote) { $notes.Add($skipNote) } return (& $done "Uygulandi" ($notes -join " ")) } function Undo-KTTweakWritten { <# .SYNOPSIS Yazilan islemleri ters sirayla geri alir; sonucu kisa metin olarak dondurur. Geri alinan islemin yedek girdisine GeriAlindi yazilir ve dosya kaydedilir (sonraki geri alma onu yeniden yazmasin). Geri alinamayan girdi yedekte kalir; satiri "Hata" olur. #> param([AllowEmptyCollection()][object[]]$Items = @(), $Session = $null) if ($Items.Count -eq 0) { return "geri alinacak yazma yok" } $fail = [System.Collections.Generic.List[string]]::new() $isaretli = $false for ($i = $Items.Count - 1; $i -ge 0; $i--) { $it = $Items[$i] $ok = $false try { Undo-KTAction -Action $it.Action -Before $it.State $ok = Test-KTActionRestored -Action $it.Action -Before $it.State -Now (Get-KTActionState $it.Action) } catch { $ok = $false } if (-not $ok) { $fail.Add($it.Row.Hedef); $it.Row.Durum = "Hata"; continue } if ($it.Row.Durum -ne "Hata") { $it.Row.Durum = "Geri alindi" } if (Get-KTField $it Entry) { $it.Entry | Add-Member -NotePropertyName GeriAlindi -NotePropertyValue (Get-KTNowText) -Force; $isaretli = $true } } if ($isaretli -and $Session) { Save-KTBackupFile -Path $Session.Path -Data $Session.Data } if ($fail.Count -gt 0) { return "geri alinamadi: $($fail -join ', ')" } return "bu tweak'te yazilanlar geri alindi" } # ---------------- Yeniden baslatma (saf) ---------------- function Get-KTYenidenBaslatma { <# .SYNOPSIS Saf. Degerlerin en agiri (Yok < Explorer < Oturum < Bilgisayar). Uygulama sonuclarindan: Invoke-KTTweak ciktisinin YenidenBaslat alani (yalniz Uygulandi'da dolu). Geri alma sonuclarindan: GeriAlindi satirlarinin YenidenBaslat alani. #> param([AllowEmptyCollection()][string[]]$Degerler = @()) $max = 0 foreach ($d in $Degerler) { $i = [array]::IndexOf([string[]]$script:KT_YenidenBaslatma, $d); if ($i -gt $max) { $max = $i } } return $script:KT_YenidenBaslatma[$max] } function Format-KTYenidenBaslatmaText { <# .SYNOPSIS Saf. Ekran metni (ASCII). Arayuz Bilgisayar/Oturum icin "Simdi ... mi?" diye sorar; kullanici hayir derse de bu metin gosterilir (Adem 21.10.2026). #> param([Parameter(Mandatory)][string]$Gerek) switch ($Gerek) { "Bilgisayar" { return "Degisikliklerin tamami bilgisayar yeniden baslatildiktan sonra etkili olur." } "Oturum" { return "Degisikliklerin tamami oturum kapatilip acildiktan sonra etkili olur." } "Explorer" { return "Degisikliklerin tamami Explorer yeniden baslatildiktan sonra etkili olur." } default { return "" } } } # ============================================================== # Sistem analizi: Windows'un degistirilemeyen kaynaklarindan okuma (P/Invoke) ve saf ayristiricilar # ============================================================== # Windows surumu kayit defterinden OKUNMAZ: ProductName, DisplayVersion, CurrentBuild, BCD aciklamasi, OEM bilgisi # baska programlarca degistirilebiliyor (ornek Atlas: BCD description "AtlasOS 11", OEMInformation Model, # RegisteredOrganization; Adem 21.10.2026). Surum cekirdekten RtlGetVersion ile, mimari GetNativeSystemInfo ile okunur. # S0/S3: CallNtPowerInformation(SystemPowerCapabilities); powercfg /a ciktisi dile gore degisir. # Islemci yapisi: GetLogicalProcessorInformationEx (cekirdek verimlilik sinifi, L3 onbellek sayisi). $script:KT_YerelKaynak = @' using System; using System.Runtime.InteropServices; public static class KTYerel { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] struct OSVERSIONINFOEX { public int dwOSVersionInfoSize; public int dwMajorVersion; public int dwMinorVersion; public int dwBuildNumber; public int dwPlatformId; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string szCSDVersion; public ushort wServicePackMajor; public ushort wServicePackMinor; public ushort wSuiteMask; public byte wProductType; public byte wReserved; } [DllImport("ntdll.dll")] static extern int RtlGetVersion(ref OSVERSIONINFOEX v); public static int[] OsVersion() { var v = new OSVERSIONINFOEX(); v.dwOSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEX)); int rc = RtlGetVersion(ref v); if (rc != 0) throw new Exception("RtlGetVersion " + rc); return new int[] { v.dwMajorVersion, v.dwMinorVersion, v.dwBuildNumber, v.wProductType }; } [StructLayout(LayoutKind.Sequential)] struct SYSTEM_INFO { public ushort wProcessorArchitecture; public ushort wReserved; public uint dwPageSize; public IntPtr lpMinimumApplicationAddress; public IntPtr lpMaximumApplicationAddress; public UIntPtr dwActiveProcessorMask; public uint dwNumberOfProcessors; public uint dwProcessorType; public uint dwAllocationGranularity; public ushort wProcessorLevel; public ushort wProcessorRevision; } [DllImport("kernel32.dll")] static extern void GetNativeSystemInfo(out SYSTEM_INFO si); public static int NativeArch() { SYSTEM_INFO s; GetNativeSystemInfo(out s); return s.wProcessorArchitecture; } [DllImport("powrprof.dll")] static extern uint CallNtPowerInformation(int level, IntPtr inBuf, int inLen, byte[] outBuf, int outLen); public static byte[] PowerCaps() { var b = new byte[76]; // sizeof(SYSTEM_POWER_CAPABILITIES) uint rc = CallNtPowerInformation(4, IntPtr.Zero, 0, b, b.Length); // 4 = SystemPowerCapabilities if (rc != 0) throw new Exception("CallNtPowerInformation 0x" + rc.ToString("X8")); return b; } [DllImport("kernel32.dll", SetLastError = true)] static extern bool GetLogicalProcessorInformationEx(int rel, IntPtr buf, ref int len); public static byte[] ProcInfo(int rel) { int len = 0; GetLogicalProcessorInformationEx(rel, IntPtr.Zero, ref len); if (len <= 0) throw new Exception("GetLogicalProcessorInformationEx boyut " + Marshal.GetLastWin32Error()); IntPtr p = Marshal.AllocHGlobal(len); try { if (!GetLogicalProcessorInformationEx(rel, p, ref len)) throw new Exception("GetLogicalProcessorInformationEx " + Marshal.GetLastWin32Error()); var b = new byte[len]; Marshal.Copy(p, b, 0, len); return b; } finally { Marshal.FreeHGlobal(p); } } // Ekranlar (HDR, karar 52): QueryDisplayConfig (etkin yollar) + DisplayConfigGetDeviceInfo. // Yapi boyutlari wingdi.h: DISPLAYCONFIG_PATH_INFO 72, MODE_INFO 64, TARGET_DEVICE_NAME 420 (ad 36. bayt, 64 WCHAR), // GET_ADVANCED_COLOR_INFO_2 (tip 15) 36 bayt, bitler 20. baytta, activeColorMode 32. baytta. [DllImport("user32.dll")] static extern int GetDisplayConfigBufferSizes(uint flags, out uint numPaths, out uint numModes); [DllImport("user32.dll")] static extern int QueryDisplayConfig(uint flags, ref uint numPaths, byte[] paths, ref uint numModes, byte[] modes, IntPtr topology); [DllImport("user32.dll")] static extern int DisplayConfigGetDeviceInfo(byte[] packet); static byte[] DevInfo(int type, int size, byte[] luid, uint id, out int rc) { var b = new byte[size]; BitConverter.GetBytes(type).CopyTo(b, 0); BitConverter.GetBytes(size).CopyTo(b, 4); luid.CopyTo(b, 8); BitConverter.GetBytes(id).CopyTo(b, 16); rc = DisplayConfigGetDeviceInfo(b); return b; } // Her etkin ekran icin: "ad|hedefId|rc15|bitler15|renkModu" public static string[] Displays() { uint np, nm; int rc = GetDisplayConfigBufferSizes(2, out np, out nm); // 2 = QDC_ONLY_ACTIVE_PATHS if (rc != 0) throw new Exception("GetDisplayConfigBufferSizes " + rc); var p = new byte[np * 72]; var m = new byte[nm * 64]; rc = QueryDisplayConfig(2, ref np, p, ref nm, m, IntPtr.Zero); if (rc != 0) throw new Exception("QueryDisplayConfig " + rc); var o = new string[np]; for (int i = 0; i < np; i++) { int off = i * 72; var luid = new byte[8]; Array.Copy(p, off + 20, luid, 0, 8); uint id = BitConverter.ToUInt32(p, off + 28); int rcN; var n = DevInfo(2, 420, luid, id, out rcN); string ad = rcN == 0 ? System.Text.Encoding.Unicode.GetString(n, 36, 128).TrimEnd('\0') : ""; int rcC; var c = DevInfo(15, 36, luid, id, out rcC); uint bit = rcC == 0 ? BitConverter.ToUInt32(c, 20) : 0; int mod = rcC == 0 ? BitConverter.ToInt32(c, 32) : -1; o[i] = ad.Replace("|", "/") + "|" + id + "|" + rcC + "|" + bit + "|" + mod; } return o; } } '@ function Initialize-KTYerel { if (-not ("KTYerel" -as [type])) { Add-Type -TypeDefinition $script:KT_YerelKaynak -ErrorAction Stop } } # ---------------- Ince sarmalayicilar (testte degistirilir) ---------------- function Get-KTOsVersionRaw { Initialize-KTYerel; $v = [KTYerel]::OsVersion(); [pscustomobject]@{ Major = $v[0]; Minor = $v[1]; Build = $v[2]; ProductType = $v[3] } } function Get-KTNativeArchRaw { Initialize-KTYerel; [KTYerel]::NativeArch() } function Get-KTPowerCapsRaw { Initialize-KTYerel; ,([KTYerel]::PowerCaps()) } function Get-KTProcInfoRaw { param([int]$Relationship) Initialize-KTYerel; ,([KTYerel]::ProcInfo($Relationship)) } function Get-KTDisplaysRaw { Initialize-KTYerel; ,([KTYerel]::Displays()) } # ---------------- Saf ayristiricilar ---------------- $script:KT_OsSurumleri = @{ 26200 = "25H2"; 26300 = "26H2" } # karar 43.1 function ConvertTo-KTOsInfo { <# .SYNOPSIS Saf. Cekirdek surumu + mimari -> @{ Os ('25H2'/'26H2'/$null); Build; Destekli; Neden }. Mimari 9 = PROCESSOR_ARCHITECTURE_AMD64 (x64). ProductType 1 = istemci (Windows 11), 3 = sunucu. #> param([Parameter(Mandatory)] $Version, [Parameter(Mandatory)][int]$Arch) $os = $script:KT_OsSurumleri[[int]$Version.Build] $neden = @() if ($Arch -ne 9) { $neden += "Yalniz x64 (amd64) destekleniyor" } if ([int]$Version.Major -ne 10 -or [int]$Version.ProductType -ne 1 -or -not $os) { $neden += "Yalniz Windows 11 25H2 (26200) ve 26H2 (26300) destekleniyor (build $($Version.Build))" } return [pscustomobject]@{ Os = $os; Build = [int]$Version.Build; Destekli = ($neden.Count -eq 0); Neden = ($neden -join "; ") } } function ConvertFrom-KTDisplays { <# .SYNOPSIS Saf. Get-KTDisplaysRaw satirlarindan ekran listesi: @{ Ad; HedefId; HdrDestek; HdrAcik } (okunamayan ekranda $null). GET_ADVANCED_COLOR_INFO_2 bitleri: 4 highDynamicRangeSupported, 5 highDynamicRangeUserEnabled; activeColorMode 2 = HDR. Tip 9'daki advancedColorSupported HDR'yi ayirt etmiyor (genis renkli ekranda da 1; Adem'in PC'si 22.10.2026). #> param([string[]]$Lines) $out = foreach ($l in @($Lines)) { if (-not $l) { continue } $f = $l.Split('|') if ($f.Count -lt 5) { continue } $ok = [int]$f[2] -eq 0; $bit = [uint32]$f[3] [pscustomobject]@{ Ad = $f[0]; HedefId = [uint32]$f[1] HdrDestek = $(if ($ok) { [bool]($bit -band 0x10) } else { $null }) HdrAcik = $(if ($ok) { [bool]($bit -band 0x20) -or [int]$f[4] -eq 2 } else { $null }) } } return @($out) } function ConvertTo-KTHags { <# .SYNOPSIS Saf. HwSchMode -> 'Acik' (2) / 'Kapali' (1) / $null (deger yok veya bilinmeyen; destek bilinmiyor). Adem'in PC'si 22.10.2026. #> param($HwSchMode) switch ("$HwSchMode") { "2" { return "Acik" } "1" { return "Kapali" } default { return $null } } } function ConvertFrom-KTPowerCaps { <# .SYNOPSIS Saf. SYSTEM_POWER_CAPABILITIES baytlari -> @{ S3; S0; Pil }. Ofsetler winnt.h: SystemS3 = 5, AoAc (S0 dusuk guc bosta, Modern Standby) = 20, SystemBatteriesPresent = 30. #> param([Parameter(Mandatory)][byte[]]$Bytes) if ($Bytes.Length -lt 32) { throw "SYSTEM_POWER_CAPABILITIES kisa: $($Bytes.Length)" } return [pscustomobject]@{ S3 = [bool]$Bytes[5]; S0 = [bool]$Bytes[20]; Pil = [bool]$Bytes[30] } } function ConvertFrom-KTProcInfo { <# .SYNOPSIS Saf. SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX dizisini gezer. Her kayit: Relationship (DWORD, 0), Size (DWORD, 4), govde (8+). RelationProcessorCore (0): EfficiencyClass = bayt 9 (heterojen islemcide sifirdan farkli siniflar olur). RelationCache (2): Level = bayt 8, Type = DWORD 16 (0 Unified). Donus: @{ Cekirdek; VerimlilikSiniflari (benzersiz); L3 (Level 3 onbellek sayisi) } #> param([Parameter(Mandatory)][byte[]]$Bytes) $off = 0; $cores = 0; $classes = [System.Collections.Generic.HashSet[int]]::new(); $l3 = 0 while ($off + 8 -le $Bytes.Length) { $rel = [BitConverter]::ToInt32($Bytes, $off); $size = [BitConverter]::ToInt32($Bytes, $off + 4) if ($size -le 0 -or $off + $size -gt $Bytes.Length) { throw "Bozuk islemci bilgisi (ofset $off, boyut $size)" } if ($rel -eq 0) { $cores++; [void]$classes.Add([int]$Bytes[$off + 9]) } elseif ($rel -eq 2 -and $Bytes[$off + 8] -eq 3) { $l3++ } $off += $size } return [pscustomobject]@{ Cekirdek = $cores; VerimlilikSiniflari = @($classes); L3 = $l3 } } # ============================================================== # Sistem analizi (karar 43): Get-KTSistem -> motorun Kosullar icin kullandigi Sistem nesnesi # ============================================================== # Ilke: tespit edilemeyen bilgi $null kalir ("bilinmiyor"); ona bagli tweak "Sistem bilgisi yok" diye atlanir. # Tahmine dayanip yanlis yere yazilmaz. Get-*Raw: SALT OKUNUR. ConvertTo-*/Select-*: saf (sahte veriyle test edilir). # # Sistem nesnesi: # Os ('25H2'/'26H2'/$null), Build, Destekli, DestekNeden (cekirdekten; kayit defterinden degil) # Kasa ('Masaustu'/'Laptop'/$null), KasaNeden, Sablon (varsayilan: Masaustu yalniz kasa masaustuyse, aksi Laptop; Adem 21.10.2026) # S0, S3 (bool/$null), Bekleme (desteklenen uyku listesi 'S0'/'S3'; motor kosulu) # Cpu ('Intel'/'AMD'/$null), CpuAd, Hibrit (bool/$null), L3 (sayi/$null) # RamKB (sayi/$null) # Gpu (uretici listesi), GpuKartlari (@{ Ad; Uretici; Tur; ClassKey }), IgpuDgpu (bool/$null) # Disk ('SSD'/'HDD'/$null) # Ag (tur listesi), AgKartlari (@{ Guid; Ad; Tur; PnpId }): tum fiziksel kartlar, bagli olmayan ve USB dahil (Adem 21.10.2026) # GucCihazlari (@{ PnpId; Ad; Sinif }): Aygit Yoneticisi'nde guc tasarrufu kutusu olan aygitlar (MSPower_DeviceEnable kaydi) # Kurulu (AppX paket adlari + Edge, Chrome, Brave, OneDrive), UcuncuTaraf (uyari icin program adlari) # Ekranlar (@{ Ad; HedefId; HdrDestek; HdrAcik }), Hdr (HDR destekli ekran var mi: bool/$null) (karar 52) # Hags ('Acik'/'Kapali'/$null): HwSchMode; desteklemeyen sistemde deger yok sayilir, destek bilinmiyor (karar 53) # CopilotPlus, CppcOtonom: yontem yok; $null (bilinmiyor) (karar 57, 60) # Win32_SystemEnclosure.ChassisTypes (SMBIOS, firmware) $script:KT_LaptopChassis = @(8, 9, 10, 11, 12, 14, 18, 21, 30, 31, 32) $script:KT_DesktopChassis = @(3, 4, 5, 6, 7, 13, 15, 16, 24, 35, 36) $script:KT_GpuVendorIds = @{ "10DE" = "NVIDIA"; "1002" = "AMD"; "8086" = "Intel" } # NdisPhysicalMedium: 14 = 802.3 Ethernet, 9 = Native 802.11 (Wi-Fi) $script:KT_VirtualNicPattern = '\b(VMware|VirtualBox|Hyper-V|Virtual|VPN|WireGuard|WireSock|Wintun|Loopback|Bluetooth|TAP-Windows)\b|\bTAP-' # Uyari icin ucuncu taraf araclar (karar 43.10; Y19, karar 44.7) $script:KT_UcuncuTarafPattern = '\b(Process Lasso|ParkControl|Advanced SystemCare)\b' function Test-KTMatch { <# .SYNOPSIS Saf. Kultur bagimsiz, buyuk/kucuk harf duyarsiz regex. -match tr-TR'de 'I'yi 'i' saymaz ('VMWARE VIRTUAL' -match 'Virtual' -> False; OGRENILENLER 4). #> param([AllowNull()][string]$Text, [Parameter(Mandatory)][string]$Pattern) if ([string]::IsNullOrEmpty($Text)) { return $false } return [regex]::IsMatch($Text, $Pattern, [Text.RegularExpressions.RegexOptions]'IgnoreCase, CultureInvariant') } # ---------------- Saf ---------------- function ConvertTo-KTKasa { <# .SYNOPSIS Saf. Kasa turu + pil -> @{ Kasa; Neden }. Kasa turu masaustuyse pil UPS sayilir. Kasa turu bilinmiyorsa pil varsa Laptop; hicbir bilgi yoksa $null. #> param([AllowEmptyCollection()][int[]]$ChassisTypes = @(), $Pil = $null) if ($ChassisTypes | Where-Object { $script:KT_LaptopChassis -contains $_ }) { return [pscustomobject]@{ Kasa = "Laptop"; Neden = "Kasa turu dizustu" } } if ($ChassisTypes | Where-Object { $script:KT_DesktopChassis -contains $_ }) { $why = if ($Pil) { "Kasa turu masaustu (pil olarak gorunen cihaz UPS sayildi)" } else { "Kasa turu masaustu" } return [pscustomobject]@{ Kasa = "Masaustu"; Neden = $why } } if ($null -eq $Pil) { return [pscustomobject]@{ Kasa = $null; Neden = "Kasa turu ve pil bilgisi okunamadi" } } if ($Pil) { return [pscustomobject]@{ Kasa = "Laptop"; Neden = "Kasa turu bilinmiyor, pil var" } } return [pscustomobject]@{ Kasa = $null; Neden = "Kasa turu bilinmiyor, pil yok" } } function Select-KTSablon { <# .SYNOPSIS Saf. Varsayilan sablon: kasa masaustuyse Masaustu, laptop veya bilinmiyorsa Laptop (Adem 21.10.2026). #> param([AllowNull()][string]$Kasa) if ($Kasa -eq "Masaustu") { return "Masaustu" } else { return "Laptop" } } function ConvertTo-KTCpuVendor { <# .SYNOPSIS Saf. Win32_Processor.Manufacturer -> Intel / AMD / $null. #> param([AllowNull()][string]$Manufacturer) switch (([string]$Manufacturer).Trim()) { "GenuineIntel" { return "Intel" } "AuthenticAMD" { return "AMD" } default { return $null } } } function ConvertTo-KTGpuVendor { <# .SYNOPSIS Saf. PNPDeviceID (PCI\VEN_xxxx) -> NVIDIA / AMD / Intel / $null (sanal, uzak, bilinmeyen). #> param([AllowNull()][string]$PnpDeviceId) $m = [regex]::Match([string]$PnpDeviceId, 'VEN_([0-9A-F]{4})', 'IgnoreCase, CultureInvariant') if (-not $m.Success) { return $null } return $script:KT_GpuVendorIds[$m.Groups[1].Value.ToUpperInvariant()] } function ConvertTo-KTGpuTur { <# .SYNOPSIS Saf. Harici / Tumlesik / $null. Model adindan; emin olunamazsa $null (tahmin yok). NVIDIA: x64'te tumlesik yok -> Harici. AMD: Radeon RX/PRO/VII -> Harici; modelsiz "Radeon(TM) Graphics" / "Vega N" -> Tumlesik. Intel: Arc A/B serisi -> Harici; "Arc Graphics" (mobil) belirsiz -> $null; digerleri Tumlesik. #> param([AllowNull()][string]$Uretici, [AllowNull()][string]$Ad) $n = [string]$Ad switch ($Uretici) { "NVIDIA" { return "Harici" } "AMD" { if (Test-KTMatch $n 'Radeon.*\b(RX|PRO|VII)\b') { return "Harici" } if (Test-KTMatch $n 'Radeon(\(TM\))?\s+Graphics$|Radeon(\(TM\))?\s+\d{3}M\b|Vega\s+\d+\s+Graphics') { return "Tumlesik" } return $null } "Intel" { if (Test-KTMatch $n '\bArc(\(TM\))?\s+[AB]\d{3}') { return "Harici" } if (Test-KTMatch $n '\bArc\b') { return $null } return "Tumlesik" } default { return $null } } } function ConvertTo-KTDiskKind { <# .SYNOPSIS Saf. MediaType + BusType -> SSD / HDD / $null. NVMe her zaman SSD. #> param([AllowNull()][string]$MediaType, [AllowNull()][string]$BusType) if ($BusType -eq "NVMe") { return "SSD" } if ($MediaType -eq "SSD") { return "SSD" } if ($MediaType -eq "HDD") { return "HDD" } return $null } function ConvertTo-KTNicTur { <# .SYNOPSIS Saf. Fiziksel Ethernet / Wi-Fi; sanal, VPN, Bluetooth ve digerleri $null (urun disi). #> param([Parameter(Mandatory)] $Nic) if ($Nic.Virtual -or -not $Nic.HardwareInterface -or (Test-KTMatch ([string]$Nic.Description) $script:KT_VirtualNicPattern)) { return $null } switch ([int]$Nic.NdisPhysicalMedium) { 14 { return "Ethernet" } 9 { return "Wi-Fi" } default { return $null } } } function ConvertTo-KTGucCihazlari { <# .SYNOPSIS Saf. MSPower_DeviceEnable kayit adlarini ("_0") aygit listesiyle eslestirir: @( @{ PnpId; Ad; Sinif } ). Listede olmayan (bagli olmayan) aygit alinmaz; ayni aygit bir kez. #> param([string[]]$Instances, $Devices) $byId = @{} foreach ($d in @($Devices)) { if ($d -and $d.PnpId) { $byId[([string]$d.PnpId).ToUpperInvariant()] = $d } } $seen = @{} foreach ($i in @($Instances)) { if (-not $i) { continue } $k = ([string]($i -replace '_\d+$', '')).ToUpperInvariant() if ($seen.ContainsKey($k) -or -not $byId.ContainsKey($k)) { continue } $seen[$k] = $true $d = $byId[$k] [pscustomobject]@{ PnpId = [string]$d.PnpId; Ad = [string]$d.Ad; Sinif = [string]$d.Sinif } } } # ---------------- Ince sarmalayicilar (salt okunur; testte degistirilir) ---------------- function Get-KTChassisRaw { $t = @(); $pil = $null try { $t = @((Get-CimInstance -ClassName Win32_SystemEnclosure -ErrorAction Stop).ChassisTypes | ForEach-Object { [int]$_ }) } catch {} try { $pil = (@(Get-CimInstance -ClassName Win32_Battery -ErrorAction Stop).Count -gt 0) } catch {} return [pscustomobject]@{ ChassisTypes = $t; Pil = $pil } } function Get-KTCpuRaw { Get-CimInstance -ClassName Win32_Processor -ErrorAction Stop | Select-Object -First 1 Manufacturer, Name } function Get-KTRamRaw { # CTT WinUtil yontemi (CPU04 SvcHostSplitThresholdInKB): fiziksel bellek modullerinin toplami $sum = (Get-CimInstance -ClassName Win32_PhysicalMemory -ErrorAction Stop | Measure-Object -Property Capacity -Sum).Sum return [int64]$sum } function Get-KTGpuRaw { foreach ($vc in @(Get-CimInstance -ClassName Win32_VideoController -ErrorAction Stop)) { $key = $null if ($vc.PNPDeviceID) { # Kartin gercek sinif anahtari (karar 11): Enum\\Driver = {4d36e968-...}\NNNN; sabit 0000 kullanilmaz $k = Open-KTRegKey "HKLM:\SYSTEM\CurrentControlSet\Enum\$($vc.PNPDeviceID)" if ($k) { try { $d = $k.GetValue("Driver", $null); if ($d) { $key = "HKLM:\SYSTEM\CurrentControlSet\Control\Class\$d" } } finally { $k.Dispose() } } } [pscustomobject]@{ Ad = ([string]$vc.Name).Trim(); Pnp = [string]$vc.PNPDeviceID; ClassKey = $key } } } function Get-KTSystemDiskRaw { $letter = ([string]$env:SystemDrive).TrimEnd(':') $part = Get-Partition -DriveLetter $letter -ErrorAction Stop | Select-Object -First 1 $pd = Get-PhysicalDisk -ErrorAction Stop | Where-Object { [string]$_.DeviceId -eq [string]$part.DiskNumber } | Select-Object -First 1 if (-not $pd) { return $null } return [pscustomobject]@{ MediaType = [string]$pd.MediaType; BusType = [string]$pd.BusType } } function Get-KTNicRaw { foreach ($n in @(Get-NetAdapter -ErrorAction Stop)) { [pscustomobject]@{ Guid = [string]$n.InterfaceGuid; Ad = [string]$n.Name; Description = [string]$n.InterfaceDescription; PnpId = [string]$n.PnPDeviceID Virtual = [bool]$n.Virtual; HardwareInterface = [bool]$n.HardwareInterface; NdisPhysicalMedium = [int]$n.NdisPhysicalMedium } } } function Get-KTPowerDeviceRaw { # Yonetici gerekir. Guc tasarrufu kutusu olan aygitlarin WMI kayit adlari ve aygit listesi (sinif, ad) $ins = @(Get-CimInstance -Namespace root\wmi -ClassName MSPower_DeviceEnable -ErrorAction Stop | ForEach-Object { [string]$_.InstanceName }) $dev = @(Get-CimInstance -ClassName Win32_PnPEntity -ErrorAction Stop | ForEach-Object { [pscustomobject]@{ PnpId = [string]$_.PNPDeviceID; Ad = [string]$_.Name; Sinif = [string]$_.PNPClass } }) return [pscustomobject]@{ Instances = $ins; Devices = $dev } } function Get-KTAppxNamesRaw { # Yonetici gerekir (-AllUsers, -Online) $a = @(Get-AppxPackage -AllUsers -ErrorAction Stop | ForEach-Object { [string]$_.Name }) $p = @(Get-AppxProvisionedPackage -Online -ErrorAction Stop | ForEach-Object { [string]$_.DisplayName }) return @($a + $p | Where-Object { $_ } | Sort-Object -Unique) } function Get-KTFileExistsRaw { param([string[]]$Paths) foreach ($p in $Paths) { if ($p -and (Test-Path -LiteralPath $p -PathType Leaf)) { return $true } }; return $false } function Get-KTHwSchModeRaw { $p = Get-ItemProperty -LiteralPath 'HKLM:\SYSTEM\CurrentControlSet\Control\GraphicsDrivers' -Name HwSchMode -ErrorAction SilentlyContinue if ($p) { return $p.HwSchMode } else { return $null } } function Get-KTUninstallNamesRaw { $keys = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*" return @(Get-ItemProperty -Path $keys -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName } | ForEach-Object { ([string]$_.DisplayName).Trim() } | Sort-Object -Unique) } function Get-KTTarayiciYollari { <# .SYNOPSIS Saf. Tarayici / OneDrive bilinen kurulum yollari (makine ve kullanici kurulumu). #> param([string]$ProgramFiles, [string]$ProgramFilesX86, [string]$LocalAppData) return [ordered]@{ Edge = @("$ProgramFilesX86\Microsoft\Edge\Application\msedge.exe", "$ProgramFiles\Microsoft\Edge\Application\msedge.exe") Chrome = @("$ProgramFiles\Google\Chrome\Application\chrome.exe", "$ProgramFilesX86\Google\Chrome\Application\chrome.exe", "$LocalAppData\Google\Chrome\Application\chrome.exe") Brave = @("$ProgramFiles\BraveSoftware\Brave-Browser\Application\brave.exe", "$LocalAppData\BraveSoftware\Brave-Browser\Application\brave.exe") OneDrive = @("$ProgramFiles\Microsoft OneDrive\OneDrive.exe", "$LocalAppData\Microsoft\OneDrive\OneDrive.exe") } } # ---------------- Analiz ---------------- function Get-KTSistem { <# .SYNOPSIS Salt okunur sistem analizi. Her madde ayri denenir; okunamayan $null kalir, Hatalar listesine yazilir. Yonetici olarak calistirilmali (AppX -AllUsers). #> $s = [ordered]@{ Os = $null; Build = $null; Destekli = $false; DestekNeden = "" Kasa = $null; KasaNeden = ""; Sablon = "Laptop"; S0 = $null; S3 = $null; Bekleme = @() Cpu = $null; CpuAd = $null; Hibrit = $null; L3 = $null; CppcOtonom = $null; RamKB = $null Gpu = @(); GpuKartlari = @(); IgpuDgpu = $null; Ekranlar = @(); Hdr = $null; Hags = $null; CopilotPlus = $null Disk = $null; Ag = @(); AgKartlari = @(); GucCihazlari = @(); Kurulu = @(); UcuncuTaraf = @(); Hatalar = @() } $hata = { param($ne, $e) $s.Hatalar += "${ne}: $(ConvertTo-KTAsciiText $e.Exception.Message)" } try { $o = ConvertTo-KTOsInfo -Version (Get-KTOsVersionRaw) -Arch (Get-KTNativeArchRaw); $s.Os = $o.Os; $s.Build = $o.Build; $s.Destekli = $o.Destekli; $s.DestekNeden = $o.Neden } catch { & $hata "Windows" $_; $s.DestekNeden = "Windows surumu okunamadi" } $pc = $null try { $pc = ConvertFrom-KTPowerCaps (Get-KTPowerCapsRaw); $s.S0 = $pc.S0; $s.S3 = $pc.S3; $s.Bekleme = @(@(if ($pc.S0) { "S0" }) + @(if ($pc.S3) { "S3" })) } catch { & $hata "Guc yetenekleri" $_ } try { $ch = Get-KTChassisRaw $pil = if ($null -ne $ch.Pil) { $ch.Pil } elseif ($pc) { $pc.Pil } else { $null } $k = ConvertTo-KTKasa -ChassisTypes $ch.ChassisTypes -Pil $pil; $s.Kasa = $k.Kasa; $s.KasaNeden = $k.Neden } catch { & $hata "Kasa" $_ } $s.Sablon = Select-KTSablon $s.Kasa try { $c = Get-KTCpuRaw; $s.Cpu = ConvertTo-KTCpuVendor $c.Manufacturer; $s.CpuAd = ([string]$c.Name).Trim() } catch { & $hata "CPU" $_ } try { $pi = ConvertFrom-KTProcInfo (Get-KTProcInfoRaw 0xFFFF); $s.Hibrit = (@($pi.VerimlilikSiniflari).Count -gt 1); $s.L3 = $pi.L3 } catch { & $hata "CPU yapisi" $_ } try { $r = Get-KTRamRaw; if ($r -gt 0) { $s.RamKB = [int64]($r / 1KB) } } catch { & $hata "RAM" $_ } try { $kartlar = foreach ($g in @(Get-KTGpuRaw)) { $u = ConvertTo-KTGpuVendor $g.Pnp if (-not $u) { continue } # sanal / uzak masaustu / bilinmeyen uretici [pscustomobject]@{ Ad = $g.Ad; Uretici = $u; Tur = (ConvertTo-KTGpuTur $u $g.Ad); ClassKey = $g.ClassKey } } $s.GpuKartlari = @($kartlar) $s.Gpu = @($s.GpuKartlari | ForEach-Object { $_.Uretici } | Sort-Object -Unique) $turler = @($s.GpuKartlari | ForEach-Object { $_.Tur }) $s.IgpuDgpu = if ($turler -contains "Tumlesik" -and $turler -contains "Harici") { $true } elseif ($turler -contains $null) { $null } else { $false } } catch { & $hata "Ekran karti" $_ } try { $s.Ekranlar = @(ConvertFrom-KTDisplays (Get-KTDisplaysRaw)) $okunan = @($s.Ekranlar | Where-Object { $null -ne $_.HdrDestek }) if ($okunan.Count -gt 0) { $s.Hdr = [bool](@($okunan | Where-Object { $_.HdrDestek }).Count) } } catch { & $hata "Ekran" $_ } try { $s.Hags = ConvertTo-KTHags (Get-KTHwSchModeRaw) } catch { & $hata "HAGS" $_ } try { $d = Get-KTSystemDiskRaw; if ($d) { $s.Disk = ConvertTo-KTDiskKind $d.MediaType $d.BusType } } catch { & $hata "Disk" $_ } try { $nics = foreach ($n in @(Get-KTNicRaw)) { $t = ConvertTo-KTNicTur $n if ($t) { [pscustomobject]@{ Guid = $n.Guid; Ad = $n.Ad; Tur = $t; PnpId = $n.PnpId } } } $s.AgKartlari = @($nics) $s.Ag = @($s.AgKartlari | ForEach-Object { $_.Tur } | Sort-Object -Unique) } catch { & $hata "Ag karti" $_ } try { $p = Get-KTPowerDeviceRaw; $s.GucCihazlari = @(ConvertTo-KTGucCihazlari -Instances $p.Instances -Devices $p.Devices) } catch { & $hata "Aygit guc yonetimi" $_ } try { $kur = [System.Collections.Generic.List[string]]::new() try { foreach ($a in @(Get-KTAppxNamesRaw)) { $kur.Add($a) } } catch { & $hata "AppX" $_ } $yol = Get-KTTarayiciYollari -ProgramFiles $env:ProgramFiles -ProgramFilesX86 ${env:ProgramFiles(x86)} -LocalAppData $env:LOCALAPPDATA foreach ($ad in $yol.Keys) { if (Get-KTFileExistsRaw $yol[$ad]) { $kur.Add($ad) } } $s.Kurulu = @($kur | Sort-Object -Unique) } catch { & $hata "Kurulu" $_ } try { $s.UcuncuTaraf = @(Get-KTUninstallNamesRaw | Where-Object { Test-KTMatch $_ $script:KT_UcuncuTarafPattern }) } catch { & $hata "Ucuncu taraf" $_ } return [pscustomobject]$s } # ============================================================== # Arayuz: analiz satirlari ve pencereye gidecek liste (karar 85) # ============================================================== # Akis: Get-KTSistem -> Format-KTAnalizSatirlari (PowerShell'e satir satir) -> Get-KTDurumlar (her tweak -Oku, yalniz okur) # -> New-KTPencereListesi (saf) -> pencere. # Sayfalar: Tweakler (sablonda secili gelen), IstegeBagli (secili gelmeyen secmeliler), Kisisellestirme (sayfa 2'de ayri baslik), # Uygulamalar (yalniz kurulu olanlar). Bu sistemde uygulanamayan tweak listeye girmez. $script:KT_Sayfalar = "Tweakler", "IstegeBagli", "Kisisellestirme", "Uygulamalar" function Format-KTAnalizSatirlari { <# .SYNOPSIS Saf. Sistem analizi ciktisini PowerShell'de gosterilecek ASCII satirlara cevirir. Bilinmeyen alan "bilinmiyor". #> param([Parameter(Mandatory)] $Sistem) $b = { param($v) if ($null -eq $v -or "$v" -eq "") { "bilinmiyor" } else { ConvertTo-KTAsciiText ([string]$v) } } $kartlar = @($Sistem.GpuKartlari | ForEach-Object { $_.Ad }) -join ", " $ekran = @($Sistem.Ekranlar | ForEach-Object { if ($_.HdrAcik) { "$($_.Ad) (HDR acik)" } else { $_.Ad } }) -join ", " $ag = @($Sistem.AgKartlari | ForEach-Object { "$($_.Ad) ($($_.Tur))" }) -join ", " $ram = if ($Sistem.RamKB) { "{0} GB" -f [math]::Round([double]$Sistem.RamKB / 1MB) } else { $null } $satir = @( "Windows : $(& $b $Sistem.Os) (build $(& $b $Sistem.Build))" "Kasa : $(& $b $Sistem.Kasa) -> sablon $(& $b $Sistem.Sablon)" "Islemci : $(& $b $Sistem.CpuAd)" "Bellek : $(& $b $ram)" "Ekran karti : $(& $b $kartlar)" "Ekranlar : $(& $b $ekran)" "Sistem diski: $(& $b $Sistem.Disk)" "Ag : $(& $b $ag)" ) # Kurulu programlar alt alta (Adem 25.10.2026) $kurulu = @($Sistem.Kurulu | Where-Object { $_ } | Sort-Object) $satir += "Kurulu : $(if ($kurulu.Count) { "$($kurulu.Count) adet" } else { 'bilinmiyor' })" foreach ($k in $kurulu) { $satir += " $(ConvertTo-KTAsciiText ([string]$k))" } if (-not $Sistem.Destekli) { $satir += "Desteklenmeyen sistem: $(& $b $Sistem.DestekNeden)" } foreach ($h in @($Sistem.Hatalar)) { if ($h) { $satir += "Okunamadi : $(ConvertTo-KTAsciiText ([string]$h))" } } return $satir } function Get-KTSayfa { <# .SYNOPSIS Saf. Tweak hangi sayfada: Uygulamalar (UYGULAMA), Kisisellestirme, sablonda secili gelirse Tweakler, degilse IstegeBagli. IstegeBagli alani $true ise secili gelse de IstegeBagli (karar 102). #> param([Parameter(Mandatory)] $Tweak, [Parameter(Mandatory)][ValidateSet("Masaustu", "Laptop")][string]$Sablon) if ([string]$Tweak.Kategori -eq "UYGULAMA") { return "Uygulamalar" } if ([string]$Tweak.Sablon -eq "Kisisellestirme") { return "Kisisellestirme" } if (Get-KTField $Tweak IstegeBagli) { return "IstegeBagli" } if (Test-KTSecili -Tweak $Tweak -Sablon $Sablon) { return "Tweakler" } return "IstegeBagli" } function Get-KTDurumlar { <# .SYNOPSIS Her tweak'in bu sistemdeki durumunu okur (Invoke-KTTweak -Oku; sisteme yazmaz). Donus: Id -> sonuc nesnesi. Yaz verilirse her 25 tweak'te bir ilerleme satiri icin cagrilir (PowerShell ekrani). #> param([Parameter(Mandatory)] $Tweaks, $Sistem, [scriptblock]$Yaz) $d = @{} $liste = @($Tweaks); $i = 0 foreach ($t in $liste) { $i++ try { $d[$t.Id] = Invoke-KTTweak -Tweak $t -Sistem $Sistem -Oku } catch { $d[$t.Id] = [pscustomobject]@{ Id = $t.Id; Sonuc = "Okunamadi"; Neden = "($(ConvertTo-KTAsciiText $_.Exception.Message))" } } # Yaz'in ciktisi ekrana gider, donus degerine karismaz if ($Yaz -and ($i % 25 -eq 0 -or $i -eq $liste.Count)) { & $Yaz "Tweak durumu okunuyor: $i / $($liste.Count)" | Out-Host } } return $d } function New-KTPencereListesi { <# .SYNOPSIS Saf. Pencerede gosterilecek satirlar. Durumu "Uygun degil" olan tweak listeye girmez. Secili: sablonda secili gelir mi (Test-KTSecili); zaten uygulanmis olan secili gelmez, pencerede gri (karar 89, 93). Not: satirin yaninda yazan parantezli metinler (uyari, laptop uyarisi, geri alinamaz, okunamadi). #> param([Parameter(Mandatory)] $Tweaks, $Sistem, [Parameter(Mandatory)][ValidateSet("Masaustu", "Laptop")][string]$Sablon, [hashtable]$Durumlar = @{}) foreach ($t in @($Tweaks)) { $d = $Durumlar[$t.Id] $sonuc = if ($d) { [string]$d.Sonuc } else { "" } if ($sonuc -eq "Uygun degil") { continue } # Kaldirilacak uygulama sistemde yoksa satir hic gosterilmez (Adem 08.11.2026). Yanindaki kayit islemi # (APP112 Copilot dugmesi, APP136 Widgets ilkesi) uygulanmis olsa da satir gri "zaten uygulanmis" gorunmez. if ([string]$t.Kategori -eq "UYGULAMA" -and $d) { $app = @($d.Islemler | Where-Object { $_ -and [string]$_.Tur -eq "Uygulama" }) if ($app.Count -gt 0 -and @($app | Where-Object { [string]$_.Durum -ne "Sistemde yok" }).Count -eq 0) { continue } } $not =[System.Collections.Generic.List[string]]::new() $u = Get-KTUyari -Tweak $t -Sistem $Sistem; if ($u) { $not.Add($u) } $geri = Test-KTTweakIrreversible -Tweak $t # Uygulamalar sayfasi basliginda bir kez yazar; zaten uygulanmis satir pencerede gri yazilir (karar 89) if ($geri -and [string]$t.Kategori -ne "UYGULAMA") { $not.Add("(Geri alinamaz)") } if ($sonuc -eq "Okunamadi") { $not.Add("(Durumu okunamadi)") } [pscustomobject]@{ Id = $t.Id Ad = $t.Ad Aciklama = $t.Aciklama Kategori = $t.Kategori Sayfa = Get-KTSayfa -Tweak $t -Sablon $Sablon Secili = (Test-KTSecili -Tweak $t -Sablon $Sablon) -and $sonuc -ne "Zaten uygulanmis" Not = ($not -join " ") GeriAlinamaz = $geri Zaten = ($sonuc -eq "Zaten uygulanmis") Secenekler = @($t.Secenekler | Where-Object { $_ } | ForEach-Object { [string]$_.Ad }) YenidenBaslat = $t.YenidenBaslat Varsayilan = (@($t.Varsayilan | Where-Object { $_ }).Count -gt 0) } } } # ============================================================== # Arayuz: aydinlik / karanlik tema (Adem 25.10.2026) # ============================================================== # Pencere renkleri kaynak (resource) anahtarlaridir; XAML DynamicResource, koddaki ogeler SetResourceReference ile baglanir. # Set-KTTema anahtarlarin degerini degistirir, pencere aninda yeni renge gecer. Baslangic temasi Windows'un uygulama temasi. $script:KT_Temalar = @{ Acik = [ordered]@{ Arka = "#F3F3F3"; Kart = "#FFFFFF"; KartKenar = "#E5E5E5"; Yazi = "#1B1B1B"; Ikinci = "#5F5F5F"; Soluk = "#A0A0A0" Uyari = "#8A5300"; Dugme = "#FBFBFB"; DugmeKenar = "#D6D6D6"; DugmeUst = "#F0F0F0"; Vurgu = "#005FB8"; VurguYazi = "#FFFFFF" Bant = "#FFF4CE"; BantKenar = "#F2D46B"; Alan = "#FFFFFF"; Iz = "#E0E0E0"; Nokta = "#C4C4C4"; Kutu = "#8A8A8A"; Secim = "#E8F1FB" } Koyu = [ordered]@{ Arka = "#202020"; Kart = "#2B2B2B"; KartKenar = "#3B3B3B"; Yazi = "#F3F3F3"; Ikinci = "#C8C8C8"; Soluk = "#7C7C7C" Uyari = "#F2C661"; Dugme = "#373737"; DugmeKenar = "#474747"; DugmeUst = "#3F3F3F"; Vurgu = "#60CDFF"; VurguYazi = "#000000" Bant = "#3D3420"; BantKenar = "#6E5C25"; Alan = "#2D2D2D"; Iz = "#474747"; Nokta = "#5C5C5C"; Kutu = "#9A9A9A"; Secim = "#1F3A4D" } } function Get-KTWindowsTemasi { <# .SYNOPSIS Windows'un uygulama temasi: 'Koyu' (AppsUseLightTheme = 0) veya 'Acik' (deger yoksa da). #> $v = (Get-ItemProperty -LiteralPath 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize' -Name AppsUseLightTheme -ErrorAction SilentlyContinue).AppsUseLightTheme if ($null -ne $v -and [int]$v -eq 0) { return "Koyu" } else { return "Acik" } } $script:KT_BaslikKaynak = @' using System; using System.Runtime.InteropServices; public static class KTBaslik { [DllImport("dwmapi.dll")] static extern int DwmSetWindowAttribute(IntPtr h, int a, ref int v, int s); // DWMWA_USE_IMMERSIVE_DARK_MODE = 20 (Windows 11): baslik cubugu koyu public static void Koyu(IntPtr h, bool on) { int v = on ? 1 : 0; DwmSetWindowAttribute(h, 20, ref v, 4); } } '@ function Set-KTTema { <# .SYNOPSIS Pencerenin renk kaynaklarini verilen temaya ayarlar; pencere aciksa baslik cubugu da koyu/acik olur. #> param([Parameter(Mandatory)] $Pencere, [Parameter(Mandatory)][ValidateSet("Acik", "Koyu")][string]$Ad) foreach ($k in $script:KT_Temalar[$Ad].Keys) { # Tip donusumu: New-Object PSObject sarmali verir, WPF kaynak olarak kabul etmez $Pencere.Resources[$k] = [System.Windows.Media.SolidColorBrush][System.Windows.Media.Color]([System.Windows.Media.ColorConverter]::ConvertFromString($script:KT_Temalar[$Ad][$k])) } try { $h = (New-Object System.Windows.Interop.WindowInteropHelper $Pencere).Handle if ($h -ne [IntPtr]::Zero) { if (-not ("KTBaslik" -as [type])) { Add-Type -TypeDefinition $script:KT_BaslikKaynak -ErrorAction Stop } [KTBaslik]::Koyu($h, $Ad -eq "Koyu") } } catch { } # baslik rengi yalniz gorunum; olmazsa tema yine uygulanir } # ============================================================== # Arayuz: WPF pencere (karar 85) # ============================================================== # Sayfa 1 Tweakler, sayfa 2 Istege bagli (+ Kisisellestirme ayri baslik), sayfa 3 Uygulamalar (yalniz listede uygulama varsa). # Kartlar 3 sutuna, en kisa sutuna eklenerek dagitilir. Sablon degisince liste yeniden kurulur (secimler sifirlanir). # -Onizleme: Uygula kapali (sisteme dokunmaz). -Goruntu: her sayfayi PNG olarak klasore yazar ve pencereyi kapatir. $script:KT_KategoriAdlari = [ordered]@{ CPU = "Islemci"; "SYS/CPU" = "Islemci ve sistem"; SYS = "Sistem"; GPU = "Ekran karti"; DSP = "Ekran"; NET = "Ag"; INP = "Fare ve klavye" MEM = "Bellek ve disk"; "MEM/STORAGE" = "Bellek ve disk"; SVC = "Hizmetler"; TSK = "Zamanlanmis gorevler"; PRV = "Gizlilik"; AI = "Yapay zeka" SEC = "Guvenlik"; DBL = "Gereksiz ozellikler"; WU = "Windows Update"; TEMIZLIK = "Temizlik"; OYUN = "Oyun"; QOL = "Gorunum ve kullanim" UYGULAMA = "Uygulamalar" } $script:KT_PencereXaml = @'