Apa itu Windows Handle?

153

Apa itu "Menangani" ketika membahas sumber daya di Windows? Bagaimana mereka bekerja?

Al C
sumber

Jawaban:

167

Ini nilai referensi abstrak ke sumber daya, sering memori atau file terbuka, atau pipa.

Benar , di Windows, (dan umumnya dalam komputasi) pegangan adalah abstraksi yang menyembunyikan alamat memori nyata dari pengguna API, yang memungkinkan sistem untuk mengatur kembali memori fisik secara transparan ke program. Memecahkan pegangan ke penunjuk mengunci memori, dan melepaskan pegangan membatalkan penunjuk. Dalam hal ini anggap itu sebagai indeks ke dalam tabel petunjuk ... Anda menggunakan indeks untuk panggilan sistem API, dan sistem dapat mengubah pointer di tabel sesuka hati.

Atau, penunjuk nyata dapat diberikan sebagai pegangan ketika penulis API bermaksud agar pengguna API diisolasi dari spesifikasi alamat yang dikembalikan; dalam hal ini harus dipertimbangkan bahwa apa yang ditunjukkan oleh pegangan dapat berubah kapan saja (dari versi API ke versi atau bahkan dari panggilan ke panggilan API yang mengembalikan gagang) - karena itu gagang harus diperlakukan hanya sebagai nilai buram hanya berarti bagi API.

Saya harus menambahkan bahwa dalam sistem operasi modern apa pun, bahkan apa yang disebut "pointer nyata" masih merupakan pegangan buram ke dalam ruang memori virtual dari proses, yang memungkinkan O / S untuk mengelola dan mengatur ulang memori tanpa membatalkan pointer dalam proses. .

