Bagikan melalui


releaseHandleFailed MDA

Catatan

Artikel ini khusus untuk .NET Framework. Ini tidak berlaku untuk implementasi .NET yang lebih baru, termasuk .NET 6 dan versi yang lebih baru.

Asisten penelusuran kesalahan terkelola (MDA) releaseHandleFailed diaktifkan untuk memberi tahu pengembang ketika metode ReleaseHandle kelas yang berasal dari SafeHandle atau CriticalHandle mengembalikan false.

Gejala

Kebocoran sumber daya atau memori. Jika metode ReleaseHandle kelas yang berasal dari SafeHandle atau CriticalHandle gagal, sumber daya yang dienkapsulasi oleh kelas mungkin belum dirilis atau dibersihkan.

Penyebab

Pengguna harus menyediakan implementasi metode ReleaseHandle jika pengguna membuat kelas yang berasal dari SafeHandle atau CriticalHandle; dengan demikian, keadaan khusus untuk sumber daya individu. Namun, persyaratannya adalah sebagai berikut:

  • Jenis SafeHandle dan CriticalHandle mewakili pembungkus di sekitar sumber daya proses vital. Kebocoran memori akan membuat proses tidak dapat digunakan dari waktu ke waktu.

  • Metode ReleaseHandle tidak boleh gagal melakukan fungsinya. Setelah proses memperoleh sumber daya seperti itu, ReleaseHandle adalah satu-satunya cara untuk merilisnya. Oleh karena itu, kegagalan menyiratkan kebocoran sumber daya.

  • Setiap kegagalan yang terjadi selama eksekusi ReleaseHandle, menghambat rilis sumber daya, adalah bug dalam implementasi metode ReleaseHandle itu sendiri. Programmer bertanggung jawab untuk memastikan bahwa kontrak terpenuhi, bahkan jika kode tersebut memanggil kode yang ditulis oleh orang lain untuk melakukan fungsinya.

Resolusi

Kode yang menggunakan jenis SafeHandle (atau CriticalHandle) tertentu yang menaikkan pemberitahuan MDA harus ditinjau, mencari tempat di mana nilai handel mentah diekstraksi dari SafeHandle dan disalin di tempat lain. Ini adalah penyebab kegagalan yang biasa dalam implementasi SafeHandle atau CriticalHandle, karena penggunaan nilai handel mentah kemudian tidak lagi dilacak oleh runtime bahasa umum. Jika salinan handel mentah kemudian ditutup, itu dapat menyebabkan kemudian ReleaseHandle panggilan gagal karena penutupan dicoba pada handel yang sama, yang sekarang tidak valid.

