Shutdown Windows dari sisi aplikasi — bertahan dengan benar dari notifikasi keluar, restart, dan putus daya

· Diperbarui pada: · · Windows, Shutdown, Pengembangan Windows, Layanan Windows, PC perangkat, Pelestarian data, Operasi berjalan lama, UPS

Riwayat revisi (1 pembaruan, terakhir pada 31 Aug 2026)

Catatan perubahan yang dilakukan pada artikel ini. Jika versi sebelumnya telah diarsipkan, versi itu tetap dapat dibaca melalui tautan permanen dengan DOI.

Diterjemahkan ulang sebagai terjemahan lengkap dari naskah Jepang. Versi bahasa Indonesia sebelumnya adalah ringkasan yang hanya memindahkan sebagian naskah, sehingga bagian, tabel, gambar Mermaid, keterangan gambar, dan FAQ tidak ada. Semuanya dipulihkan sesuai naskah Jepang, dan klaim teknisnya sama dengan versi Jepang.
Publikasi pertama
Mengutip artikel ini(DOI (arsip terdaftar): 10.5281/zenodo.22176535)

DOI di bawah mengarah ke versi yang telah diarsipkan sebelumnya dan mungkin berbeda dari teks saat ini. Gunakan URL halaman ini untuk merujuk teks saat ini.

Go Komura (2026). Shutdown Windows dari sisi aplikasi — bertahan dengan benar dari notifikasi keluar, restart, dan putus daya. KomuraSoft LLC. https://comcomponent.com/id/blog/windows-shutdown-handling-for-apps/

DOI (arsip terdaftar)
10.5281/zenodo.22176535
DOI (versi terakhir yang didaftarkan)
10.5281/zenodo.22176536

“Setelah restart malam hari oleh Windows Update, aplikasi pengukuran di PC perangkat jatuh bersama data yang sedang ditulis, dan pagi harinya berkas pengukuran rusak.” “Seseorang keluar dari PC bersama, lalu komplain bahwa suntingan yang belum disimpan hilang.” — Dua keluhan ini klasik untuk aplikasi Windows yang berjalan lama.

Yang sama di kedua lapangan adalah memperlakukan shutdown sebagai “kejadian abnormal yang seharusnya tidak terjadi”. Padahal restart otomatis Windows Update, keluar pengguna, perintah shutdown dari UPS, sampai putus daya tanpa pengumuman — event yang memotong eksekusi dari luar aplikasi pasti datang suatu saat. Kedatangannya tidak bisa dicegah. Yang bisa dicegah adalah “kehilangan data ketika event itu datang”.

Untungnya Windows punya mekanisme yang mengirim notifikasi ke aplikasi sebelum shutdown, masing-masing untuk aplikasi GUI, aplikasi konsol, dan layanan. Artikel ini ditujukan kepada staf IT di perusahaan kecil–menengah dan pengembang aplikasi Windows (khususnya PC perangkat dan aplikasi yang berjalan lama). Isinya merangkum cara menerima notifikasi, merancang pembersihan yang “selesai dalam beberapa detik”, pemulihan otomatis setelah restart, dan persiapan untuk putus daya yang tidak membawa notifikasi — berdasarkan sumber primer Microsoft Learn per Agustus 2026.

1. Kesimpulan di muka

  • Rancang shutdown sebagai “event normal yang pasti datang suatu saat”. Waktu yang bisa dipakai setelah notifikasi, pada prinsipnya, hanya sekitar 5 detik. Desain yang buru-buru menyimpan semuanya di saat itu akan ambruk. Prasyaratnya adalah autosave yang sering, agar “selisih yang masih harus disimpan saat shutdown” tetap kecil.1
  • Pada OS klien Windows 8 ke atas, jika fast startup aktif (bawaan pada banyak PC yang mendukung hibernasi), “Shut down” adalah hybrid shutdown: kernel hanya berhibernasi. Yang di-reset penuh hanya “Restart”. Itulah wujud “sudah di-shut down tapi tidak membaik, setelah restart baru membaik”.2
  • Aplikasi GUI mengembalikan TRUE segera ke WM_QUERYENDSESSION, dan melakukan pembersihan di WM_ENDSESSION. Pada prinsipnya jangan mengembalikan FALSE (menolak).1
  • Hanya jika ada operasi yang benar-benar tidak boleh dipotong, tampilkan alasan dengan ShutdownBlockReasonCreate. Pengguna dan OS tetap bisa memaksa lanjut, jadi desain yang berasumsi “kita bisa memblokir” tidak berdiri.34
  • Aplikasi konsol menerima notifikasi dengan SetConsoleCtrlHandler. Masa tenggang lebih pendek lagi: bawaan sekitar 5 detik saat konsol ditutup. Ada juga jebakan: proses yang sudah memuat gdi32.dll atau user32.dll tidak menerima sebagian event.56
  • Pembersihan yang mengandalkan AppDomain.ProcessExit milik .NET, sejak .NET 10, tidak berjalan pada jalur di mana proses “diakhiri dari luar”. Pada keluar normal seperti kembali dari Main, event itu masih berjalan seperti dulu. Namun runtime tidak lagi menyediakan penanganan bawaan untuk sinyal terminasi seperti penutupan konsol dan shutdown, jadi pembersihan pada jalur itu harus dipindah ke notifikasi yang sesuai model aplikasi.7
  • Layanan Windows menerima notifikasi lebih dulu, dengan masa tenggang yang dapat dikonfigurasi, jika memakai SERVICE_ACCEPT_PRESHUTDOWN daripada SERVICE_ACCEPT_SHUTDOWN (masa tenggang sekitar 20 detik). Timeout PRESHUTDOWN bawaan dipendekkan menjadi 10 detik sejak Windows 10 Creators Update, jadi bagaimanapun desain tidak boleh terlalu bersandar pada masa tenggang.89
  • Pemulihan otomatis setelah restart dapat diwujudkan dengan kombinasi RegisterApplicationRestart dan ARSO (sign-in otomatis). Jalur pemulihan tersedia untuk crash, tidak merespons, dan restart karena pembaruan.1011
  • Putus daya tidak membawa notifikasi sama sekali. Pola standar adalah menulis tuntas ke berkas sementara, flush, lalu tukar dengan ReplaceFile. Karena ReplaceFile juga tidak menjamin atomisitas menyeberangi putus daya, jalur pemulihan cadangan (.bak) plus validasi saat start termasuk satu paket. Isolasi setelah kejadian dapat memakai event log (1074/41/6008).121314

Dalam satu kalimat, kesimpulan artikel ini adalah: “selalu jaga state yang memungkinkan aplikasi ditutup rapi dalam beberapa detik ketika notifikasi tiba, dan tulis dengan cara yang tidak rusak meski daya putus tanpa notifikasi”.

Pada diagram, garis utuh menunjukkan relasi yang selalu berlaku dan garis putus-putus menunjukkan relasi bersyarat (syaratnya ada pada penjelasan masing-masing relasi di halaman rincian). Daftar lengkap relasi (total 14, beserta bukti dan tingkat kepastian) serta definisi konsep utama dikumpulkan di halaman rincian peta pengetahuan (dalam bahasa Jepang). Data: JSON-LD / Turtle

2. Apa yang terjadi saat shutdown — empat cara berakhir

2.1. Keluar, shutdown, restart, dan putus daya

Dari sisi aplikasi, yang penting adalah dua sumbu: “bagaimana sesi pengguna berakhir” dan “apa yang terjadi pada kernel”.

Operasi Sesi pengguna Kernel dan driver Notifikasi ke aplikasi
Keluar Berakhir Tetap berjalan WM_QUERYENDSESSION (ENDSESSION_LOGOFF) → WM_ENDSESSION
Shutdown (fast startup aktif) Berakhir Hibernasi (disimpan ke hiberfil.sys) WM_QUERYENDSESSION → WM_ENDSESSION, (PRE)SHUTDOWN ke layanan
Restart Berakhir Berakhir penuh; boot berikutnya adalah boot penuh Sama seperti di atas
Putus daya Hilang seketika Hilang seketika Tidak ada

Keluar dan shutdown, dari sisi aplikasi, hampir event yang sama. Jika bit ENDSESSION_LOGOFF di lParam WM_QUERYENDSESSION menyala, itu keluar; jika 0, itu shutdown atau restart (keduanya tidak bisa dibedakan).1 Artinya asumsi “ini cuma keluar, tidak apa-apa” tidak berlaku; yang benar adalah kode pembersihan yang sama ikut terpanggil.

