powershell etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
powershell etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

2026-06-15

Klavye vuruşları göndermek

Powershell ile bir pencereye klavye vuruşları göndermek mümkün. Bunu bir animasyon ile yapmak istedim. 3 not defteri süreci oluşturup, her birinin içine birer cümle ekleyip, sonra da bunları kapatmak istiyorum.

Açılan bir not defteri penceresinin içine bir kelime yazmak için

[System.Windows.Forms.SendKeys]::SendWait()

 kullanmam gerek. Ama bunu kullanmak için önce

Add-Type -AssemblyName System.Windows.Forms

eklemeliyim. Kapatırken de bir not defteri sürecini diğerinden ayırt etmek için pencerenin başlığına göre ayırt etmek için

[Microsoft.VisualBasic.Interaction]::AppActivate("Not Defteri")

gibi bir şey kullanmak istiyorum. Kapatırken Alt+F4 komutunun karşılığını

[System.Windows.Forms.SendKeys]::SendWait("%{F4}")

 ile göndermek, bu sorunun cevabını "Hayır" ile cevaplamak için "n" kısayolunu da

[System.Windows.Forms.SendKeys]::SendWait("n")

ile göndermek istiyorum. Her not defteri penceresinin içeriği olacak kelimeleri de aşağıdaki gibi bir değişkene koydum.

$kelimeler = "Bugun","gunlerden","pazartesi"

Hepsini tek bir betiğe koyunca:

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName Microsoft.VisualBasic

$kelimeler = "Bugun","gunlerden","pazartesi"

$kelimeler | % {
    start notepad
    sleep -Milliseconds 500
    [System.Windows.Forms.SendKeys]::SendWait($_)
}

sleep -Milliseconds 500

try {
    $kelimeler | % {
        [Microsoft.VisualBasic.Interaction]::AppActivate("$_, Not Defteri")
        [System.Windows.Forms.SendKeys]::SendWait("%{F4}")
        sleep -Milliseconds 500
        [System.Windows.Forms.SendKeys]::SendWait("n")
        sleep -Milliseconds 500
    }
}
finally {
    ps notepad -ea silent | kill -force -ea silent
}

Kapatmak için pencere başlığına göre Alt+F4 göndermek istedim, ama Türkçe yerelleştirme dışında herhangi bir durumda AppActivate çalışmayacaktır. Bu durum için try - finally ile bütün not defteri süreçlerini sonlandırdım.

2026-06-11

Uygulama kilitlenince bildirim gelsin

Windows bir makine üzerinde çalışan bir uygulamamız var. Bu uygulama, nedendir bilinmez, bazen kilitleniyor. Böyle durumlar sonrasında yapmasını beklediğimiz işlemi yapamadığı için kesinti oluyor. Bunun önüne geçmek istiyorum.

Windows, uygulamanın bu şekilde kilitlendiği (Application hang) durumunda Uygulama olay günlüğüne 1002 kimliği ile bir kayıt düşüyor.

Logname = Application
ProviderName = Application Hang
Id = 1002

Bu olay kaydında kilitlenen uygulamanın başlatıldığı tarih ve saat bilgisi de yer alıyor. Bu bilgileri şimdilik eposta yoluyla bana atmasını istiyorum. Bu amaçla bir betik oluşturdum. Betik, son 1 dakika içinde oluşan bu tipteki bir olayları listeleyip sonuncusunu bir değişkene atıyor.

$ev1 = Get-WinEvent -FilterhashTable @{
LogName='Application';
ID=1002;
StartTime=(Get-Date).AddMinutes(-1);
ProviderName='Application Hang'
} -ErrorAction SilentlyContinue |
    Where-Object {$_.Properties[0].Value -eq "uygulama.exe"} |
    Select-Object -First 1

Olay kaydı içinde şu bilgiler var:

Uygulama adı : $_.Properties[0]

Uygulama kimliği : $ev1.Properties[2].Value 

Başlangıç zamanı : $ev1.Properties[3].Value

Kilitlenme tipi : $ev1.Properties[9].Value