Lawrence Dol
sumber
4
Saya sangat menghargai respon cepat. Sayangnya, aku pikir aku masih terlalu banyak dari pemula untuk memahami itu :-(
Al C
4
Apakah jawaban saya yang diperluas memberi titik terang?
Lawrence Dol
100

A HANDLEadalah pengidentifikasi unik khusus konteks. Secara spesifik konteks, maksud saya bahwa pegangan yang diperoleh dari satu konteks tidak selalu dapat digunakan dalam konteks aribtrary lain yang juga berfungsi pada HANDLEs.

Misalnya, GetModuleHandlemengembalikan pengidentifikasi unik ke modul yang saat ini dimuat. Pegangan yang dikembalikan dapat digunakan dalam fungsi lain yang menerima pegangan modul. Itu tidak dapat diberikan ke fungsi yang membutuhkan jenis pegangan lainnya. Misalnya, Anda tidak dapat memberikan pegangan kembali dari GetModuleHandleke HeapDestroydan mengharapkannya melakukan sesuatu yang masuk akal.

Itu HANDLEsendiri hanyalah tipe integral. Biasanya, tetapi tidak harus, itu adalah penunjuk ke beberapa tipe atau lokasi memori yang mendasarinya. Sebagai contoh, yang HANDLEdikembalikan oleh GetModuleHandlesebenarnya adalah pointer ke alamat memori virtual dasar modul. Tetapi tidak ada aturan yang menyatakan bahwa pegangan harus pointer. Pegangan juga bisa berupa integer sederhana (yang mungkin dapat digunakan oleh beberapa Win32 API sebagai indeks ke dalam array).

HANDLES adalah representasi sengaja buram yang menyediakan enkapsulasi dan abstraksi dari sumber daya Win32 internal. Dengan cara ini, Win32 APIs berpotensi mengubah tipe yang mendasari di belakang HANDLE, tanpa memengaruhi kode pengguna dengan cara apa pun (setidaknya itulah idenya).

Pertimbangkan tiga implementasi internal berbeda dari Win32 API yang baru saja saya buat, dan anggap itu Widgetadalah a struct.

Widget * GetWidget (std::string name)
{
    Widget *w;

    w = findWidget(name);

    return w;
}
void * GetWidget (std::string name)
{
    Widget *w;

    w = findWidget(name);

    return reinterpret_cast<void *>(w);
}
typedef void * HANDLE;

HANDLE GetWidget (std::string name)
{
    Widget *w;

    w = findWidget(name);

    return reinterpret_cast<HANDLE>(w);
}

Contoh pertama memperlihatkan detail internal tentang API: memungkinkan kode pengguna untuk mengetahui yang GetWidgetmengembalikan pointer ke struct Widget. Ini memiliki beberapa konsekuensi:

  • kode pengguna harus memiliki akses ke file header yang mendefinisikan Widgetstruct
  • kode pengguna berpotensi memodifikasi bagian internal dari Widgetstruct yang dikembalikan

Kedua konsekuensi ini mungkin tidak diinginkan.

Contoh kedua menyembunyikan detail internal ini dari kode pengguna, dengan mengembalikan adil void *. Kode pengguna tidak perlu akses ke tajuk yang mendefinisikanWidget struct.

Contoh ketiga persis sama dengan yang kedua, tetapi kita sebut saja void * a HANDLEsebagai gantinya. Mungkin ini mencegah kode pengguna untuk mencoba mencari tahu apa sebenarnya void *poinnya.

Mengapa harus melalui masalah ini? Pertimbangkan contoh keempat ini dari versi yang lebih baru dari API yang sama ini:

typedef void * HANDLE;

HANDLE GetWidget (std::string name)
{
    NewImprovedWidget *w;

    w = findImprovedWidget(name);

    return reinterpret_cast<HANDLE>(w);
}

Perhatikan bahwa antarmuka fungsi identik dengan contoh ketiga di atas. Ini berarti bahwa kode pengguna dapat terus menggunakan versi baru API ini, tanpa perubahan apa pun, meskipun implementasi "di belakang layar" telah berubah untuk menggunakanNewImprovedWidget struct sebagai gantinya.

Pegangan dalam contoh ini benar-benar hanya nama baru, mungkin lebih ramah, untuk void *, yang persis seperti apa yang HANDLEada di Win32 API (lihat di MSDN ). Ini memberikan dinding buram antara kode pengguna dan representasi internal perpustakaan Win32 yang meningkatkan portabilitas, antara versi Windows, kode yang menggunakan Win32 API.

Dan Moulding
sumber
5
Disengaja atau tidak, Anda benar - konsepnya jelas buram (setidaknya bagi saya :-)
Al C
5
Saya telah memperluas jawaban asli saya dengan beberapa contoh nyata. Semoga ini akan membuat konsep sedikit lebih transparan.
Dan Moulding
2
Ekspansi yang sangat membantu ... Terima kasih!
Al C
4
Ini harus menjadi salah satu respons paling bersih, langsung, dan paling baik ditulis untuk setiap pertanyaan yang pernah saya lihat. Terima kasih dengan tulus telah meluangkan waktu untuk menulisnya!
Andrew
@DanMoulding: Jadi alasan utama untuk menggunakan handlealih-alih void *adalah mencegah kode pengguna untuk mencoba mencari tahu persis apa yang ditunjukkan oleh void * . Apakah saya benar?
Lion Lai
37

HANDLE dalam pemrograman Win32 adalah token yang mewakili sumber daya yang dikelola oleh kernel Windows. Pegangan bisa ke jendela, file, dll.

Pegangan hanyalah cara mengidentifikasi sumber daya partikulat yang ingin Anda gunakan dengan menggunakan Win32 API.

Jadi misalnya, jika Anda ingin membuat Window, dan menunjukkannya di layar Anda bisa melakukan hal berikut:

// Create the window
HWND hwnd = CreateWindow(...); 
if (!hwnd)
   return; // hwnd not created

// Show the window.
ShowWindow(hwnd, SW_SHOW);

Dalam contoh di atas HWND berarti "pegangan ke jendela".

Jika Anda terbiasa dengan bahasa berorientasi objek, Anda dapat menganggap HANDLE sebagai instance dari kelas tanpa metode yang statusnya hanya dapat dimodifikasi oleh fungsi lain. Dalam hal ini ShowWindow fungsi mengubah status dari PENANGANAN Jendela.

Lihat Pegangan dan Jenis Data untuk informasi lebih lanjut.

Nick Haddad
sumber
Objek yang dirujuk melalui HANDLEADT dikelola oleh kernel. Jenis pegangan lain yang Anda beri nama ( HWND, dll.) Di sisi lain, adalah objek USER. Itu tidak dikelola oleh kernel Windows.
IInspectable
1
@Ispektabel menebak itu dikelola oleh hal-hal User32.dll?
the_endian
8

Pegangan adalah pengidentifikasi unik untuk objek yang dikelola oleh Windows. Ini seperti sebuah pointer , tetapi bukan sebuah pointer di dalam arti bahwa itu bukan alamat yang dapat ditinjau ulang oleh kode pengguna untuk mendapatkan akses ke beberapa data. Sebaliknya pegangan harus diteruskan ke serangkaian fungsi yang dapat melakukan tindakan pada objek yang diidentifikasi pegangan.

sharptooth
sumber
5

Jadi pada level paling dasar, HANDLE dalam bentuk apa pun adalah pointer ke pointer atau

#define HANDLE void **

Sekarang mengapa Anda ingin menggunakannya

Mari kita siapkan:

class Object{
   int Value;
}

class LargeObj{

   char * val;
   LargeObj()
   {
      val = malloc(2048 * 1000);
   }

}

void foo(Object bar){
    LargeObj lo = new LargeObj();
    bar.Value++;
}

void main()
{
   Object obj = new Object();
   obj.val = 1;
   foo(obj);
   printf("%d", obj.val);
}

Jadi karena obj diteruskan oleh nilai (membuat salinan dan memberikannya ke fungsi) untuk foo, printf akan mencetak nilai asli 1.

Sekarang jika kami memperbarui foo ke:

void foo(Object * bar)
{
    LargeObj lo = new LargeObj();
    bar->val++;
}

Ada kemungkinan bahwa printf akan mencetak nilai yang diperbarui dari 2. Tetapi ada juga kemungkinan bahwa foo akan menyebabkan beberapa bentuk kerusakan atau pengecualian memori.

Alasannya adalah ini sementara Anda sekarang menggunakan pointer untuk meneruskan obj ke fungsi Anda juga mengalokasikan 2 Megs memori, ini dapat menyebabkan OS untuk memindahkan memori sekitar memperbarui lokasi obj. Karena Anda telah melewati pointer dengan nilai, jika obj dipindahkan maka OS memperbarui pointer tetapi tidak menyalin dalam fungsi dan berpotensi menyebabkan masalah.

Pembaruan terakhir untuk:

void foo(Object **bar){
    LargeObj lo = LargeObj();
    Object * b = &bar;
    b->val++;
}

Ini akan selalu mencetak nilai yang diperbarui.

Lihat, ketika kompiler mengalokasikan memori untuk pointer itu menandainya sebagai tidak bergerak, sehingga setiap pengocokan kembali memori yang disebabkan oleh objek besar yang dialokasikan nilai yang diteruskan ke fungsi akan menunjuk ke alamat yang benar untuk mengetahui lokasi akhir dalam memori untuk memperbarui.

Setiap jenis HANDLE tertentu (hWnd, FILE, dll.) Adalah spesifik domain dan arahkan ke jenis struktur tertentu untuk melindungi dari kerusakan memori.


sumber
1
Ini adalah alasan yang salah; subsistem alokasi memori C tidak bisa hanya membatalkan pointer sesuka hati. Kalau tidak, tidak ada program C atau C ++ yang bisa dibuktikan benar; lebih buruk lagi, setiap program dengan kompleksitas yang cukup akan terbukti salah menurut definisi. Selain itu, tipuan ganda tidak membantu jika memori yang diarahkan langsung dipindahkan di bawah program kecuali jika pointer itu sendiri sebenarnya merupakan abstraksi dari memori nyata - yang akan membuatnya menjadi pegangan .
Lawrence Dol
1
Sistem operasi Macintosh (dalam versi hingga 9 atau 8) melakukan persis seperti di atas. Jika Anda mengalokasikan beberapa objek sistem, Anda akan sering mendapatkannya, membiarkan OS bebas untuk memindahkan objek. Dengan ukuran memori yang terbatas dari Mac pertama itu agak penting.
Rhialto mendukung Monica
5

Pegangan seperti nilai kunci utama dari catatan dalam database.

sunting 1: baik, mengapa downvote, kunci primer secara unik mengidentifikasi catatan basis data, dan pegangan dalam sistem Windows secara unik mengidentifikasi jendela, file yang dibuka, dll. Itulah yang saya katakan.

Edwin Yip
sumber
1
Saya tidak membayangkan Anda dapat menyatakan bahwa pegangannya unik. Mungkin unik untuk setiap Stasiun Windows pengguna, tetapi tidak dijamin unik jika ada banyak pengguna yang mengakses sistem yang sama pada saat yang sama. Yaitu, banyak pengguna bisa mendapatkan kembali nilai pegangan yang identik secara numerik, tetapi dalam konteks pengguna Windows Station mereka memetakan ke hal-hal yang berbeda ...
Nick
2
@nick Ini unik dalam konteks yang diberikan. Kunci utama tidak akan menjadi unik di antara tabel yang berbeda ...
Benny Mackney
2

Pikirkan jendela di Windows sebagai struktur yang menjelaskannya. Struct ini merupakan bagian internal Windows dan Anda tidak perlu tahu detailnya. Sebaliknya, Windows menyediakan typedef untuk pointer ke struct untuk struct itu. Itu adalah "pegangan" dimana Anda dapat memegang di jendela.,

Charlie Martin
sumber
Benar, tetapi selalu patut diingat bahwa pegangan biasanya bukan alamat memori dan satu kode pengguna tidak boleh merujuknya.
sharptooth