Empat cara berakhir dan notifikasi ke aplikasiKeluar, shutdown, dan restart mengirim notifikasi WM_QUERYENDSESSION lalu WM_ENDSESSION, dan pembersihan selesai dalam beberapa detik. Hanya putus daya yang tanpa notifikasi sama sekali, sehingga yang bisa dilakukan adalah merancang penulisan di bab 8 dan memakai UPSKeluarAda notifikasi: WM_QUERYENDSESSION → WM_ENDSESSION (ke layanan: (PRE)SHUTDOWN)ShutdownRestartPutus dayaTanpa notifikasi — siapkan dengan desain tulis bab 8 dan UPSPembersihan dalam beberapa detik (bab 3–6)

2.2. Wujud “sudah di-shut down tapi tidak membaik” — hybrid shutdown

Baris yang mudah terlewat adalah baris kedua tabel. Pada OS klien Windows 8 ke atas, fast startup (hybrid shutdown) aktif secara bawaan di PC yang mendukung hibernasi, dan perilaku “Shut down” berubah. Keluar sesi pengguna tetap berjalan seperti biasa, tetapi sesi kernel tidak ditutup; kernel bersama driver perangkat disimpan ke berkas hibernasi (hiberfil.sys) dan dipulihkan apa adanya pada boot berikutnya. Startup menjadi lebih cepat, sementara state kernel dan driver tetap terbawa meski daya sudah diputus.2 Ini perilaku bersyarat. Di lingkungan yang hibernasinya sendiri dinonaktifkan (powercfg /hibernate off), di mana kebijakan atau Power Options mematikan fast startup, dan di Windows Server, shutdown adalah shutdown penuh seperti dulu. Apakah PC sasaran berjalan dengan cara yang mana dapat dicek dari kotak “Turn on fast startup” di Power Options, atau dari apakah powercfg /a (keadaan tidur yang tersedia) menampilkan “Fast Startup”.

Apa yang terjadi pada kernel saat operasi Shut downOperasi Shut down terbelah menjadi shutdown penuh atau hibernasi kernel tergantung fast startup aktif atau tidak, sedangkan Restart selalu menjadi boot penuhFast startup aktif (bawaan klien)Hibernasi nonaktif / dimatikan kebijakan / Windows ServerOperasi \Shut down\Operasi \Restart\Sesi pengguna berakhir + kernel berhibernasi ke hiberfil.sysShutdown penuhBoot berikutnya: pulihkan state kernel dan driverBoot berikutnya: inisialisasi dengan boot penuh

Sebaliknya, “Restart” selalu menjalankan siklus boot lengkap. Setelah pembaruan driver, misalnya, diperlukan state yang sama sekali baru.2 Dari sini, gejala yang sering didengar di lapangan menjadi rapi.

  • “Sudah di-shut down lalu dinyalakan lagi, tetapi gangguan perangkat tidak hilang” — kernel dan driver hanya dipulihkan dari hibernasi; mereka tidak di-reset
  • “Setelah restart, baru membaik” — karena diinisialisasi oleh boot penuh
  • Prosedur penanganan gangguan PC perangkat harus menulis “Restart”, bukan “matikan lalu nyalakan lagi”

Untuk menyatakan shutdown penuh dari baris perintah, pakai shutdown /s (bawaan Shutdown.exe adalah shutdown penuh); untuk meniru perilaku hybrid bawaan, shutdown /s /hybrid.2 Menonaktifkan fast startup tidak direkomendasikan. Sisi aplikasi mengasumsikan “pada shutdown, kernel mungkin hanya berhibernasi” — misalnya jangan menaksir “waktu operasi kumulatif” dari waktu boot OS — dan merancang agar tidak rusak di kedua lingkungan (aktif/nonaktif fast startup berbeda per mesin).

3. Tata cara aplikasi GUI — WM_QUERYENDSESSION dan WM_ENDSESSION

3.1. Pembagian peran dua pesan

Aplikasi yang punya jendela dan antrean pesan diberitahu tentang akhir sesi dalam dua tahap.1

  1. WM_QUERYENDSESSION — kueri “bolehkah berakhir?”. Aplikasi harus mengembalikan TRUE segera. Respons bawaan DefWindowProc juga TRUE. Jangan mulai pembersihan di sini.
  2. WM_ENDSESSION (wParam=TRUE) — notifikasi yang sudah dikomit: “sesi benar-benar berakhir”. Pembersihan dilakukan di sini.

Mengembalikan FALSE ke WM_QUERYENDSESSION bisa membatalkan shutdown, tetapi dokumentasi menyatakan tegas “hormati niat pengguna dan kembalikan TRUE”. Aplikasi yang mengembalikan FALSE tetap dijejer di UI layar penuh sebagai “aplikasi yang menghalangi shutdown”. Aplikasi konsol dan aplikasi tanpa jendela terlihat sejak awal tidak bisa membatalkan shutdown, dan jika tidak merespons dalam 5 detik mereka diakhiri secara paksa.14

Alur notifikasi dua tahap saat sesi berakhirJika WM_QUERYENDSESSION dijawab TRUE, WM_ENDSESSION mengomit dan pembersihan dijalankan. Penolakan FALSE menampilkan aplikasi sebagai penghalang. Tanpa respons sekitar 5 detik, shutdown dapat dipaksa lanjutTRUE (prinsip)FALSE (penolakan luar biasa)Tanpa respons sekitar 5 detikPengguna memaksa lanjutPengguna membatalkanWM_QUERYENDSESSION (kueri)WM_ENDSESSION wParam=TRUE (komit)Ditampilkan di UI layar penuh sebagai \aplikasi yang menghalangi\Diperlakukan tidak meresponsPembersihan di sini (simpan, putus koneksi)Proses berakhirShutdown dibatalkan → semua aplikasi lanjut

3.2. Jika tidak merespons — tembok 5 detik

Baik WM_QUERYENDSESSION maupun WM_ENDSESSION hanya boleh menunda respons sekitar 5 detik. Melewati itu, sistem menampilkan layar “aplikasi ini menghalangi shutdown”, dan pengguna dapat memilih paksa lanjut (= proses diakhiri paksa).4 Proses yang sudah diakhiri paksa tidak mendapat kesempatan untuk menyambung penyimpanan.

Karena itu, dua titik desain berikut yang penting.

  • Jaga volume pembersihan agar selesai dalam 5 detik. Microsoft sendiri merekomendasikan menyimpan data secara rutin agar jumlah yang harus disimpan saat shutdown kecil, dan menyimpan data belum tersimpan ke lokasi sementara untuk dipulihkan pada start berikutnya.1
  • Jangan menampilkan dialog konfirmasi di tengah shutdown. Sambil menunggu jawaban “simpan?”, 5 detik habis. Diam-diam condong ke sisi aman (autosave).

3.3. Implementasi di WinForms dan WPF

Di aplikasi desktop .NET, pesan-pesan ini diterjemahkan menjadi event kerangka. Di WinForms, FormClosing terpanggil, dan CloseReason membedakan apakah penyebabnya shutdown.

// WinForms: FormClosing juga terpanggil saat shutdown/keluar
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
    if (e.CloseReason == CloseReason.WindowsShutDown)
    {
        // Hanya simpan snapshot yang idempoten. Jangan tampilkan dialog.
        // Jangan set e.Cancel = true (menolak).
        SaveWorkingStateToTempFile();
        return;
    }

    // Penutupan biasa, misalnya tombol × oleh pengguna: konfirmasi di sini boleh
}

Di WPF yang setara adalah event Application.SessionEnding (atribut XAML SessionEnding, atau override OnSessionEnding).

Korespondensi event WinForms/WPF dengan pesanTahap kueri WM_QUERYENDSESSION berpasangan dengan FormClosing WinForms dan SessionEnding WPF; yang boleh dilakukan di sini hanya simpan snapshot idempoten. WM_ENDSESSION sebagai notifikasi komit tidak punya event padanan, jadi terima lewat WndProc atau hook untuk pembersihan yang hanya boleh setelah dikomitWM_QUERYENDSESSION (kueri)WinForms: FormClosing (WindowsShutDown)WPF: SessionEndingSampai simpan snapshot yang idempotenWM_ENDSESSION (komit)Tidak ada event padanan → terima lewat WndProc/hookPembersihan yang hanya boleh setelah dikomit
// WPF: App.xaml.cs
protected override void OnSessionEnding(SessionEndingCancelEventArgs e)
{
    base.OnSessionEnding(e);

    // ReasonSessionEnding.Logoff / Shutdown bisa dibedakan,
    // tetapi dasarnya jalankan simpan snapshot yang sama untuk keduanya
    SaveWorkingStateToTempFile();

    // Jangan set e.Cancel = true kecuali ada alasan yang sangat kuat
}