Ancak başlangıç zamanı şurada yazdığım gibi, FILETIME verisi. Bunu okunabilir bir şekle getirmek için

Get-Date -Format "yyyy-MM-dd HH:mm:ss" -Date ([datetime]::FromFileTimeUtc($ev1.Properties[3].Value))

kullanıyorum. Ayrıca gelen eposta içinde bilgisayarın başlatılma zamanı da olsa iyi olur. Bu sebeple CIM içinden bu bilgiyi de çekiyorum:

Bilgisayar açılış : (gcim win32_operatingsystem).LastBootUpTime

Tarih verilerini görüntülemede yyyy-MM-dd HH:mm:ss şeklinde format görmek istediğim için bunu da dönüştürüyorum:

Get-Date -Format "yyyy-MM-dd HH:mm:ss" -Date ((gcim win32_operatingsystem).LastBootUpTime)

Olay kaydında bir de sonlanma zamanı var, Properties[4] içinde, ama bu alanda hep  4294967295 verisi var. Bu da aslında aslında henüz süreç resmi olarak sonlanmadığı için 0xFFFFFFFF değerine eşit olan

[uint32]::MaxValue

Dolayısıyla bu alanı kullanmamıza gerek yok.

Bu betiği C:\Scripts\betik1.ps1 gibi bir konuma kopyaladıktan sonra zamanlanmış görevi oluşturmak için aşağıdaki kodu kullandım.

schtasks /Create
        /TN "Uygulama Sonlanma Bildirimi"
        /SC ONEVENT
        /EC "Application"
        /MO "*[System[Provider[@Name='Application Hang'] and (EventID=1002)]]"
        /TR "powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\betik1.ps1"

Böyle bir durumda eposta göndermesin, sadece uygulamayı sadece yeniden başlatılsın denebilir. Bu durumda basit bir şekilde

ps uygulama.exe | kill
start uygulama.exe

gibi içeriğe sahip bir betik iş görür. 

2026-06-02

EFI disk bölümüne sürücü harfi ataması yapmak

Şu konuyla ilgilenirken alternatif yöntemlere denk geldim, çözümün şıklığı hoşuma gitti. Öncelikle hangi disk bölümünün EFI bölümü olduğunu bulmak için

$EFIbolumu = gcim win32_volume | where SystemVolume -eq $true

Bulduğumuzdan emin olmak istersek

$EFIbolumu | ft DriveLetter, @{N="SizeMB";E={[int]($_.Capacity/1MB)}},
    @{N="FreeMB";E={[int]($_.FreeSpace/1MB)}}, 
    Label, SystemVolume, FileSystem, PageFilePresent, BootVolume

Bunun sonucunda benim durumumda şöyle bir çıktı verdi:

DriveLetter SizeMB FreeMB Label SystemVolume FileSystem PageFilePresent BootVolume
----------- ------ ------ ----- ------------ ---------- --------------- ----------
                96     45               True FAT32                False      False

EFI sürücüleri için SystemVolume = True, C: sürücüsü gibi önyükleme bölümleri için ise BootVolume = True olur. Ek ayırt edici olarak bir de PageFilePresent'i kadraja aldım, emin olmak için. Genellikle EFI bölümleri 100 MB kadar ve FAT32 yapıda olurlar.

Son aşamada Set-CimInstance alias'ı scim'i kullanıyoruz.

$EFIbolumu | where SystemVolume -eq $true | scim -Property @{DriveLetter='E:'} 

Benzer şekilde birim etiketini değiştirmek için

$EFIbolumu | where SystemVolume -eq $true | scim -Property @{Caption='EFI-part'} 

Harf atamasını geri almak için yöntem aynı:

mountvol E: /d 

---

https://devblogs.microsoft.com/powershell-community/changing-drive-letters-and-labels-via-powershell/ 

2026-05-06

Şişmiş bir CapabilityAccessManager.db-wal dosyası

Bir bilgisayarda C: sürücüsünde yer kalmamış. Sysinternals aracı du64 ile diski adım adım inceledim.

du64 -q -l 1 C:\
du64 -q -l 1 C:\ProgramData
du64 -q -l 1 C:\ProgramData\Microsoft
... 