Ada sejumlah cara di mana duplikasi penanganan yang salah dapat terjadi:

  • Cari panggilan ke metode DangerousGetHandle. Panggilan ke metode ini harus sangat jarang, dan setiap yang Anda temukan harus dikelilingi oleh panggilan ke metode DangerousAddRef dan DangerousRelease. Metode terakhir ini menentukan wilayah kode di mana nilai handel mentah dapat digunakan dengan aman. Di luar wilayah ini, atau jika jumlah referensi tidak pernah bertambah di tempat pertama, nilai handel dapat dibatalkan kapan saja dengan panggilan ke Dispose atau Close di utas lain. Setelah semua penggunaan DangerousGetHandle telah dilacak, Anda harus mengikuti jalur yang diperlukan handel mentah untuk memastikan tidak diserahkan ke beberapa komponen yang akhirnya akan memanggil CloseHandle atau metode asli tingkat rendah lainnya yang akan melepaskan handel.

  • Pastikan bahwa kode yang digunakan untuk menginisialisasi SafeHandle dengan nilai handel mentah yang valid memiliki handel. Jika Anda membentuk SafeHandle di sekitar handel yang tidak dimiliki kode Anda tanpa mengatur parameter ownsHandle ke false di konstruktor dasar, maka SafeHandle dan pemilik handel yang sebenarnya dapat mencoba menutup handel, yang mengarah ke kesalahan di ReleaseHandle jika SafeHandle kehilangan jejak.

  • Ketika SafeHandle disusun antara domain aplikasi, konfirmasikan SafeHandle bahwa derivasi yang digunakan telah ditandai sebagai dapat diserialisasikan. Dalam kasus yang jarang terjadi di mana kelas yang berasal SafeHandle telah dibuat dapat diserialisasikan, kelas harus mengimplementasikan antarmuka ISerializable atau menggunakan salah satu teknik lain untuk mengontrol proses serialisasi dan deserialisasi secara manual. Perilaku ini diperlukan karena tindakan serialisasi default adalah membuat klon bitwise dari nilai handel mentah yang diapit, yang mengakibatkan dua instans SafeHandle berpikir memiliki handel yang sama. Keduanya akan mencoba memanggil ReleaseHandle pada handel yang sama di beberapa titik. SafeHandle kedua yang melakukannya akan gagal. Tindakan yang benar saat menserialisasikan SafeHandle adalah memanggil fungsi DuplicateHandle atau fungsi serupa untuk jenis handel asli Anda untuk membuat salinan handel hukum yang berbeda. Jika jenis handel Anda tidak mendukung ini, maka jenis SafeHandle yang membungkusnya tidak dapat dibuat serialisasi.

  • Mungkin untuk melacak di mana handel ditutup lebih awal, yang menyebabkan kegagalan ketika metode ReleaseHandle akhirnya dipanggil, dengan menempatkan titik henti debugger pada rutinitas asli yang digunakan untuk merilis handel, misalnya fungsi CloseHandle. Ini mungkin tidak mungkin untuk skenario stres atau bahkan tes fungsional berukuran sedang karena lalu lintas padat rutinitas seperti yang sering ditangani. Ini dapat membantu melengkapi kode yang memanggil metode rilis asli, untuk menangkap identitas pemanggil, atau mungkin pelacakan tumpukan penuh, dan nilai handel yang dirilis. Nilai handel dapat dibandingkan dengan nilai yang dilaporkan oleh MDA ini.

  • Perhatikan bahwa beberapa jenis handel asli, seperti semua handel Win32 yang dapat dirilis melalui fungsi CloseHandle, berbagi namespace layanan handel yang sama. Rilis yang salah dari satu jenis handel dapat menyebabkan masalah dengan yang lain. Misalnya, secara tidak sengaja menutup handel peristiwa Win32 dua kali dapat menyebabkan handel file yang tampaknya tidak terkait ditutup sebelum waktunya. Ini terjadi ketika handel dirilis dan nilai handel menjadi tersedia untuk digunakan untuk melacak sumber daya lain, berpotensi dari jenis lain. Jika ini terjadi dan diikuti oleh rilis kedua yang salah, handel utas yang tidak terkait mungkin tidak valid.

Efek pada Runtime

MDA ini tidak berpengaruh pada runtime bahasa umum.

Output

Pesan yang menunjukkan bahwa SafeHandle atau CriticalHandle gagal melepaskan handel dengan benar. Contohnya:

"A SafeHandle or CriticalHandle of type 'MyBrokenSafeHandle'
failed to properly release the handle with value 0x0000BEEF. This
usually indicates that the handle was released incorrectly via
another means (such as extracting the handle using DangerousGetHandle
and closing it directly or building another SafeHandle around it."

Konfigurasi

<mdaConfig>
  <assistants>
    <releaseHandleFailed/>
  </assistants>
</mdaConfig>

Contoh

Berikut ini adalah contoh kode yang dapat mengaktifkan MDA releaseHandleFailed.

bool ReleaseHandle()
{
    // Calling the Win32 CloseHandle function to release the
    // native handle wrapped by this SafeHandle. This method returns
    // false on failure, but should only fail if the input is invalid
    // (which should not happen here). The method specifically must not
    // fail simply because of lack of resources or other transient
    // failures beyond the user’s control. That would make it unacceptable
    // to call CloseHandle as part of the implementation of this method.
    return CloseHandle(handle);
}

Lihat juga