Satu peringatan di sini. FormClosing (CloseReason.WindowsShutDown) maupun SessionEnding WPF hanya berpasangan dengan tahap kueri (WM_QUERYENDSESSION). Jika aplikasi lain menolak, shutdown dibatalkan dan aplikasi sendiri terus berjalan. Jadi yang boleh dilakukan di event ini hanya simpan snapshot yang tidak berbahaya meski dibatalkan, dan menghasilkan hasil yang sama berapa kali pun dijalankan (idempoten). Jika diperlukan “pembersihan yang hanya boleh saat benar-benar berakhir” (memutus koneksi, menyerahkan resource, dan sejenisnya), hook langsung WM_ENDSESSION (wParam=TRUE) di WndProc dan kerjakan di situ.

Di kedua jalur, isi pekerjaan dikumpulkan ke “fungsi simpan snapshot” yang sama, dan tulis data pemulihan dalam format yang sama untuk keluar normal, shutdown, dan (jika mungkin) crash. Desain yang menyisakan informasi saat crash dibahas di “Desain menyisakan log dan dump saat aplikasi Windows crash”.

4. Jika terpaksa memblokir — ShutdownBlockReasonCreate

Pengecualian hanya operasi yang secara fisik rusak jika dipotong di tengah jalan, seperti penulisan CD atau firmware. Tata cara yang benar: saat operasi yang tidak boleh dipotong dimulai, daftarkan string alasan dengan ShutdownBlockReasonCreate, dan segera lepas dengan ShutdownBlockReasonDestroy setelah selesai. Ketika shutdown diminta, alasan itu tampil di layar “aplikasi ini menghalangi shutdown”, sehingga pengguna dapat menilai lanjut atau batal.3

Alur perlindungan dengan ShutdownBlockReasonCreateDaftarkan alasan saat operasi yang tidak boleh dipotong dimulai. Jika permintaan shutdown datang selama perlindungan, alasan tampil di layar penuh dan FALSE dikembalikan ke WM_QUERYENDSESSION. Pengguna dapat batal atau memaksa lanjut. Setelah selesai, lepas alasanPengguna membatalkanPengguna memaksa lanjutMulai operasi yang tidak boleh dipotongDaftarkan alasan dengan ShutdownBlockReasonCreateJalankan operasi (worker thread)Selesai → lepas dengan ShutdownBlockReasonDestroyPermintaan shutdown selama ituTampilkan alasan di layar 「menghalangi」 + FALSE ke WM_QUERYENDSESSIONProses berakhir (tetap tidak terjaga sepenuhnya)
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool ShutdownBlockReasonCreate(IntPtr hWnd, string pwszReason);

[DllImport("user32.dll", SetLastError = true)]
static extern bool ShutdownBlockReasonDestroy(IntPtr hWnd);

// Panggil dari thread yang membuat jendela utama (dari thread lain akan gagal)
_criticalOperationInProgress = true;
ShutdownBlockReasonCreate(this.Handle, "Sedang menulis data pengukuran ke berkas");
try
{
    // Operasi yang tidak boleh dipotong dijalankan di worker thread. Jika UI thread
    // mengeksekusi secara sinkron, message pump berhenti, dan sebelum kode penolakan
    // WM_QUERYENDSESSION di bawah sempat berjalan, sistem memaksa lanjut sebagai "tidak merespons"
    await Task.Run(() => WriteMeasurementData());
}
finally
{
    ShutdownBlockReasonDestroy(this.Handle);
    _criticalOperationInProgress = false;
}

// Selain itu, hanya selama perlindungan, tolak dengan mengembalikan FALSE ke WM_QUERYENDSESSION
protected override void WndProc(ref Message m)
{
    const int WM_QUERYENDSESSION = 0x0011;
    if (m.Msg == WM_QUERYENDSESSION && _criticalOperationInProgress)
    {
        m.Result = IntPtr.Zero;   // Menolak. String alasan yang didaftarkan tampil di UI layar penuh
        return;
    }
    base.WndProc(ref m);
}

Yang mudah disalahpahami adalah pembagian peran. ShutdownBlockReasonCreate hanya mendaftarkan string alasan; API ini sendiri tidak menghentikan shutdown. Yang menahan adalah pemrosesan sendiri yang menaikkan flag perlindungan dan mengembalikan FALSE ke WM_QUERYENDSESSION, seperti di atas. Pakai keduanya sebagai satu set, dan lepas keduanya segera setelah operasi selesai. Operasi yang dilindungi sendiri dijalankan di worker thread, dan UI thread tetap memproses pesan — mekanisme penolakan baru berfungsi setelah pesan sampai (meski begitu, pengguna dan OS tetap bisa memaksa lanjut, jadi desain tulis yang tidak rusak “jika tidak tertahan” — bab 8 — tetap diperlukan).

Ada tiga catatan operasional.

  • String alasan pendek dan konkret. Pengguna sedang terburu-buru dan hanya membaca beberapa detik. Dokumentasi juga mencontohkan tingkat “sedang menulis CD”.3
  • Jangan biarkan terdaftar sepanjang aplikasi hidup. Asumsi API adalah “hanya selama operasi yang tidak boleh dipotong”.
  • Jangan merancang seolah pemblokiran dijamin. Pengguna dapat memaksa lanjut, dan pada shutdown paksa (ENDSESSION_CRITICAL) bahkan tidak ada yang menunggu. Dinyatakan tegas: “aplikasi tidak boleh bergantung pada kemampuan memblokir shutdown”.4

5. Tata cara aplikasi konsol dan proses latar

5.1. SetConsoleCtrlHandler dan masa tenggang yang pendek

Aplikasi konsol tidak menerima pesan jendela, jadi sinyal kontrol sampai ke fungsi handler yang didaftarkan dengan SetConsoleCtrlHandler. Masa tenggang bawaan per sinyal adalah sebagai berikut.5

Sinyal Kapan terjadi Masa tenggang bawaan
CTRL_C_EVENT / CTRL_BREAK_EVENT Ctrl+C / Ctrl+Break Tanpa timeout
CTRL_CLOSE_EVENT Menutup konsol, “End task” di Task Manager (pengakhiran paksa proses dari tab “Details” adalah pengakhiran seketika tanpa notifikasi, di luar tabel ini) Sekitar 5 detik
CTRL_SHUTDOWN_EVENT Shutdown sistem (proses layanan) Sekitar 20 detik

Dua hal perlu diperhatikan. Pertama, CTRL_LOGOFF_EVENT dan CTRL_SHUTDOWN_EVENT pada praktiknya hanya diterima proses yang berjalan sebagai layanan. Aplikasi di sesi interaktif diakhiri pada saat keluar, jadi desain yang menunggu sinyal ini tidak berdiri.5 Kedua, proses yang memuat gdi32.dll atau user32.dll diperlakukan sebagai aplikasi Windows meski berniat menjadi aplikasi konsol, dan handler LOGOFF/SHUTDOWN tidak terpanggil. Workaround resmi: buat jendela tersembunyi dan terima WM_QUERYENDSESSION/WM_ENDSESSION.6

Masa tenggang per sinyal konsolCtrl+C dan Ctrl+Break tidak punya timeout eksplisit. Penutupan konsol sekitar 5 detik. Sinyal shutdown ke proses layanan sekitar 20 detik. Melewati itu, proses diakhiri paksaTanpa timeoutSekitar 5 detikSekitar 20 detikCTRL_C / CTRL_BREAKPembersihan di HandlerRoutine yang didaftarkanCTRL_CLOSE_EVENTCTRL_SHUTDOWN_EVENT (proses layanan)Melewati masa tenggang → diakhiri paksa
// Aplikasi konsol: pembersihan pada Ctrl+C dan penutupan konsol
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetConsoleCtrlHandler(HandlerRoutine handler, bool add);

delegate bool HandlerRoutine(int ctrlType);   // 2 = CTRL_CLOSE_EVENT

static readonly HandlerRoutine s_handler = OnCtrlEvent;  // Tahan agar tidak di-GC