ve sonunda

C:\Programdata\Microsoft\Windows\CapabilityAccessManager\CapabilityAccessManager.db-wal

dosyasının boyutunun +130 GB olduğunu gördüm. Bu dosyaya kamera, mikrofon ve konum gibi bilgilere erişim isteyen uygulamalar kaydediliyor. Bu dosyaya erişen hizmet ise camsvc. Bu klasöre ve altındaki dosyalara sadece NT AUTHORITY\SYSTEM hesabı erişebiliyor. Bu dosya bir SQLite veritabanı. Dosyayı incelemek için önce sahibini ve yetkilerini değiştirmem gerekti.

gsv camsvc | spsv
Set-Service camsvc -startuptype disabled
$dosya = "C:\Programdata\Microsoft\Windows\CapabilityAccessManager\CapabilityAccessManager.db-wal"
$acl1 = Get-Acl $path
$acl1.SetOwner([System.Security.Principal.NTAccount]"DOMAIN\user")
$kural1 = New-Object System.Security.AccessControl.FileSystemAccessRule("DOMAIN\user","Read","Allow")
$acl1.AddAccessRule($kural1)
Set-Acl $path $acl1

Daha sonra bu .db-wal uzatılı dosya ile birlikte .db ve .db-shm dosyaları bilgisayarıma aktarıp (yukarıdaki yetkilendirmeyi onlara da yaptıktan sonra) SQLite ile incelemek istedim. Bu aşamada sqlitebrowser.org adresinden DB Browser for SQLite'ı indirip .db uzantılı dosyayı açtım. Aşağıdaki SQLite sorgusunu kullanarak ezici bir çoğunlukla +56000 kez konum bilgisinin ExpressConnectNetworkService tarafından talep edildiğini gördüm.

SELECT
    b.StringValue AS ExecutablePath,
    c.StringValue AS CapabilityName,
    COUNT(*) AS RequestCount
FROM NonPackagedUsageHistory h
JOIN BinaryFullPaths b ON b.rowid = h.BinaryFullPath
JOIN Capabilities c ON c.rowid = h.Capability
GROUP BY h.BinaryFullPath, h.Capability
ORDER BY RequestCount DESC
LIMIT 20;

ExpressConnectNetworkService'in vazgeçilmez bir bileşen olmadığı (bir bloatware?) ortada. Şimdilik devre dışı bıraktım.

gsv 'ExpressConnect Network Service' | spsv
set-service 'ExpressConnect Network Service' -StartupType disabled

Hatta DellOptimizer hizmetini de devre dışı bıraktım. Ama Dell hizmetlerinin bir süre sonra tekrar kendiliğinden etkinleşmek gibi kötü huyları var. Bir süre izleyip göreceğim.

2026-04-10

Tek elemanlı diziler

Bir dizi (array) oluşturuyorum.

$tamliste = @()

Sonra bu dizinin içini dolduruyorum. Ama işin bir yerinde bu diziyi, bir kritere göre filtrelemek istiyorum.

$filtrelenmis = $tamliste | Where-Object { $_.Name -match $anahtarkelime }

Sonrasında filtrelenmiş $filtrelenmis üzerinde bir for döngüsü ile işlem yapmam gerek.

for ($i = 0; $i -lt $filtrelenmis.Count; $i++) {
    Write-Host "[$i] $($filtrelenmis[$i].Name)"
}

Ama öncesinde boş bir dizi dönüp dönmediğini denetliyorum. Boş bir dizi döndüyse bunu bildirmem gerek.

if ($filtrelenmis.Count -eq 0) {
    Write-Host "Bos liste dondu."
} else {
    Write-Host "Bulunan:`r`n------"
    for ($i = 0; $i -lt $filtrelenmis.Count; $i++) {
        Write-Host "[$i] $($filtrelenmis[$i].Name)"
    }
}

$filtrelenmis değişkeninin sadece 1 elemanlı olması durumunda $filterelenmis.Count'un 1 dönmesini bekleriz. Ama dizinin 1 elemanlı olması durumunda bu artık bir dizi olmuyor, sadece düz bir değişken oluyor. Bu sebeple de .Count tanımlı değil.

