Apa sebenarnya "Tidak Merespons" — cara Windows menilai aplikasi hang, dan desain agar tidak hang
· Diperbarui pada: · Go Komura · Windows, Pengembangan Windows, Pemecahan masalah, Multithreading, WinForms, WPF, Win32 API, Desain UI
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.22176671)
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). Apa sebenarnya "Tidak Merespons" — cara Windows menilai aplikasi hang, dan desain agar tidak hang. KomuraSoft LLC. https://comcomponent.com/id/blog/windows-app-not-responding-hang-mechanism/
- DOI (arsip terdaftar)
- 10.5281/zenodo.22176671
- DOI (versi terakhir yang didaftarkan)
- 10.5281/zenodo.22176672
“Aplikasi memutih di tengah pemrosesan dan muncul (Tidak Merespons).” “Ada laporan kadang hang, tetapi di mesin pengembangan tidak bisa direproduksi.” — Untuk aplikasi bisnis Windows, “Tidak Merespons” ini termasuk keluhan yang paling sering. Yang jarang disadari: yang menampilkan “Tidak Merespons” bukan aplikasi yang hang itu sendiri, melainkan OS.
Bagaimana Windows mengetahui bahwa aplikasi “hang”? Jendela yang memutih kabur itu apa? Artikel ini ditujukan kepada pengembang yang membuat aplikasi bisnis di Windows, dan kepada staf IT yang menerima laporan aplikasi hang. Mekanisme penilaian “Tidak Merespons” diuraikan dari dasar message loop, lalu disusun penyebab hang yang lazim, desain agar tidak hang, dan prosedur investigasi pada saat hang — berdasarkan sumber primer.
1. Kesimpulan dulu
- “Tidak Merespons” adalah penilaian OS. Jendela (dan thread GUI pemiliknya) yang tidak sedang menunggu input, tidak sedang dalam pemrosesan start, dan selama 5 detik belum mengambil pesan (
PeekMessage) diperlakukan OS sebagai tidak merespons. Penilaian ini bukan per proses.1 - Jendela yang memutih adalah “ghost window”. OS menyembunyikan jendela asli dan menggantinya dengan salinan palsu pada posisi, ukuran, dan tampilan yang sama. Yang bisa dilakukan hanya memindahkan, meminimalkan, atau menutup; isinya tidak berjalan. Saat debugger terhubung, ghost window tidak dibuat.2
- Penyebab hang hampir selalu satu hal. Thread UI yang seharusnya memutar message loop tersumbat oleh pekerjaan berat atau menunggu. I/O sinkron, panggilan jaringan, menunggu lock, dan
SendMessageantar-thread adalah pola klasik.3 - Prinsip penanggulangan: jangan menunggu dan jangan menghitung di thread UI. Pindahkan pekerjaan berat ke thread worker (
async/await+Task.Rundi C#), dan biarkan thread UI fokus pada menggambar, progres, dan menerima pembatalan.4 DoEventsdan penggerakan message pump secara manual adalah sarang bug reentrancy. Tampilan Tidak Merespons memang hilang, tetapi strukturnya membiarkan event mana pun menyela di tengah pemrosesan. Yang benar bukan menghindar, melainkan memisahkan pekerjaan.- Investigasi dimulai dari mengamankan state pada saat hang. Ambil dump lalu lihat stack thread UI; hampir selalu terlihat apa yang ditunggu sampai macet.
2. Prasyarat: aplikasi Windows bergerak lewat pesan
Untuk memahami “Tidak Merespons”, pertama-tama perlu dipegang bahwa aplikasi GUI Windows bersifat event-driven. Aplikasi GUI tidak menjemput input sendiri; ia menerima pesan yang dikirim OS (mouse, keyboard, permintaan menggambar ulang, timer, dan sebagainya) lalu bertindak.3
Setiap thread yang membuat jendela punya message queue, dan memutar message loop seperti berikut.
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg); // window procedure dipanggil
}
GetMessage mengambil pesan dari queue, lalu DispatchMessage memanggil window procedure jendela itu (fungsi penanganan pesan). Penanganan klik tombol, menggambar ulang, maupun event handler WinForms atau WPF, jika ditelusuri sampai ujung, semuanya dijalankan di dalam satu putaran loop ini.5
flowchart TB
accTitle: Struktur dasar message loop
accDescr: OS memasukkan input seperti mouse dan keyboard ke message queue thread; loop thread UI mengambilnya dengan GetMessage, memanggil window procedure lewat DispatchMessage, dan setelah pemrosesan selesai kembali ke awal loop
os["OS (input, permintaan menggambar ulang, timer)"] --> q["Message queue thread"]
q --> gm["Ambil dengan GetMessage"]
gm --> dm["DispatchMessage"]
dm --> wp["Diproses di window procedure"]
wp --> gm
Gambar 1: Jantung aplikasi GUI adalah message loop; setiap penanganan event dijalankan sebagai satu putaran loop ini.
Struktur ini punya satu konsekuensi penting. Jika pekerjaan yang memakan waktu dilakukan di dalam window procedure (event handler), selama itu loop tidak bisa mengambil pesan berikutnya. Tidak bisa bereaksi terhadap klik maupun permintaan menggambar ulang — itulah wujud “hang”.
Cara pesan sampai juga perlu dipegang: ada dua jalur. PostMessage menaruh pesan di queue lalu segera kembali, dan loop mengambil serta memprosesnya berurutan. Sebaliknya, SendMessage memanggil window procedure secara langsung, dan tidak kembali ke pemanggil sampai pemrosesan selesai.36 Perbedaan ini langsung relevan untuk deadlock di bab 4.
flowchart TB
accTitle: Dua jalur pengiriman pesan
accDescr: PostMessage menaruh pesan di queue lalu segera kembali, dan message loop mengambil serta memprosesnya berurutan. SendMessage memanggil window procedure secara langsung dan tidak kembali ke pemanggil sampai pemrosesan selesai
pm["PostMessage"] --> q2["Taruh di queue (segera kembali)"]
q2 --> loop["Loop mengambil dan memproses berurutan"]
sm["SendMessage"] --> direct["Panggil procedure secara langsung"]
direct --> w2["Tidak kembali sampai pemrosesan selesai"]
Gambar 2: Keduanya “mengirim pesan”, tetapi Post lewat queue dan Send yang menunggu selesai punya sifat yang sama sekali berbeda.
3. Mekanisme penilaian “Tidak Merespons” — aturan 5 detik dan ghost window
Lalu, bagaimana OS mengetahui bahwa “aplikasi ini hang”? Kriteria penilaiannya didokumentasikan secara resmi. OS memperlakukan jendela sebagai tidak merespons jika jendela itu tidak sedang menunggu input, tidak sedang dalam pemrosesan start, dan selama 5 detik belum memanggil PeekMessage (pengambilan pesan).1 Dengan kata lain, OS mengamati apakah “message loop benar-benar berputar” seperti denyut nadi, dan jika tidak ada denyut selama 5 detik, jendela dinilai tidak merespons (dokumentasi menyatakan bahwa nilai 5 detik ini bisa berubah di masa depan). Satuan penilaian adalah jendela dan thread GUI pemiliknya. Pada aplikasi dengan beberapa thread UI, satu thread hang tidak berarti jendela di thread lain ikut mati. Yang perlu dilihat di dump adalah thread pemilik jendela yang hang.
Apa yang terjadi pada jendela tingkat atas yang sudah dinilai juga didokumentasikan. OS menyembunyikan jendela asli, lalu menggantinya dengan “ghost window” yang punya Z-order, posisi, ukuran, dan tampilan yang sama. Yang bisa dilakukan pengguna hanya memindahkan, mengubah ukuran, atau menutup (secara paksa). Aplikasi di dalamnya sebenarnya tidak merespons, jadi operasi lain tidak bisa.2
flowchart TB
accTitle: Alur penilaian Tidak Merespons dan penggantian ghost window
accDescr: Ketika thread UI tersumbat pekerjaan berat dan pengambilan pesan berhenti selama 5 detik, OS menilai jendela tidak merespons, menyembunyikan jendela asli, menggantinya dengan ghost window yang tampilannya sama, dan hanya menyediakan operasi pindah, minimalkan, dan tutup bagi pengguna
busy["Thread UI tersumbat pekerjaan berat"] --> stop["Pengambilan pesan berhenti"]
stop --> judge{"5 detik berlalu?"}
judge -->|"tidak"| stop
judge -->|"ya"| ghost["Diganti ghost window"]
ghost --> u1["Title menampilkan (Tidak Merespons)"]
ghost --> u2["Memutih kabur; hanya pindah dan tutup"]
Gambar 3: Tampilan “Tidak Merespons” maupun layar putih milik ghost window yang OS pasang sebagai pengganti, bukan milik aplikasi yang hang.
String “(Tidak Merespons)” di title bar, dan tampilan yang memutih kabur pada tema Aero, semuanya milik ghost window ini. Dari sini ada dua konsekuensi praktis.
- Pada saat “Tidak Merespons” ditampilkan, thread pemilik jendela itu sudah setidaknya 5 detik tidak memproses pesan. Bukan “tampilannya terlalu cepat muncul” — thread UI memang tersumbat.
- Saat debugger terhubung, ghost window tidak dibuat.2 Jika tampak “saat debug Tidak Merespons tidak muncul, tetapi di release muncul”, hang-nya sendiri bisa sama dan yang berbeda hanya tampilannya.
Selain itu, ada API DisableProcessWindowsGhosting yang menonaktifkan penggantian ini per proses.7 API ini untuk kasus khusus seperti terminal kiosk, di mana OS tidak diinginkan membuat jendela tampak seolah masih bisa dioperasikan. Memanggilnya membuat tampilan “Tidak Merespons” tidak muncul, tetapi fakta bahwa aplikasi hang tidak berubah. Pahami bahwa ini bukan sesuatu yang dipakai aplikasi umum sebagai penanggulangan Tidak Merespons.
4. Mengapa hang — pola klasik yang menyumbat thread UI
Jika ditelusuri sampai ujung, penyebabnya satu: “thread UI tidak kembali ke message loop”. Namun bentuk yang dijumpai di praktik bisa dikelompokkan ke beberapa pola klasik.
flowchart TB
accTitle: Klasifikasi penyebab klasik yang menyumbat thread UI
accDescr: Empat keluarga klasik — I/O sinkron dan panggilan jaringan, menunggu lock, SendMessage antar-thread, dan keterlibatan COM STA — semuanya bermuara pada satu hal yang sama: thread UI tidak bisa kembali ke message loop
c1["I/O sinkron dan panggilan jaringan"] --> core["Thread UI tidak bisa kembali ke loop"]
c2["Menunggu lock"] --> core
c3["SendMessage antar-thread"] --> core
c4["Keterlibatan COM STA"] --> core
core --> ar["Menuju penilaian Tidak Merespons"]
Gambar 4: Gejalanya sama, tetapi pelakunya yang menyumbat terbagi empat keluarga, dan penanggulangannya berbeda.
I/O sinkron dan panggilan jaringan. Ini yang paling sering. Pola di mana di dalam click handler tombol, baca-tulis file besar, query database, panggilan Web API, atau akses file di network drive dilakukan secara sinkron. Di mesin pengembangan selesai dalam pecahan detik sehingga tidak terasa, lalu di produksi latency jaringan atau gangguan file server membuat tunggu puluhan detik, dan muncul laporan “kadang hang”. Network drive punya timeout yang panjang saat koneksi putus, dan itu memperburuk gejala secara drastis.
Menunggu lock. Thread UI mencoba mengambil lock atas data yang dipakai bersama thread worker, lalu terpaksa menunggu worker yang memegang lock itu dalam waktu lama. Disiplin lock dibahas lebih rinci di seri praktik multithreading.
SendMessage antar-thread. SendMessage tidak kembali sampai procedure jendela tujuan selesai memproses.6 Jika dikirim ke jendela di thread lain, pengirim menunggu sampai thread itu mampu memproses pesan. Jika thread tujuan juga sedang menunggu sesuatu, keduanya saling menunggu dan terbentuk message deadlock.3 Pengiriman ke HWND_BROADCAST khususnya mudah terseret hanya karena ada satu jendela yang tidak merespons. Jika tidak bisa menunggu, pertimbangkan SendMessageTimeout atau PostMessage yang tidak menunggu jawaban.8
sequenceDiagram
accTitle: Deadlock akibat SendMessage antar-thread
accDescr: Ketika thread UI sedang memblokir sambil menunggu hasil thread worker, lalu thread worker mengirim SendMessage ke jendela thread UI, keduanya saling menunggu penyelesaian lawan dan terjadi deadlock
participant U as Thread UI
participant W as Thread worker
U->>U: Menunggu selesai worker (blokir)
W->>U: SendMessage (tidak kembali sampai selesai)
Note over U: Tidak bisa memproses pesan (sedang menunggu)
Note over W: Tidak bisa kembali dari SendMessage
Note over U,W: Saling menunggu, deadlock
Gambar 5: “Thread UI menunggu worker, worker menunggu thread UI lewat SendMessage” adalah deadlock klasik.
Keterlibatan apartment COM. Panggilan ke objek STA dikirim sebagai window message, sehingga jika thread UI (STA) tersumbat, panggilan COM dari thread lain ikut terblokir. Struktur ini dijelaskan di artikel COM STA/MTA.
Akumulasi “kan cuma sebentar”. Pemrosesan sinkron 50 ms sekali, jika dipanggil 100 kali dalam loop, sudah 5 detik. Ambang Tidak Merespons adalah 5 detik, tetapi rasa “lambat” mulai terasa sekitar 100 ms. Patokan desain: “thread UI hanya boleh tersumbat dalam satuan milidetik”.
5. Desain agar tidak hang — keluarkan pekerjaan berat dari thread UI
Prinsip penanggulangannya satu: keluarkan pekerjaan yang memakan waktu dari thread UI. Di C# (WinForms/WPF), async/await adalah jalan terpendek yang benar.
private async void RunButton_Click(object sender, EventArgs e)
{
runButton.Enabled = false;
try
{
// Pekerjaan berbeban CPU tinggi / API yang hanya sinkron dipindah ke worker dengan Task.Run
var result = await Task.Run(() => HeavyCalculation(input));
// I/O memakai API yang native-nya async (tidak mengonsumsi thread)
var data = await httpClient.GetStringAsync(url);
// Setelah await sudah kembali ke thread UI, jadi kontrol bisa disentuh langsung
resultLabel.Text = result + data.Length.ToString();
}
catch (Exception ex)
{
// Exception yang bocor dari handler async void membuat aplikasi crash. Tangkap di sini
MessageBox.Show($"Pemrosesan gagal: {ex.Message}");
}
finally
{
runButton.Enabled = true;
}
}
Ada tiga poin. Pertama, selama menunggu await, thread UI sudah kembali ke message loop sehingga Tidak Merespons tidak terjadi. Kedua, kelanjutan await kembali ke thread UI, sehingga setelahnya kontrol bisa disentuh seperti biasa (menyentuh kontrol langsung dari thread worker dilarang; jika perlu, pakai Control.Invoke / Dispatcher.InvokeAsync).4 Ketiga, selama eksekusi nonaktifkan tombol dan sejenisnya agar reentrancy dimatikan lewat desain.
Di Win32 native polanya sama: serahkan pekerjaan ke thread worker, beri tahu selesai ke thread UI sebagai pesan buatan sendiri lewat PostMessage, lalu perbarui tampilan di window procedure. PostMessage hanya menaruh di queue lalu segera kembali, jadi sisi worker tidak ikut terblokir.6 Penantian thread worker itu sendiri ditulis dengan disiplin yang dibahas di artikel condition variable.
flowchart TB
accTitle: Pembagian peran aplikasi yang tidak hang
accDescr: Thread UI hanya menangani penerimaan input, tampilan progres, dan penerimaan pembatalan; pekerjaan berat dijalankan thread worker, lalu penyelesaian dikembalikan ke thread UI lewat PostMessage atau kelanjutan await
ui["Thread UI: input, progres, pembatalan"] -->|"Serahkan pekerjaan"| w["Thread worker: pekerjaan berat"]
w -->|"PostMessage / kelanjutan await"| ui
ui -.-> ng["I/O sinkron dan perhitungan panjang di thread UI dilarang"]
Gambar 6: Thread UI cukup menjadi “penerima”, dan pekerjaan berat selalu diserahkan ke worker; yang diterima hanya pemberitahuan selesai.
Yang hendak dihindari adalah teknik menyelipkan Application.DoEvents() atau loop PeekMessage di sela pekerjaan berat agar tampilan saja tetap hidup. Tidak Merespons memang terhindari, tetapi di tengah pemrosesan event handler mana pun bisa masuk kembali. Klik tombol dua kali, menutup form saat masih memproses, timer yang menyala — semuanya bisa merusak data yang sedang diproses, dan menjadi bug yang bergantung timing serta sulit direproduksi. Memutar message pump secara manual hanya ditahan di dalam struktur terbatas seperti dialog progres modal; sebagai prinsip, selesaikan dengan pemisahan.
sequenceDiagram
accTitle: Urutan waktu bug reentrancy akibat DoEvents
accDescr: Jika DoEvents dipanggil di tengah pekerjaan berat, event handler klik yang tertunda menyela dan dijalankan, menimpa data yang sedang diproses, lalu pemrosesan asli dilanjutkan sehingga terjadi kerusakan data yang bergantung timing
participant U as Thread UI
U->>U: Mulai pekerjaan berat (sedang mengolah data)
U->>U: DoEvents (proses pesan yang menumpuk)
Note over U: Handler klik tombol sekali lagi menyela
U->>U: Pemrosesan yang menyela menimpa data
U->>U: Pemrosesan asli dilanjutkan (data sudah tidak konsisten)
Gambar 7: DoEvents menghilangkan “Tidak Merespons”, tetapi sebagai gantinya mengundang event mana pun ke tengah pemrosesan.
Untuk pemrosesan yang lama, tampilan progres dan pembatalan juga dimasukkan ke desain. Kirim progres ke UI lewat IProgress<T>, dan sampaikan pembatalan lewat CancellationToken. Dengan begitu pengguna tahu bahwa “masih berjalan”, dan tidak terburu memaksa keluar (yang sering menjadi penyebab kerusakan data).
flowchart TB
accTitle: Alur progres dan pembatalan pada pemrosesan lama
accDescr: Thread worker mengirim progres ke thread UI lewat IProgress; operasi batal di UI disampaikan ke worker lewat CancellationToken; worker berhenti di titik yang rapi lalu merapikan sisa pekerjaan
w3["Worker: pemrosesan lama"] -->|"Progres lewat IProgress"| ui2["UI: tampilan progres dan tombol batal"]
ui2 -->|"CancellationToken"| w3
w3 --> stop2["Berhenti di titik yang rapi, lalu merapikan"]
Gambar 8: Progres dari “worker → UI”, pembatalan dari “UI → worker”. Jalur komunikasi tipis dua arah ini dimasukkan ke desain sejak awal.
6. Cara menyelidiki saat hang
Pada investigasi “kadang hang”, yang paling bernilai adalah state thread tepat pada saat hang itu. Jika sudah direstart, buktinya hilang.
Ambil dump. Di tab Detail Task Manager, klik kanan proses target → “Buat file dump”. Dengan itu saja full dump yang berisi stack semua thread sudah didapat. Cukup menyampaikan ke sisi IT yang menerima laporan, “jika hang, jangan ditutup; ambil ini dulu”, keberhasilan investigasi berubah besar. Cara merancang pengumpulan dibahas di artikel pengumpulan crash dump.
Lihat stack thread UI. Buka dump di WinDbg, lalu lihat stack thread yang memutar message loop (biasanya thread 0). Jika I/O sinkron, ReadFile atau API jaringan; jika menunggu lock, keluarga WaitFor…; jika SendMessage antar-thread, tampak sedang menunggu di dalam SendMessage — semuanya terekam apa adanya. Cara membacanya dijelaskan di artikel pengantar WinDbg.
Lihat dalam keadaan hidup. Dengan Process Explorer, daftar thread dan stack bisa dicek di tempat. Jika secara rutin terasa lambat, ambil trace dengan WPR lalu analisis tunggu thread UI sepanjang waktu (praktik WPR/WPA).
flowchart TB
accTitle: Prosedur dasar investigasi Tidak Merespons
accDescr: Ambil dump pada saat hang, lihat stack thread UI, tentukan apakah macet di I/O sinkron, menunggu lock, atau SendMessage antar-thread, lalu hubungkan ke perbaikan desain yang sesuai
hang["Saat hang"] --> dump["Ambil dump (sebelum ditutup)"]
dump --> stack["Lihat stack thread UI"]
stack --> io["I/O sinkron / menunggu jaringan"]
stack --> lock["Menunggu lock"]
stack --> sm["SendMessage antar-thread"]
io -.-> fix["Pisahkan bagian terkait ke worker"]
lock -.-> fix
sm -.-> fix
Gambar 9: Pemeran utama investigasi adalah “dump pada saat hang”; stack thread UI langsung menjadi klasifikasi penyebab.
Cara mengarahkan dari gejala juga bisa distandarkan. Jika selalu hang pada operasi tertentu, curigai dulu I/O sinkron di dalam handler itu. Jika jarang hang dan tidak berkorelasi dengan operasi, curigai urutan lock atau deadlock SendMessage antar-thread, lalu cocokkan tujuan tunggu kedua thread di dump. Jika hanya hang di lingkungan tertentu, curigai timeout dari faktor lingkungan seperti network drive, proxy, atau perangkat lunak antivirus.
7. Ringkasan
- “Tidak Merespons” adalah mekanisme OS yang menilai aplikasi belum mengambil pesan selama 5 detik, lalu menggantinya dengan ghost window. Yang menampilkan itu OS, bukan aplikasi.
- Penyebab hang satu: “thread UI tidak bisa kembali ke message loop”. I/O sinkron, jaringan, menunggu lock, dan SendMessage antar-thread adalah yang klasik.
- Penanggulangannya: keluarkan pekerjaan berat dari thread UI. Di C#,
async/await+Task.Run; di Win32, thread worker +PostMessage. Reentrancy selama eksekusi dicegah lewat desain, misalnya menonaktifkan tombol. - Menghindar dengan
DoEventsditukar bug reentrancy.DisableProcessWindowsGhostinghanya menghilangkan tampilan. Keduanya bukan penanggulangan akar. - Untuk investigasi, dump “pada saat hang” paling penting. Penyebabnya hampir selalu terekam apa adanya di stack thread UI.
Dari sisi pengguna, “Tidak Merespons” terasa “rusak”. Setelah mekanismenya diketahui, itu bisa diterjemahkan menjadi satu kalimat yang tepat: “thread UI tidak kembali selama 5 detik”. Jika dihitung mundur dari kalimat itu, kandidat penyebab, cara memperbaikinya, dan prosedur investigasi jatuh dengan sendirinya.
Artikel terkait
- Spurious wakeup — mengapa condition variable “bangun tanpa notifikasi”, dan cara menunggu yang benar di Windows
- Praktik terbaik multithreading di lapangan, edisi .NET
- Dasar COM STA/MTA — model thread dan cara berpikir agar tidak hang
- Membaca crash dump dengan WinDbg + SOS — pengantar analisis praktis setelah pengumpulan
- Praktik Process Explorer / Handle / VMMap — menelusuri hang, kebocoran, dan “file sedang digunakan” dari state saat ini
- Shutdown Windows dari sisi aplikasi — selamat dari notifikasi keluar, restart, dan putus daya dengan benar
Area konsultasi terkait
KomuraSoft LLC menangani investigasi penyebab aplikasi bisnis yang “kadang hang” atau menjadi “Tidak Merespons” (analisis dump dan analisis trace), perbaikan kode UI warisan yang penuh pemrosesan sinkron menjadi pemisahan async/await dan thread worker, serta tinjauan desain UI yang tidak membeku. Bahkan pada tahap prosedur reproduksi belum jelas, bantuan bisa dimulai dari merancang cara mengambil bukti.
- Investigasi bug dan analisis penyebab
- Konsultasi teknis dan tinjauan desain
- Pengembangan aplikasi Windows
- Hubungi kami
Tautan referensi
-
Microsoft Learn, IsHungAppWindow function (winuser.h). Tentang kriteria penilaian bahwa aplikasi diperlakukan tidak merespons jika “tidak sedang menunggu input, tidak sedang dalam pemrosesan start, dan selama timeout internal 5 detik belum memanggil PeekMessage”; bahwa kriteria 5 detik ini bisa berubah; dan bahwa untuk ghost window selalu mengembalikan TRUE. ↩ ↩2
-
Microsoft Learn, GetMessage function (winuser.h). Tentang sistem yang memperlakukan jendela tingkat atas sebagai tidak merespons jika beberapa detik tidak merespons pesan, lalu menggantinya dengan ghost window yang punya Z-order, posisi, ukuran, dan tampilan yang sama; bahwa pengguna hanya bisa memindahkan, mengubah ukuran, atau menutup; dan bahwa ghost window tidak dibuat saat debugger terhubung. ↩ ↩2 ↩3
-
Microsoft Learn, About Messages and Message Queues. Tentang aplikasi Windows yang bersifat event-driven dan window procedure yang memproses pesan; perbedaan pesan lewat queue dan pesan yang dikirim langsung; penggantian jendela tidak merespons dengan ghost window; dan bagian deadlock akibat thread yang saling mengirim pesan. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, How to make thread-safe calls to controls (Windows Forms). Tentang kontrol WinForms yang tidak aman disentuh dari thread selain yang membuatnya; bahwa pembaruan dari thread lain memakai Invoke/BeginInvoke; dan pola asinkron yang aman dengan async/await atau BackgroundWorker. ↩ ↩2
-
Microsoft Learn, Using Messages and Message Queues. Tentang contoh implementasi message loop standar dengan GetMessage, TranslateMessage, dan DispatchMessage, serta cara memeriksa message queue. ↩
-
Microsoft Learn, SendMessage function (winuser.h). Tentang SendMessage yang memanggil window procedure jendela yang ditentukan dan tidak kembali sampai pemrosesan selesai; bahwa kiriman ke jendela di thread lain membuat pengirim menunggu sampai thread itu memproses pesan; dan perbedaan dari PostMessage yang hanya menaruh di queue tanpa menunggu jawaban. ↩ ↩2 ↩3
-
Microsoft Learn, DisableProcessWindowsGhosting function (winuser.h). Tentang dapat menonaktifkan, untuk proses GUI pemanggil, fitur ghost window yang membuat jendela tidak merespons bisa diminimalkan, dipindahkan, dan ditutup; dan bahwa penonaktifan berlangsung selama masa hidup proses. ↩
-
Microsoft Learn, SendMessageTimeout function (winuser.h). Tentang dapat mengirim pesan dengan timeout; dan bahwa ada flag (SMTO_ABORTIFHUNG) yang kembali tanpa menunggu jika jendela tidak merespons (sudah dinilai hang). ↩
Artikel terkait
Artikel terbaru dengan tag yang sama untuk mendalami topik-topik terdekat.
DllMain dan loader lock — alasan sebenarnya di balik peringatan "jangan lakukan apa pun saat inisialisasi DLL"
Mengapa LoadLibrary dan sinkronisasi antar-thread dilarang di dalam DllMain. Dari mekanisme loader lock yang menserialisasi semua notifik...
API thread pool Win32 — konkurensi tanpa membuat thread, lewat CreateThreadpoolWork
Apakah kode native masih menumpuk CreateThread? Artikel ini menjelaskan API thread pool Win32 yang didesain ulang di Vista: empat objek w...
Aplikasi rusak setelah bangun dari tidur — mekanisme event daya dan cara merancang aplikasi bisnis yang tahan bangun
Laptop dibuka, koneksi aplikasi bisnis sudah putus — penyebabnya desain yang tidak memperhitungkan tidur. Artikel ini menata alur notifik...
Cara kerja clipboard dan seret-dan-lepas — menangani transfer data OLE dengan benar di aplikasi bisnis
Tabel Excel berantakan saat ditempel, atau tidak bisa menempel setelah aplikasi sumber ditutup — keduanya berasal dari clipboard yang men...
Membaca kode error Windows — struktur tiga lapis Win32, HRESULT, dan NTSTATUS
Jika 0x80004005 muncul, uraikan dulu sebelum mencari. Artikel ini membahas struktur tiga lapis Win32, HRESULT, dan NTSTATUS, pola terpent...
Topik terkait
Halaman-halaman ini menempatkan topik dalam konteks layanan dan keputusan yang lebih luas.
Topik teknis Windows
Portal tentang pengembangan Windows, investigasi bug, dan pemanfaatan aset yang ada.
Thread UI dan timer
Thread UI WPF / WinForms, alur asinkron, Dispatcher, dan desain timer.
Layanan yang terkait dengan topik ini
Artikel ini berkaitan langsung dengan layanan berikut.
Pengembangan aplikasi Windows
Aplikasi bisnis, integrasi perangkat, dan alat komunikasi, dari kebutuhan hingga pengembangan.
Pertanyaan yang sering diajukan
Pertanyaan yang sering muncul dalam konsultasi tentang topik artikel ini.
- Syarat apa yang membuat "Tidak Merespons" muncul?
- OS menilai jendela tidak merespons jika aplikasi yang punya jendela tidak sedang menunggu input, tidak sedang dalam pemrosesan start, dan selama 5 detik belum mengambil pesan (PeekMessage). Jendela tingkat atas yang dinilai hang disembunyikan, lalu diganti ghost window dengan posisi, ukuran, dan tampilan yang sama. Teks "(Tidak Merespons)" di title bar dan tampilan yang memutih kabur milik ghost window ini; yang bisa dilakukan hanya memindahkan, meminimalkan, atau menutup. Jadi "Tidak Merespons" bukan tampilan dari aplikasi itu sendiri, melainkan layar yang OS pasang sebagai pengganti.
- Apakah ada pengaturan agar "Tidak Merespons" tidak muncul selama pemrosesan?
- Memanggil DisableProcessWindowsGhosting menonaktifkan penggantian ke ghost window untuk proses itu. Namun ini hanya membuat hang kurang terlihat bagi pengguna. Jendela tetap tidak bereaksi terhadap input, dan dari sisi pengguna justru tampak membeku total: tidak bisa dipindah maupun ditutup. Penanggulangan yang benar bukan menekan tampilan, melainkan memindahkan pekerjaan berat ke thread worker agar thread UI tidak tersumbat, bahkan dalam satuan 0,1 detik, apalagi 5 detik. Perhatikan juga bahwa selama debugger terhubung OS tidak membuat ghost window, sehingga bisa tampak seolah "Tidak Merespons" tidak terjadi saat debug.
- Bolehkah menghindari Tidak Merespons dengan DoEvents (menggerakkan message pump secara manual)?
- Tidak disarankan. Memutar DoEvents atau loop PeekMessage di tengah pekerjaan berat memang bisa menghindari penilaian hang, tetapi di tengah pemrosesan event handler mana pun bisa masuk kembali: klik tombol sekali lagi, menutup jendela, timer, dan sebagainya. Handler lain yang menimpa data yang sedang diproses, atau menyentuh form yang seharusnya sudah ditutup lalu melempar exception, menghasilkan bug reentrancy yang bergantung timing dan sulit direproduksi — lebih merepotkan daripada Tidak Merespons itu sendiri. Cara yang benar adalah memindahkan pekerjaan itu sendiri ke thread worker dengan Task.Run dan sejenisnya, lalu membiarkan thread UI hanya menampilkan progres dan menerima pembatalan.
- Bagaimana memperbarui tampilan (kontrol) dari thread worker?
- Kontrol WinForms dan elemen WPF hanya boleh disentuh dari thread yang membuatnya (biasanya thread UI). Menyentuhnya langsung dari thread worker menimbulkan exception atau perilaku tidak terdefinisi. Di C#, cara termudah adalah async/await: kelanjutan setelah await kembali ke thread UI pemanggil, sehingga setelah await kontrol bisa diperbarui seperti biasa. Jika perlu beralih secara eksplisit, pakai Control.Invoke/BeginInvoke di WinForms dan Dispatcher.InvokeAsync di WPF. Di Win32 native, pola yang lazim adalah thread worker mengirim pesan penyelesaian buatan sendiri ke thread UI lewat PostMessage, lalu window procedure yang memperbarui tampilan.
- Bagaimana menelusuri penyebab aplikasi yang menjadi Tidak Merespons?
- Yang penting adalah mengambil state pada saat hang itu sendiri. Ambil full dump dulu dari tab Detail Task Manager lewat "Buat file dump", lalu di WinDbg lihat stack thread UI (thread yang memutar message loop). Apakah macet di I/O sinkron, menunggu jaringan, menunggu lock, atau menunggu thread lain lewat SendMessage akan terlihat langsung di stack. Untuk melihat proses yang masih hidup, daftar thread dan tampilan stack di Process Explorer berguna; untuk menelusuri sepanjang waktu, ambil trace dengan WPR. Artikel pengantar WinDbg, praktik Process Explorer, dan praktik WPR/WPA di situs ini juga bisa dipakai sebagai rujukan.
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.