static bool OnCtrlEvent(int ctrlType)
{
    // Hanya pembersihan yang selesai dalam 5 detik
    FlushAndCloseDataFile();
    return false;   // Lanjut ke handler bawaan, proses berakhir
}

static void Main()
{
    SetConsoleCtrlHandler(s_handler, add: true);
    // ...
}

5.2. Jebakan .NET — jangan andalkan ProcessExit

Di .NET lama ada pola “bersihkan di AppDomain.ProcessExit”, tetapi sejak .NET 10 runtime tidak lagi menyediakan handler sinyal terminasi bawaan, dan pada CTRL_CLOSE_EVENT/CTRL_SHUTDOWN_EVENT, ProcessExit maupun AssemblyLoadContext.Unloading tidak muncul. Handler bawaan OS hanya mengakhiri proses seketika.7

Perilaku ProcessExit yang berubah di .NET 10Sampai .NET 9, handler sinyal bawaan runtime menerima sinyal terminasi, memunculkan ProcessExit, lalu berakhir. Sejak .NET 10 runtime tidak menyediakan handler bawaan, sehingga penanganan bawaan OS mengakhiri proses seketika. Daftarkan handler sendiriCTRL_CLOSE_EVENT / CTRL_SHUTDOWN_EVENTSampai .NET 9: handler bawaan runtime → ProcessExit muncul → berakhirSejak .NET 10: tanpa handler bawaan → diakhiri seketika oleh penanganan bawaan OS (tanpa ProcessExit)Mitigasi: daftarkan SetConsoleCtrlHandler / PosixSignalRegistration sendiri

Sebagai gantinya, condong ke jalur resmi per model aplikasi.

  • Aplikasi GUI: FormClosing / SessionEnding di bab sebelumnya
  • Generic Host (termasuk Worker Service): IHostApplicationLifetime dan BackgroundService.StopAsync. Masa tenggang berhenti dinyatakan dengan HostOptions.ShutdownTimeout
  • Aplikasi konsol polos: SetConsoleCtrlHandler (atau berlangganan SIGINT/SIGTERM setara dengan PosixSignalRegistration)
Titik terima notifikasi keluar per model aplikasiAplikasi GUI memakai FormClosing dan SessionEnding, plus hook WM_ENDSESSION untuk pemrosesan yang dikomit. Generic Host memakai IHostApplicationLifetime dan StopAsync. Aplikasi konsol polos memakai SetConsoleCtrlHandler atau PosixSignalRegistration. Mengandalkan ProcessExit tidak muncul pada jalur sinyal eksternalGUI (WinForms/WPF)Generic Host / Worker ServiceKonsol polosModel aplikasi yang mana?FormClosing / SessionEnding (pemrosesan dikomit: hook WM_ENDSESSION)IHostApplicationLifetime + StopAsync (nyatakan ShutdownTimeout)SetConsoleCtrlHandler / PosixSignalRegistrationMengandalkan AppDomain.ProcessExit✕ Sejak .NET 10, tidak muncul pada jalur sinyal eksternal

Masa tenggang berbeda per jalur — GUI dan penutupan konsol sekitar 5 detik, layanan memakai masa tenggang SCM di bab 6 (sekitar 20 detik, atau nilai yang dikonfigurasi jika PRESHUTDOWN), Ctrl+C tanpa timeout eksplisit. Namun di semua jalur masa tenggang terbatas dan tidak bisa diandalkan, jadi poros desainnya: bukan “berusaha keras di event keluar”, melainkan menyimpan di setiap tonggak pemrosesan sebagai jalur normal.

6. Tata cara layanan Windows — SHUTDOWN dan PRESHUTDOWN

6.1. Dua jenis notifikasi shutdown

Layanan tidak terpengaruh keluar sesi, tetapi dihentikan pada shutdown dan restart. Notifikasi datang sebagai kode kontrol dari Service Control Manager (SCM), dan untuk menerimanya perlu menyatakan flag penerimaan.8

Pernyataan Notifikasi yang sampai Waktu dan masa tenggang
SERVICE_ACCEPT_SHUTDOWN SERVICE_CONTROL_SHUTDOWN Diberitahu di tengah pemrosesan shutdown. Bawaan sekitar 20 detik, batas atas WaitToKillServiceTimeout
SERVICE_ACCEPT_PRESHUTDOWN SERVICE_CONTROL_PRESHUTDOWN Diberitahu lebih dulu daripada SHUTDOWN. SCM menunggu sampai layanan berhenti atau timeout
Urutan notifikasi shutdown ke layananSaat shutdown dimulai, layanan yang menyatakan PRESHUTDOWN diberitahu lebih dulu dengan masa tenggang yang dikonfigurasi, lalu notifikasi SHUTDOWN dikirim dengan masa tenggang bawaan sekitar 20 detik, dan proses berakhir jika masa tenggang habisShutdown dimulaiSERVICE_CONTROL_PRESHUTDOWN (hanya layanan yang menyatakan; masa tenggang terkonfigurasi)SERVICE_CONTROL_SHUTDOWN (bawaan sekitar 20 detik)Masa tenggang habis → proses berakhir

Timeout PRESHUTDOWN dapat dikonfigurasi dengan ChangeServiceConfig2 (SERVICE_CONFIG_PRESHUTDOWN_INFO). Nilai bawaan adalah 10 detik sejak Windows 10 Creators Update (build 15063), 3 menit sebelumnya.9 Jika pengetahuan lama “PRESHUTDOWN memberi 3 menit” tidak diubah, di OS kini masa tenggangnya hanya 1/18 dari yang diharapkan. Selain itu PRESHUTDOWN menahan shutdown seluruh sistem selama interval itu, sehingga dokumentasi juga menyebut “hanya untuk situasi khusus”.8

Tata cara di sisi handler juga penting. Control handler harus kembali dalam 30 detik. Pekerjaan berhenti yang lama diserahkan ke thread lain, dan handler melaporkan SERVICE_STOP_PENDING lalu segera kembali.8

// Layanan Win32: terima PRESHUTDOWN, serahkan penghentian ke worker
g_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PRESHUTDOWN;

DWORD WINAPI ServiceHandlerEx(DWORD control, DWORD, LPVOID, LPVOID)
{
    switch (control)
    {
    case SERVICE_CONTROL_PRESHUTDOWN:
    case SERVICE_CONTROL_STOP:
        ReportStatus(SERVICE_STOP_PENDING, /*waitHintMs*/ 3000);
        SetEvent(g_stopEvent);   // Perintahkan worker berhenti, lalu kembali segera
        return NO_ERROR;
    }
    return ERROR_CALL_NOT_IMPLEMENTED;
}

// Sisi worker: jika pembersihan lebih lama dari waitHint, terus laporkan
// SERVICE_STOP_PENDING sambil menaikkan dwCheckPoint secara berkala. SCM menilai
// "masih hidup dan maju" dari waitHint dan kemajuan checkpoint. Jika laporan
// berhenti, dianggap hang dan shutdown dapat maju. Setelah selesai, selalu laporkan SERVICE_STOPPED

6.2. Desain yang tidak bersandar pada masa tenggang

Memperpanjang WaitToKillServiceTimeout — batas atas masa tenggang — dengan menulis ulang dari sisi layanan secara tegas tidak direkomendasikan. Dokumentasi justru menuntut arah sebaliknya: agar mesin yang ditenagai UPS sempat menyelesaikan shutdown sebelum baterai habis, layanan harus menyelesaikan pembersihan secepat mungkin. Arahannya: simpan secara rutin agar data belum tersimpan selalu minimal, jangan menghabiskan waktu untuk membebaskan memori saat shutdown, dan jangan terlalu lama menunggu jawaban saat memberi tahu rekan di jaringan. Selain itu, SCM saat shutdown secara bawaan tidak mempertimbangkan dependensi, jadi proses berhenti harus “tidak rusak meski layanan yang dijadikan dependensi sudah jatuh lebih dulu”.8

Desain penghentian yang tidak bersandar pada masa tenggangJika data belum tersimpan selalu dijaga minimal dengan menyimpan di setiap tonggak, pembersihan saat notifikasi berhenti selesai dalam beberapa detik. Desain yang menumpuk di memori dan menyimpan sekaligus saat keluar tidak muat di masa tenggang, dan data hilang karena pengakhiran paksaSehari-hari: simpan di setiap tonggak (data belum tersimpan selalu minimal)Notifikasi berhenti → simpan sisa yang kecil → selesai dalam beberapa detikSehari-hari: menumpuk di memori, simpan sekaligus saat keluarNotifikasi berhenti → penyimpanan tidak muat di masa tenggangDiakhiri paksa → data hilang