Bu durumu düzeltmek için filtreleme satırını

$filtrelenmis = @($tamliste | Where-Object { $_.Name -match $anahtarkelime })

şeklinde düzeltmek gerek.

2026-03-26

Powershell'in sayıları

Powershell, .Net altyapısı üzerine kurulu olduğu için onun veri tiplerini kullanıyor. Herhangi bir veri tipini kullanarak bir değişkende depolanabilecek en küçük ve en büyük sayıyı bulmak için ::MinValue ve ::MaxValue eklemek yeterli. Örneğin 32 bitlik bir tam sayının en küçük ve en büyük değerlerini bulmak için

> [int]::MinValue
-2147483648

> [int]::MaxValue
2147483647

İşaretsiz bir tam sayı için

> [uint32]::MinValue
0

> [uint]::MaxValue
4294967295

İşaretsiz tam sayıların en küçük değerini sorgulamak gereksiz olduğu için 64 bitlik bir işaretsiz tam sayının en büyük değerini sorgulayalım.

> [uint64]::MaxValue 
18446744073709551615

Benzer bir şekilde float ve double için de en küçük ve büyük değerler

> [float]::MaxValue 
> [double]::MaxValue

float ve double veri tiplerinin hassasiyetleri için ::Epsilon kullanılabilir. Bu veri tipleri işaretli (signed) olduğu için en küçük değerin mutlak değeri ile en büyük değer arasında çok küçük farklar var. Ama mutlak değeri en küçük (yani sıfıra en yakın, bir bütünün en küçük parçası) sayıyı bulmaktan bahsediyorum.

> [float]::Epsilon
1,401298E-45

> [double]::Epsilon
4,94065645841247E-324

long denen veri tipi aslında işaretli bir int64, 

2026-03-07

powershell muhtelif-3

Bir betiğin içinden mevcut klasörün yolunu tutan değişken

$PSScriptRoot

betiğin tam yolunu tutan değişken ise

$MyInvocation.MyCommand.Definition

Betiğin içinde çalıştığı powershell sürecinin kimlikğini görmek için (uzak oturumlarda bu, wsmprovhost'un süreç numarasını verir)

$PID

Son komut hatasız çalıştı mı sorusuna cevap (sorunun cevabı evetse True olur): 

$? 

Çalışan powershell süreci hakkında daha fazla bilgi:

[System.Diagnostics.Process]::GetCurrentProcess()

Mevcut bilgisayarın adını almak için (herhangi biri)

hostname
$env:Computername
[System.Environment]::MachineName
[System.Net.Dns]::GetHostName().Name

Oturum açmış kullanıcı adını almak için (herhangi biri)

whoami
$env:Username
[System.Environment]::UserName
[System.Security.Principal.WindowsIdentity]::GetCurrent().Name 

 IP adresini göster (çünkü Get-NetIPAddress çok yavaş)

gcim win32_networkadapterconfiguration -filter "ipenabled = 'True'"

 

2026-03-06

Uzaktaki bilgisayar üzerinde bir kısayol oluşturmak

Uzak bilgisayar üzerinde bir hedef dosyam var, D:\klasor\dosya.txt adında. Kullanıcı masaüstünde bu dosyaya bir kısayol oluşturmak istiyorum. Kullanıcı masaüstü de en genel haliyle C:\Users\kullanici\Desktop gibi bir yerde.

$hedefdosya = "D:\klasor\dosya.txt"
$kisayolkonumu = "C:\Users\kullanici\Desktop\dosya.lnk"

Kısayolu oluşturmak için WScript.Shell nesnesi oluşturacağım.

$WS1 = New-Object -ComObject WScript.Shell
$kisayol = $WS1.CreateShortcut($kisayolkonumu)
$kisayol.TargetPath = $hedefdosya
$kisayol.Save() 

---

https://powershellfaqs.com/create-a-shortcut-to-a-folder-using-powershell/ 

2026-03-05

Uzak bilgisayardaki nonpaged pool bellek miktarına ulaşmak

Uzak bir bilgisayardaki  "disk belleği olmayan havuz" değerine ulaşmak istedim. Bunun için iki seçenek var. Performans sayaçları (performance counters) ve WMI (ya da yeni adıyla CIM) sorguları.

Performans sayaçları konusu biraz karışık. Dile göre farklılık gösterebiliyor. Örnek olarak uzak bilgisayar İngilizce yerelleştirmeye sahipse bu sayaç değerini okumak için.

(Get-Counter -Computername uzakpc "\Memory\Pool Nonpaged Bytes").CounterSamples.CookedValue

yeterli olurken, Türkçe bir bilgisayarda 

(Get-Counter -Computername uzakpc "\Bellek\disk belleği olmayan havuz bayt sayısı").CounterSamples.CookedValue

çalışmadı. Onun yerine terminalden

Invoke-Command -Computername uzakpc {Get-Counter "\Bellek\disk belleği olmayan havuz bayt sayısı").CounterSamples.CookedValue}