Pada Worker Service .NET (UseWindowsService), SERVICE_CONTROL_STOP dan SHUTDOWN diubah menjadi penghentian host, lalu BackgroundService.StopAsync terpanggil. Implementasi standar pada saat artikel ini ditulis menerima jalur STOP/SHUTDOWN; jika PRESHUTDOWN diperlukan, handler perlu diperluas. Bagaimanapun, nyatakan HostOptions.ShutdownTimeout secara eksplisit dan selesaikan StopAsync dalam beberapa detik. Cara membuat layanan secara umum ada di “Cara membuat dan mengoperasikan layanan Windows”.

7. Pulih otomatis setelah restart

Pada PC perangkat dan PC tanpa operator, cakupan desain bukan hanya “bertahan dari shutdown”, tetapi sampai “kembali sendiri setelah restart”.

7.1. RegisterApplicationRestart dan callback pemulihan

Jika RegisterApplicationRestart dipanggil, aplikasi didaftarkan sebagai sasaran restart pada crash (pengecualian tidak tertangani), tidak merespons, restart aplikasi karena pembaruan, dan restart OS karena pembaruan. Argumen baris perintah saat restart dapat didaftarkan, jadi jika “berkas mana yang terbuka” dan “restore point yang mana” disertakan, pekerjaan dapat dilanjutkan setelah restart.10

Spesifikasi yang perlu dipegang adalah sebagai berikut.10

  • Pendaftaran harus selesai sebelum masalah terjadi (pada skenario pembaruan, pemrosesan WM_QUERYENDSESSION adalah kesempatan terakhir)
  • Untuk mencegah loop restart, proses yang belum 60 detik sejak start tidak di-restart
  • Proses yang berjalan dengan elevasi administrator tidak menjadi sasaran restart otomatis (proses tidak bisa dibuat ulang tanpa persetujuan elevasi). Pemulihan otomatis untuk aplikasi yang butuh elevasi dirancang dengan memisahkan UI ke hak standar dan pekerjaan istimewa ke layanan, atau lewat jalur start eksplisit seperti tugas Task Scheduler “Run with highest privileges”
  • Restart saat crash/hang melalui persetujuan pengguna; restart karena pembaruan otomatis
  • Untuk pulih menyeberangi restart OS, pihak yang memerintahkan restart (installer, dll.) harus memanggil API shutdown dengan flag EWX_RESTARTAPPS / SHUTDOWN_RESTARTAPPS

Jika RegisterApplicationRecoveryCallback juga didaftarkan, WER (Windows Error Reporting) memanggil callback saat crash dan memberi masa tenggang untuk menyimpan data kerja. Jika penyimpanan lama, ApplicationRecoveryInProgress harus terus dipanggil dalam interval ping yang ditentukan saat pendaftaran, atau pemulihan dipotong di tengah. Setelah simpan selesai, beritahu penyelesaian dengan ApplicationRecoveryFinished. “Penggantian berkas yang sedang dipakai dan restart” saat pembaruan aplikasi adalah ranah Restart Manager, dibahas lebih dalam di “Bagaimana menukar exe/DLL yang sedang dipakai”.

7.2. ARSO — sign-in otomatis setelah restart pembaruan

Setelah restart oleh Windows Update, jika tidak ada yang masuk, aplikasi di sesi pengguna tidak kembali. Yang mengisi celah ini adalah ARSO (Winlogon Automatic Restart Sign-On). Saat Windows Update memulai restart, kredensial pengguna interaktif terakhir disimpan dengan aman, Autologon dikonfigurasi, dan setelah restart pengguna itu di-sign-in otomatis lalu layar dikunci.11 Ada juga perintah yang menyatakan restart plus pelanjutan aplikasi terdaftar, seperti shutdown /g. Di lingkungan yang dinonaktifkan kebijakan organisasi (DisableAutomaticRestartSignOn, dll.), konfirmasikan pengaturan ini sebagai satu paket saat merancang pemulihan tanpa operator. Selain itu, jika pekerjaan latar yang selalu diperlukan disandarkan pada start otomatis sesi pengguna, yang lebih masuk akal adalah menjadikannya layanan Windows sejak awal.

Jalur sampai aplikasi pulih otomatis setelah restartJika didaftarkan lebih dulu dengan RegisterApplicationRestart, pada crash atau tidak merespons aplikasi di-restart setelah persetujuan pengguna, dan pada restart karena pembaruan setelah sign-in otomatis ARSO plus kunci layar. Proses belum 60 detik sejak start dan proses terelevasi di luar sasaranPersetujuan penggunaDaftar dengan RegisterApplicationRestart (sebelum masalah terjadi)Crash / tidak meresponsRestart karena pembaruanRestart aplikasiARSO: sign-in otomatis + kunci layarDi luar sasaran: belum 60 detik sejak start (cegah loop), proses terelevasi

8. Tahan putus daya yang tanpa notifikasi — desain tulis dan UPS

8.1. Tulis yang “tidak rusak kapan pun dayanya putus” — berkas sementara + ReplaceFile

Pada pemutus sirkit, gagal PSU, atau colokan tercabut, tidak ada WM_ENDSESSION maupun PRESHUTDOWN. Selama berkas pengaturan atau hasil pengukuran ditimpa langsung ke berkas asli, putus daya di tengah tulis dapat meninggalkan berkas rusak yang mencampur isi lama dan baru.

Pola standar: tulis tuntas ke berkas sementara pada volume yang sama, lalu tukar. ReplaceFile merangkum rangkaian “simpan ke berkas baru → singkirkan berkas asli → ganti nama → hapus” ke satu API, dan atribut berkas asli seperti waktu pembuatan, ACL, dan alternate stream ikut diwariskan (ketiga berkas harus berada di volume yang sama).12 File.Replace di .NET memanggil ini apa adanya.

Alur simpan dan pemulihan dengan berkas sementara dan ReplaceFileSaat menyimpan, tulis tuntas ke berkas sementara, flush, tukar dengan ReplaceFile, dan sisakan isi lama di .bak. Saat start, validasi berkas utama dan jatuh ke .bak jika rusakSaat start berikutnyaSaat menyimpanNormalRusakKapan pun daya putus di tengahValidasi berkas utamaPakai apa adanyaJatuh ke .bakFlush (setara FlushFileBuffers)Tulis tuntas ke berkas sementaraTukar dengan ReplaceFile (isi lama ke .bak)
// Pola standar simpan pengaturan/data: tulis tuntas ke berkas sementara lalu tukar, sisakan isi lama
public static void SaveAtomically(string path, string content)
{
    string dir = Path.GetDirectoryName(path)!;
    string tmp = Path.Combine(dir, Path.GetRandomFileName());  // Buat di volume yang sama

    try
    {
        using (var fs = new FileStream(tmp, FileMode.CreateNew, FileAccess.Write))
        using (var writer = new StreamWriter(fs))
        {
            writer.Write(content);
            writer.Flush();
            fs.Flush(flushToDisk: true);   // Setara FlushFileBuffers. Tulis buffer OS ke disk
                                           // (batas cache sisi perangkat di 8.2)
        }

        if (File.Exists(path))
            File.Replace(tmp, path, path + ".bak");  // Memanggil ReplaceFile. Sisakan isi lama sebagai .bak
        else
            File.Move(tmp, path);
    }
    catch
    {
        // Jika gagal di tengah, jangan sisakan berkas sementara. Jika simpan berkala terus gagal,
        // salinan utuh akan mengisi volume
        try { File.Delete(tmp); } catch { /* Kegagalan hapus: utamakan pengecualian asli */ }
        throw;
    }
}

Dengan ini, pada operasi biasa selalu ada kondisi “berkas lama yang utuh” atau “berkas baru yang utuh” yang bisa dibaca. Namun ReplaceFile adalah operasi namespace bertahap, dan atomisitas menyeberangi putus daya tidak dijamin sebagai spesifikasi. Karena itu contoh di atas menyisakan cadangan (.bak) — sisi baca memvalidasi berkas utama saat start, dan jika rusak jatuh ke cadangan, sebagai satu paket. Pola ini tidak dipakai untuk log atau CSV yang append; untuk itu pakai format yang sudah memperhitungkan cara rusaknya, misalnya “satu baris = satu rekaman, buang baris terakhir yang rusak saat dibaca”.

8.2. Sukses WriteFile bukan berarti sudah sampai disk

Prasyarat lain: meski WriteFile mengembalikan sukses, data mungkin masih hanya di cache OS. Windows menaruh baca-tulis berkas di buffer sistem, lalu menuliskannya ke disk secara tertunda (lazy write) secara berkala. Agar benar-benar sampai disk, flush eksplisit dengan FlushFileBuffers, atau tentukan FILE_FLAG_WRITE_THROUGH saat CreateFile agar setiap tulis menembus cache. Metadata sistem berkas selalu di-cache, jadi penetapan metadata juga memerlukan flush atau write-through.13

Namun memanggil FlushFileBuffers setiap kali tidak efisien, dan dokumentasi mendorong mempertimbangkan FILE_FLAG_NO_BUFFERING+WRITE_THROUGH sebagai ganti panggilan yang terlalu sering.13 Di praktik, “flush hanya di tonggak transaksi dan tepat sebelum menutup berkas” adalah titik jatuh yang realistis. Mekanisme lapisan ini — cache manager, lazy write, dan cerita cache perangkat keras di mana “sudah di-flush tapi belum sampai disk” bisa terjadi — dibahas lebih dalam di “Cache manager: kapan WriteFile Anda sampai ke disk”.

8.3. UPS dan pemantauan baterai — mengubah putus daya menjadi shutdown

Andalan penanganan putus daya di PC perangkat adalah UPS. Peran UPS bukan “menghentikan pemadaman”, melainkan mengubah “putus daya tanpa notifikasi” menjadi “shutdown terencana yang membawa notifikasi”. Desainnya dua tingkat.

  1. Desain masa tenggang: waktu tahan baterai UPS > jumlah waktu “deteksi peralihan ke baterai ~ pembersihan aplikasi dan layanan ~ shutdown OS selesai”. Jika penghentian layanan lambat, rumus ini tidak terpenuhi (pasal 6.2)
  2. Deteksi: peralihan dari AC ke baterai dan turunnya sisa kapasitas diberitahu lewat event PBT_APMPOWERSTATUSCHANGE. Aplikasi yang punya jendela menerimanya sebagai WM_POWERBROADCAST; layanan tanpa jendela menyatakan SERVICE_ACCEPT_POWEREVENT lalu menerimanya sebagai SERVICE_CONTROL_POWEREVENT di HandlerEx (WM_POWERBROADCAST tidak sampai ke control handler layanan). Setelah diterima, panggil GetSystemPowerStatus, periksa ACLineStatus (apakah disuplai AC) dan BatteryLifePercent, lalu sambungkan ke penghentian pengukuran, penyimpanan, dan permintaan shutdown15
Alur mengubah putus daya menjadi shutdown terencana dengan UPSSaat pemadaman, UPS beralih ke baterai, PBT_APMPOWERSTATUSCHANGE diberitahu, status daya diperiksa, lalu disambungkan ke simpan dan permintaan shutdown, sehingga putus daya tanpa notifikasi menjadi shutdown terencana yang membawa notifikasiPemadaman / putus dayaUPS beralih ke suplai bateraiNotifikasi PBT_APMPOWERSTATUSCHANGEPeriksa status dengan GetSystemPowerStatusHentikan pengukuran, simpanMinta shutdown ke OSAlur notifikasi shutdown biasa (bab 3–6)

UPS USB pada umumnya terlihat sebagai baterai dari Windows, jadi dapat dideteksi dengan API standar ini. Jika perangkat lunak manajemen vendor punya fitur “shutdown OS pada sisa N%”, pastikan juga selarasnya ambang itu dengan waktu pembersihan aplikasi sendiri. Masalah pemulihan dari sleep/hibernasi dan operasi berjalan lama adalah sumbu terpisah, dibahas di “Sleep, hibernasi, Modern Standby, dan aplikasi yang berjalan lama”.

9. Cara memverifikasi — menguji shutdown dengan aman

Kode shutdown cenderung menjadi “sudah ditulis tetapi belum pernah diuji setara produksi”. Siapkan prosedur verifikasi yang aman.

  • Uji di mesin verifikasi atau VM: jangan langsung di PC perangkat produksi. Di lingkungan verifikasi dengan checkpoint (snapshot) Hyper-V dan sejenisnya, ulangi shutdown, restart, dan putus daya paksa (power-off VM). Namun “power-off” VM hanya mereproduksi “guest OS berhenti tanpa pengumuman”; hilangnya cache volatil disk fisik dan cara rusak yang bergantung pada controller tidak ikut tereproduksi. Jika dikirim sebagai PC perangkat, konfirmasi akhir adalah tes memutus daya sungguhan di perangkat keras setara produksi
  • Konfirmasi ringkas dengan keluar: jalur WM_QUERYENDSESSION→WM_ENDSESSION juga dilalui saat keluar (hanya bit ENDSESSION_LOGOFF di lParam yang berbeda), jadi perilaku kode pembersihan dapat dicek dengan mudah di mesin pengembangan1
  • Uji dengan membedakan shutdown penuh dan hybrid: coba masing-masing shutdown /s /t 0 (penuh), shutdown /s /hybrid /t 0 (perilaku bawaan), dan shutdown /r /t 0 (restart)2
  • Ukur waktu pembersihan: tulis stempel waktu di log di awal dan akhir fungsi pembersihan, dan ukur apakah muat dalam 5 detik (atau masa tenggang terkonfigurasi untuk layanan)
Operasi yang diverifikasi dan cakupan yang dapat dikonfirmasiKeluar untuk mengecek jalur notifikasi dengan mudah; perintah shutdown penuh, hybrid, dan restart untuk jalur notifikasi dan masa tenggang produksi; power-off VM untuk ketahanan berhenti mendadak; tes putus daya di perangkat nyata untuk ketahanan termasuk penyimpanan fisik sebagai konfirmasi akhirKeluarJalur WM_QUERYENDSESSION → WM_ENDSESSION (cek ringkas)shutdown /s, /s /hybrid, /rJalur notifikasi dan masa tenggang produksiPower-off VMKetahanan guest berhenti tanpa pengumumanTes putus daya di perangkat nyataKetahanan termasuk penyimpanan fisik (konfirmasi akhir)

Untuk isolasi setelah kejadian, event log (System) dapat dipakai. Pada shutdown/restart normal, event ID 1074 (proses mana, untuk siapa, dan dengan alasan apa memulai shutdown) tercatat. Pada putus daya mendadak atau crash, 1074 tidak ada, dan pada boot berikutnya event ID 41 (Kernel-Power) dan 6008 (shutdown sebelumnya tidak diharapkan) tercatat.14 “Apa yang terjadi semalam” dimulai dari sini.

# Cek riwayat terbaru event terkait shutdown
Get-WinEvent -FilterHashtable @{ LogName = 'System'; Id = 1074, 6008, 41 } -MaxEvents 20 |
    Select-Object TimeCreated, Id, ProviderName, Message |
    Format-List

Jika 1074 menunjukkan “restart oleh Windows Update” tetapi data aplikasi rusak, itu masalah kode pembersihan. 6008/41 hanya menunjukkan “shutdown tak terduga”; selain putus daya, blue screen (crash) dan reset paksa juga tercatat. Jika BugcheckCode pada 41 bukan 0, itu crash; jika 0 dan tidak ada memory dump, putus daya lebih kuat — isolasi penyebab dengan informasi sekitar, dan jika terbukti putus daya, giliran desain tulis bab 8 dan UPS.

10. Ringkasan

  • Shutdown adalah “event normal yang pasti datang suatu saat”. Masa tenggang setelah notifikasi pada prinsipnya hanya sekitar 5 detik, jadi prasyaratnya adalah meminimalkan “pekerjaan saat keluar” dengan autosave yang sering.
  • Pada OS klien Windows 8 ke atas, jika fast startup aktif, “Shut down” adalah hybrid shutdown dan kernel hanya berhibernasi. Reset penuh hanya “Restart” — tulis “Restart” di prosedur penanganan gangguan.
  • Aplikasi GUI mengembalikan TRUE segera ke WM_QUERYENDSESSION, dan pembersihan setelah dikomit dilakukan di WM_ENDSESSION. FormClosing dan SessionEnding WinForms/WPF berpasangan dengan tahap kueri, jadi yang dilakukan di situ hanya simpan snapshot idempoten. Jangan tampilkan dialog di tengah shutdown.
  • Operasi yang benar-benar tidak boleh dipotong dilindungi dengan menampilkan alasan lewat ShutdownBlockReasonCreate. Namun tidak ada jaminan pemblokiran di mana pun.
  • Aplikasi konsol menerima notifikasi dengan SetConsoleCtrlHandler, layanan dengan SERVICE_ACCEPT_PRESHUTDOWN/SHUTDOWN. Masa tenggang bawaan PRESHUTDOWN di OS kini adalah 10 detik. Di .NET, hentikan ketergantungan pada ProcessExit dan condong ke jalur resmi model aplikasi.
  • Pemulihan setelah restart dapat diotomatiskan dengan RegisterApplicationRestart (+ callback pemulihan) dan ARSO.
  • Putus daya tidak membawa notifikasi. Siapkan dengan tukar lewat berkas sementara + ReplaceFile (satu paket dengan cadangan + validasi saat start), flush di tonggak, dan “mengubah putus daya menjadi shutdown terencana” lewat UPS.