ile sonuca ulaştım. Ancak bu satırı kopyalayıp bir metin dosyasına kaydedip ps1 uzantılı bir betik dosyası oluşturduğumda aldığım hata

Internal performance counter API call failed. Error: c0000bb9.
    + CategoryInfo          : InvalidResult: (:) [Get-Counter], Exception
    + FullyQualifiedErrorId : CounterApiError,Microsoft.PowerShell.Commands.GetCounterCommand
    + PSComputerName        : uzakpc 

oldu. Bir sorun var. O da dosyayı UTF-8 olarak kaydetmek. Kodlamayı ANSI olarak değiştirince sorun çözüldü. Şu sayfada powershell'in ps1 dosyalarında UTF-8 kodlamasını da kabul ettiği, sorunun sadece bazı karakterlerin kopyala yapıştır sırasında yanlış yerleştirildiğinden bahsedilmiş. Bu konu henüz incelenmedi.

İkinci yöntem olarak CIM sorgusu ile de ilerleyebilirdim.

(Get-CIMInstance -Computername uzakpc Win32_PerfFormattedData_PerfOS_Memory).PoolNonpagedBytes

Ama ilginç bir şekilde ilk kullanımda bu değeri dönmesi uzun sürüyor (+1 dakika). 

2026-02-09

ipinfo.io ve request headers

Daha önce şurada ve öteki şurada yazdığım gibi IP adresindek konum bilgisine ulaşmak kolay. Bir de ipinfo.io hizmetini öğrendim. Yedek hizmetlerin olması güzel bişey.

ipinfo.io'nun dökümanlarında (elbette ücretsiz bir hesap oluşturup bir api-key edindikten sonra - örneğin 12345678)

curl -H "Authorization: Bearer 12345678" https://ipinfo.io/8.8.8.8

gibi bir komut ile sorgu yapılabileceğinden bahsetmiş. Powershell'de bunun eşdeğeri

irm -uri https://ipinfo.io/8.8.8.8 -headers @{Authorization: "Bearer 12345678"} -useb

olabilir. api-key header içinde gidiyor, ilk bağlantıda verdiğim ipstack.com için bu adres satırından GET ile (request string içinde) gidiyordu. ipinfo POST istemiş.

Ücretsiz bir hesap oluşturmadan da sorgu yapılabiliyor, ama sorguların sonunda dönen JSON verisinde

readme   : https://ipinfo.io/missingauth

gibi bir ilave alan da oluyor ve tıklandığında açılan sayfada hesap açılması öneriliyor. Hesap açılmadan (without authorization) yapılan sorguların artık "eski yöntem" (legacy) olduğu söylenmiş ve hesap açılması salık verilmiş.

2026-02-03

İki klasörün içindeki tekrarlayan dosyaları bulmak

D:\dizin1 ve D:\dizin2 gibi iki klasörüm ve içinde bazı dosyalarım var. Acaba Dizin1'de olan dosyaların herhangi biri Dizin2'de de olabilir mi? Görmek için önce iki dizinin de dosyalarını birer array değişkene aktardım.