Gambaran keseluruhan penanganan shutdownPada cara berakhir yang membawa notifikasi, jawab dengan pembersihan yang selesai dalam beberapa detik lalu sambungkan ke pemulihan otomatis setelah restart. Pada putus daya tanpa notifikasi, siapkan dengan tulis yang tidak rusak kapan pun dayanya putus plus UPS, dan verifikasi sampai perangkat nyata. Dua pilar ini adalah kesimpulan artikelAda notifikasi (keluar, shutdown, restart)Tanpa notifikasi (putus daya)Cara berakhirPembersihan yang selesai dalam beberapa detik (bab 3–6)Tulis yang tidak rusak kapan pun dayanya putus + UPS (bab 8)Pemulihan otomatis setelah restart (bab 7)Verifikasi termasuk perangkat nyata (bab 9)
  • Verifikasi dilakukan dengan aman di VM dan dengan keluar; setelah kejadian, isolasi dengan event ID 1074/41/6008.

Saat menambah fitur ke aplikasi berikutnya, tanyakan sekali saja: jika WM_ENDSESSION datang di tengah operasi ini, atau colokan dicabut, apa yang tersisa pada start berikutnya? Menuliskan jawaban itu ke dalam desain adalah jalan terdekat untuk menghilangkan “pagi di depan PC perangkat sambil memegang kepala”.

Artikel terkait

Area konsultasi terkait

合同会社小村ソフト menangani desain dan implementasi penanganan shutdown/putus daya untuk PC perangkat dan aplikasi yang berjalan lama, investigasi penyebab kerusakan data dan gangguan “pagi sudah berhenti” yang berawal dari restart Windows Update atau keluar, serta tinjauan desain penghentian layanan Windows dan pemulihan otomatis. Boleh mulai dari tahap “setiap shutdown rasanya ada yang rusak, tetapi tidak tahu dari mana memulai”.

Tautan rujukan

  1. Microsoft Learn, WM_QUERYENDSESSION message. Tentang WM_QUERYENDSESSION yang dikirim saat sesi berakhir, aplikasi harus mengembalikan TRUE dan menghormati niat pengguna (bawaan DefWindowProc juga TRUE), pembersihan ditunda sampai WM_ENDSESSION, setelah 5 detik sistem menampilkan UI aplikasi yang menghalangi shutdown dan pengguna dapat mengakhiri paksa, arti bit ENDSESSION_LOGOFF/CLOSEAPP/CRITICAL di lParam, shutdown dan restart tidak dapat dibedakan, serta data harus disimpan secara rutin agar volume simpan saat keluar kecil. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7

  2. Microsoft Learn, Fast startup causes hibernation or shutdown to fail in Windows 10 or Windows 8.1. Tentang fast startup yang tidak menutup sesi kernel melainkan memperlakukannya sebagai hibernasi, state kernel dan driver perangkat disimpan ke hiberfil.sys, “Restart” selalu melakukan boot penuh karena diperlukan state Windows yang sama sekali baru, fast startup aktif secara bawaan dan menonaktifkannya tidak direkomendasikan, serta bawaan Shutdown.exe adalah shutdown penuh dan opsi /hybrid menghasilkan perilaku hybrid. ↩ ↩2 ↩3 ↩4 ↩5

  3. Microsoft Learn, ShutdownBlockReasonCreate function (winuser.h). Tentang memanggil API ini saat operasi yang tidak boleh dipotong dimulai untuk mendaftarkan string alasan, memanggil ShutdownBlockReasonDestroy saat selesai, hanya dapat dipanggil dari thread yang membuat jendela, serta string harus pendek dan jelas karena pengguna hanya membaca beberapa detik. ↩ ↩2 ↩3

  4. Microsoft Learn, Shutdown Changes for Windows Vista. Tentang respons ke WM_QUERYENDSESSION/WM_ENDSESSION yang hanya boleh ditunda 5 detik masing-masing lalu pengguna memilih lanjut atau batal, aplikasi konsol dan aplikasi tanpa jendela terlihat tidak dapat membatalkan shutdown dan diakhiri otomatis jika 5 detik tanpa respons atau menjawab FALSE, jika pemblokiran perlu daftarkan alasan dengan ShutdownBlockReasonCreate, serta aplikasi tidak boleh bergantung pada kemampuan memblokir shutdown. ↩ ↩2 ↩3 ↩4

  5. Microsoft Learn, HandlerRoutine callback function. Tentang event CTRL_C/BREAK/CLOSE/LOGOFF/SHUTDOWN yang diterima handler yang didaftarkan dengan SetConsoleCtrlHandler, timeout bawaan CTRL_CLOSE_EVENT sekitar 5000 milidetik dan CTRL_SHUTDOWN_EVENT proses layanan sekitar 20000 milidetik, CTRL_LOGOFF/SHUTDOWN_EVENT pada praktiknya hanya diterima layanan karena aplikasi interaktif diakhiri saat logoff, serta handler dijalankan di thread terpisah. ↩ ↩2 ↩3

  6. Microsoft Learn, SetConsoleCtrlHandler function. Tentang proses yang memuat gdi32.dll atau user32.dll diperlakukan sebagai aplikasi Windows sehingga handler CTRL_LOGOFF_EVENT/CTRL_SHUTDOWN_EVENT tidak terpanggil, workaround membuat jendela tersembunyi dan memproses WM_QUERYENDSESSION/WM_ENDSESSION, serta fungsi konsol kadang tidak berjalan normal selama pemrosesan sinyal. ↩ ↩2

  7. Microsoft Learn, .NET runtime no longer provides default termination signal handlers. Tentang runtime sejak .NET 10 tidak lagi menyediakan handler bawaan untuk CTRL_SHUTDOWN_EVENT/CTRL_CLOSE_EVENT Windows (setara SIGTERM/SIGHUP di Unix), penanganan bawaan OS mengakhiri aplikasi seketika sehingga AppDomain.ProcessExit dan AssemblyLoadContext.Unloading tidak muncul, serta pemrosesan sinyal yang sesuai model aplikasi harus didaftarkan di pustaka tingkat atas atau kode aplikasi. ↩ ↩2

  8. Microsoft Learn, Service Control Handler Function. Tentang layanan yang menyatakan SERVICE_ACCEPT_PRESHUTDOWN menerima SERVICE_CONTROL_PRESHUTDOWN lebih dulu, lalu layanan SERVICE_ACCEPT_SHUTDOWN menerima SERVICE_CONTROL_SHUTDOWN, masa tenggang bawaan saat shutdown sekitar 20 detik dan batas atas saat restart OS adalah WaitToKillServiceTimeout, nilai itu tidak boleh diperpanjang, control handler harus kembali dalam 30 detik dengan STOP_PENDING dan wait hint lalu pekerjaan lama diserahkan ke thread lain, pembersihan harus diselesaikan secepat mungkin dengan mempertimbangkan operasi UPS, serta SCM saat shutdown secara bawaan tidak mempertimbangkan dependensi. ↩ ↩2 ↩3 ↩4 ↩5

  9. Microsoft Learn, SERVICE_PRESHUTDOWN_INFO structure (winsvc.h). Tentang SCM yang menunggu sampai layanan berhenti atau timeout setelah notifikasi PRESHUTDOWN, timeout bawaan 10 detik sejak Windows 10 Creators Update (build 15063) dan 3 menit sebelumnya, konfigurasi dengan ChangeServiceConfig2, serta pembaruan status yang dapat dilanjutkan selama SERVICE_STOP_PENDING. ↩ ↩2

  10. Microsoft Learn, RegisterApplicationRestart function (winbase.h). Tentang pendaftaran restart pada skenario crash, tidak merespons, pembaruan, dan restart komputer karena pembaruan, kemampuan menentukan argumen baris perintah saat restart, pendaftaran sebelum masalah terjadi dan kesempatan terakhir pada skenario pembaruan adalah selama pemrosesan WM_QUERYENDSESSION, proses belum 60 detik sejak start tidak di-restart, restart saat crash/hang melalui persetujuan pengguna, serta menyeberangi restart OS memerlukan shutdown dengan EWX_RESTARTAPPS/SHUTDOWN_RESTARTAPPS. ↩ ↩2 ↩3

  11. Microsoft Learn, Winlogon automatic restart sign-on (ARSO). Tentang Windows Update yang saat memulai restart otomatis menyimpan kredensial pengguna interaktif terakhir dan mengonfigurasi Autologon, men-sign-in pengguna secara otomatis setelah restart lalu mengunci sesi, menghapus kredensial tersimpan setelah sign-in berhasil, serta konfigurasi lewat kebijakan grup (DisableAutomaticRestartSignOn, dll.). ↩ ↩2

  12. Microsoft Learn, ReplaceFileW function (winbase.h). Tentang ReplaceFile yang merangkum beberapa langkah setara “simpan ke berkas baru, ganti nama sementara berkas asli, ganti nama berkas baru, hapus berkas asli” ke satu fungsi, mempertahankan atribut berkas asli seperti waktu pembuatan, DACL, enkripsi, kompresi, dan named stream, serta keharusan berkas cadangan, sasaran penggantian, dan berkas pengganti berada di volume yang sama. ↩ ↩2

  13. Microsoft Learn, File Caching. Tentang tulis yang secara bawaan masuk ke cache sistem lalu sampai ke disk lewat lazy write, FILE_FLAG_WRITE_THROUGH yang menulis ke disk segera, FlushFileBuffers untuk flush eksplisit, serta metadata sistem berkas yang selalu di-cache sehingga penetapan metadata memerlukan flush atau write-through. ↩ ↩2 ↩3

  14. Microsoft Learn, Troubleshoot unexpected reboots using system event logs. Tentang event ID 1074 (proses mana, untuk siapa, dengan alasan apa memulai shutdown) yang tercatat pada restart normal, event ID 41 (Kernel-Power) dan 6008 (shutdown sebelumnya tidak diharapkan) pada restart tak terduga, serta kemampuan mengisolasi jenis restart dengan ID tersebut. ↩ ↩2

  15. Microsoft Learn, PBT_APMPOWERSTATUSCHANGE event. Tentang event ini yang diberitahu lewat WM_POWERBROADCAST saat peralihan baterai dan AC atau turunnya sisa kapasitas, serta keharusan memanggil GetSystemPowerStatus setelah diterima dan memeriksa ACLineStatus, BatteryFlag, BatteryLifePercent, dan sejenisnya di SYSTEM_POWER_STATUS. ↩