$ar1 = dir -path D:\dizin1 -File
$ar2 = dir -path D:\dizin2 -File

Sonra da dizin1'deki her dosyanın dizin2'de olup olmadığını görmek için bir döngü oluşturdum. 

$ar1 | % {if ($_ -in $ar2) {$_}}

Bu şekilde olmadı. Neden? Çünkü $ar1'in elemanlarından her birini temsil eden $_ nesnesi tam olarak $ar2'nin  elemanlarından biri ile tamamen aynı değildir. Çünkü içinde FullName gibi sadece dizin2'ye ait bazı alanlar da vardır ve tamamen aynı olamaz. Karşılaştırmayı nesnenin bütün özellikleri ile değil de sadece dosya adları (.Name) ile kısıtlayarak aşabilirim.

$ar1.Name | % {if ($_ -in $ar2.Name) { $_ }}

Elbette bu şekilde sadece D:\dizin1 ve D:\dizin2'nin doğrudan içindeki dosyaları karşılaştırabilirim. Eğer alt klasörlerini de dahil etmek istersem biraz daha işleme ihtiyacım var. Şimdilik fikir olarak bununla kalayım.

Bu yöntem D:\dizin1\resim1.jpg ile D:\dizin2\resim1.jpg dosyalarının aynı olduğunu, sadece dosya ismine bakarak teşhis eder. Dosyanın oluşturma tarihi, değiştirme tarihi, boyutu ve içeriğini kontrol etmez. Basit bir karşılaştırma.

Tüm içeriğe girmeden sadece dosya adı ve boyutu da hesaba katarak bir karşılaştırma yapmak da mümkün. Bunun için karşılaştırmayı Compare-Object ile yapmak gerekecek.

compare $ar1 $ar2 -Property Name, Length -IncludeEqual -ExcludeDifferent

Eğer son değiştirilme tarihleri birebir aynıysa -Property'den sonra LastWriteTime da eklenebilir.

2026-01-28

Statik DNS kaydı ne zaman oluşturulmuş

Active Directory DNS sunucusu içinde bir statik kayıt acaba ne zaman oluşturulmuş diye merak ettim. Grafik arayüz gelişmiş seçeneklerle bile bunu göstermedi. Yaptığım bir aramada bu ve şu adreslere denk geldim.

RSAT yüklü olduğu (veya Powershell ActiveDirectory modülü olan) bir bilgisayar/sunucudan önce bu modülü import ederek (Bu modül, aslında Get-ADUser gibi bir komut kullanıldığında otomatik olarak import edilir. Ama modülü import etmeden Get-PSDrive cmdlet'i çalıştırılırsam aradığım şeyi görüntülemez) Get-PSDrive'ı çalıştırdığımda şunu gördüm:

Import-Module ActiveDirectory
Get-PSDrive
Name   Used (GB)   Free (GB) Provider      Root         CurrentLocation
----   ---------   --------- --------      ----         ---------------
AD                           ActiveDire... //RootDSE/

Evet, bir AD sürücüsü belirdi. Şu alt konteynere girdim:

Set-Location "AD:DC=domain.com,CN=MicrosoftDNS,DC=DomainDnsZones,DC=domain,DC=com"

Verdiğim bağlantılarda bu şekilde verildiği için ben de AD: şeklinde girdim ama aslında AD'nin bir sürücü olduğunu düşünürsek şu şekilde bir yol daha doğru:

"AD:\DC=domain.com,CN=MicrosoftDNS,DC=DomainDnsZones,DC=domain,DC=com"

Bu şekilde de cd yapılabilir. Bu adımdan sonra bulunduğum konumda bütün DNS kayıtları var. İstediğim DNS kaydının, örneğin cuma adındaki bir DNS kaydının oluşturulma ve değiştirilme tarihlerini görebilmek için yazmam gereken powershell komutları şöyle:

dir | ? Name -eq "cuma" | Get-ADObject -Properties Created,Modified | select Name, Created, Modified
Name  Created            Modified
----  -------            --------
metin 2.11.2020 09:13:44 19.12.2025 17:35:00