Artikel terbaru dengan tag yang sama untuk mendalami topik-topik terdekat.

Halaman-halaman ini menempatkan topik dalam konteks layanan dan keputusan yang lebih luas.

Artikel ini berkaitan langsung dengan layanan berikut.

Pertanyaan yang sering diajukan

Pertanyaan yang sering muncul dalam konsultasi tentang topik artikel ini.

Gangguan yang tidak hilang dengan "Shut down" justru hilang setelah "Restart". Mengapa?
Pada OS klien Windows 8 ke atas, jika fast startup aktif (bawaan pada banyak PC yang mendukung hibernasi), "Shut down" berjalan sebagai hybrid shutdown. Pengguna memang keluar, tetapi state kernel dan driver disimpan ke berkas hibernasi dan dipulihkan apa adanya pada boot berikutnya. Inti OS belum di-reset. Sebaliknya, "Restart" selalu melakukan boot penuh, sehingga gangguan driver dan layanan ikut ter-reset. Dalam prosedur isolasi masalah, tulis "Restart", bukan "Shut down lalu nyalakan lagi". Untuk shutdown penuh dari baris perintah, shutdown /s dapat dipakai.
Bisakah shutdown ditahan sampai aplikasi selesai menyimpan?
Meminta penundaan sementara bisa, menahannya secara andal tidak. Jika string alasan didaftarkan dengan ShutdownBlockReasonCreate hanya selama operasi yang tidak boleh dipotong, alasan itu tampil di layar "aplikasi ini menghalangi shutdown" dan pengguna dapat memilih lanjut atau batal. Pengguna tetap bisa memaksa lanjut, dan shutdown paksa atau restart karena pembaruan kadang tidak menunggu. Jadi pendekatan yang benar bukan "memblokir", melainkan autosave yang sering agar data yang bisa hilang sedikit, plus pembersihan yang dirancang selesai dalam beberapa detik sejak notifikasi keluar.
Penghentian layanan Windows saya lama. Bisakah masa tenggang saat shutdown diperpanjang?
Pada konfigurasi bawaan yang menerima SERVICE_CONTROL_SHUTDOWN, masa tenggang kira-kira 20 detik dan bergantung pada WaitToKillServiceTimeout di registri. Menulis ulang nilai itu dari sisi aplikasi untuk memperpanjangnya tidak direkomendasikan. Jika masa tenggang lebih panjang memang diperlukan, nyatakan SERVICE_ACCEPT_PRESHUTDOWN dan terima SERVICE_CONTROL_PRESHUTDOWN; notifikasi datang lebih dulu daripada yang lain, dan timeout dapat dikonfigurasi dengan ChangeServiceConfig2 (bawaan 10 detik sejak Windows 10 Creators Update, 3 menit sebelumnya). Namun PRESHUTDOWN menahan seluruh shutdown selama interval itu, jadi batasi pada kasus yang benar-benar perlu, dan secara mendasar rancang proses berhenti itu sendiri agar selesai dalam beberapa detik.
Apakah aman mengandalkan AppDomain.ProcessExit milik .NET untuk pembersihan saat shutdown?
Sebaiknya jangan diandalkan. Dulu runtime mendaftarkan handler sinyal bawaan, dan ProcessExit muncul pada CTRL_CLOSE_EVENT maupun CTRL_SHUTDOWN_EVENT; sejak .NET 10 runtime tidak lagi menyediakan handler sinyal terminasi bawaan, sehingga ProcessExit tidak muncul pada kasus itu. Implementasikan pembersihan pada jalur notifikasi yang sesuai model aplikasi: aplikasi GUI memakai FormClosing atau SessionEnding (itu notifikasi tahap kueri, jadi batasi pada penyimpanan yang idempoten; pembersihan yang hanya boleh dijalankan setelah sesi dikomit dilakukan di hook WM_ENDSESSION); Generic Host / Worker Service memakai IHostApplicationLifetime dan StopAsync; aplikasi konsol memakai SetConsoleCtrlHandler atau PosixSignalRegistration.
Bagaimana mencegah berkas rusak saat daya putus tiba-tiba?
Putus daya tidak membawa notifikasi sama sekali, jadi satu-satunya cara adalah menulis agar tidak rusak kapan pun dayanya terputus. Dasarnya: jangan menimpa berkas asli di tempat; tulis tuntas ke berkas sementara pada volume yang sama, flush, lalu tukar dengan ReplaceFile (File.Replace di .NET). Pada operasi biasa, yang terbaca adalah berkas lama yang utuh atau berkas baru yang utuh, tetapi atomisitas ReplaceFile menyeberangi putus daya tidak dijamin spesifikasi. Karena itu simpan cadangan (argumen ketiga) dan implementasikan pemuatan yang memvalidasi berkas utama lalu kembali ke cadangan jika rusak. Selain itu, sukses WriteFile tidak berarti data sudah sampai disk; pada titik penting, pastikan tulis dengan FlushFileBuffers atau FILE_FLAG_WRITE_THROUGH. Pada PC perangkat, pola standar adalah menggabungkannya dengan UPS, mendeteksi peralihan ke baterai, lalu mengantar ke shutdown yang aman.

Profil penulis

Halaman perkenalan penulis artikel.

Go Komura

Direktur KomuraSoft LLC

Berspesialisasi dalam pengembangan perangkat lunak Windows, konsultasi teknis, dan investigasi bug, terutama pada proyek dengan sistem yang sudah ada dan bug yang sulit direproduksi.

Kembali ke blog