Windowsハンドルとは何ですか?


153

Windowsでリソースについて議論するときの「ハンドル」とは何ですか?それらはどのように機能しますか?

回答:


167

これは、リソース、多くの場合はメモリや開いているファイル、またはパイプへの抽象的な参照値です。

正しくは、Windowsでは(そして一般にコンピューティングでは)ハンドルは、APIから実際のメモリアドレスを隠して、システムがプログラムに対して透過的に物理メモリを再編成できるようにする抽象化です。ハンドルをポインタに解決するとメモリがロックされ、ハンドルを解放するとポインタが無効になります。この場合、それをポインタのテーブルへのインデックスと考えてください...システムAPI呼び出しにインデックスを使用すると、システムはテーブル内のポインタを自由に変更できます。

あるいは、APIの作成者が、APIのユーザーが、返されたアドレスが指すものの詳細から隔離されることを意図している場合、実際のポインターをハンドルとして与えることができます。この場合、ハンドルが指すものはいつでも変更される可能性があることを考慮する必要があります(APIバージョンからバージョン、またはハンドルを返すAPIの呼び出しから呼び出しまで)-したがって、ハンドルは単に不透明な値として扱われる必要がありますAPIに対してのみ意味があります。

さらに、現代のオペレーティングシステムでは、いわゆる「実際のポインタ」でさえ、プロセスの仮想メモリ空​​間への不透明なハンドルであるため、O / Sはプロセス内のポインタを無効にすることなくメモリを管理および再配置できます。 。


4
迅速な対応に本当に感謝しています。残念ながら、私はまだそれを完全に理解するには初心者ではありません:-(
Al C

4
私の拡張された答えは何か光を放ちますか?
Lawrence Dol、

100

A HANDLEは、コンテキスト固有の一意の識別子です。コンテキスト固有とは、あるコンテキストから取得したハンドルを、HANDLEs でも機能する他の任意のコンテキストで使用できるとは限らないことを意味します。

たとえばGetModuleHandle、現在ロードされているモジュールに一意の識別子を返します。返されたハンドルは、モジュールハンドルを受け入れる他の関数で使用できます。他のタイプのハンドルを必要とする関数に与えることはできません。たとえば、GetModuleHandleto から返されるハンドルを与えて、HeapDestroyそれが何か賢明なことを期待することはできません。

HANDLEそれ自体は単なる整数型です。通常、これは必須ではありませんが、基になる型またはメモリの場所へのポインタです。たとえば、によってHANDLE返されるのGetModuleHandleは、実際にはモジュールのベース仮想メモリアドレスへのポインタです。ただし、ハンドルはポインターでなければならないという規則はありません。ハンドルを単純な整数にすることもできます(Win32 APIで配列へのインデックスとして使用される可能性があります)。

HANDLEsは、内部のWin32リソースからのカプセル化と抽象化を提供する、意図的に不透明な表現です。このように、Win32 APIは、ユーザーコードに影響を与えることなく、ハンドルの背後にある基になる型を変更する可能性があります(少なくともそれは考えです)。

先ほど作成したWin32 APIのこれら3つの異なる内部実装を検討し、それWidgetがであると仮定し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);
}

最初の例は、APIに関する内部の詳細を公開します。これにより、ユーザーコードGetWidgetは、へのポインターを返すことを知ることができますstruct Widget。これには2つの影響があります。

  • ユーザーコードは、Widget構造体を定義するヘッダーファイルにアクセスできる必要があります
  • ユーザーコードは、返されたWidget構造体の内部部分を変更する可能性があります

これらの結果はどちらも望ましくない場合があります。

2番目の例では、justを返すことにより、この内部の詳細をユーザーコードから隠していますvoid *。ユーザーコードは、Widget構造体を定義するヘッダーにアクセスする必要はありません。

3番目の例は2番目の例とまったく同じですがvoid *HANDLE代わりに単にa と呼びます。おそらく、これはユーザーコードがvoid *ポイントを正確に理解しようとするのを妨げます。

なぜこのトラブルを経験するのですか?この同じAPIの新しいバージョンの次の4番目の例を考えてみます。

typedef void * HANDLE;

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

    w = findImprovedWidget(name);

    return reinterpret_cast<HANDLE>(w);
}

関数のインターフェースは上記の3番目の例と同じです。つまり、「裏側」の実装がNewImprovedWidget構造体を使用するように変更されていても、ユーザーコードは変更なしにこの新しいバージョンのAPIを引き続き使用できます。

これらの例のハンドルは、本当に新しい、おそらくよりわかりやすい名前ですvoid *。これはHANDLE、Win32 APIのa とまったく同じです(MSDNで調べてください)。これは、ユーザーコードとWin32ライブラリの内部表現の間に不透明な壁を提供し、Win32 APIを使用するコードのWindowsのバージョン間の移植性を高めます。


5
意図的かどうかにかかわらず、あなたの言う通りです。コンセプトは確かに不透明です(少なくとも私にとっては:-)
Al C

5
いくつかの具体例を挙げて、元の回答を拡張しました。うまくいけば、これによりコンセプトがもう少し透明になります。
ダン成形

2
非常に役立つ拡張...ありがとう!
Al C

4
これは、私がしばらく見てきたあらゆる質問に対する最もクリーンで直接的な、そして最もよく書かれた応答の1つでなければなりません。お時間を割いていただき誠にありがとうございます。
Andrew

@DanMoulding:したがって、handle代わりに使用する主な理由void *、ユーザーコードがvoid *が指すものを正確に理解しようとするのを妨げることです。私は正しいですか?
Lion Lai

37

Win32プログラミングのハンドルは、Windowsカーネルによって管理されるリソースを表すトークンです。ハンドルはウィンドウ、ファイルなどに対するものです。

ハンドルは、Win32 APIを使用して操作する微粒子リソースを識別する方法です。

たとえば、ウィンドウを作成して画面に表示したい場合は、次のようにします。

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

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

上記の例では、HWNDは「ウィンドウのハンドル」を意味します。

オブジェクト指向言語に慣れている場合は、HANDLEを、他の関数によってのみ状態を変更できるメソッドを持たないクラスのインスタンスと考えることができます。この場合、ShowWindow関数はウィンドウハンドルの状態を変更します。

詳細については、ハンドルとデータ型を参照してください。


HANDLEADTを介して参照されるオブジェクトは、カーネルによって管理されます。HWND一方、名前を付ける他のハンドルタイプ(など)は、USERオブジェクトです。これらはWindowsカーネルでは管理されません。
IInspectable 2014

1
@IInspectableは、User32.dllなどによって管理されていると思いますか?
the_endian 2016

8

ハンドルは、Windowsによって管理されるオブジェクトの一意の識別子です。それはだポインタのように、しかしないポインタ、それはいくつかのデータにアクセスするためにユーザーコードによって逆参照することができ、アドレスではないことをSENCEインチ 代わりに、ハンドルは、ハンドルが識別するオブジェクトに対してアクションを実行できる一連の関数に渡されます。


5

したがって、最も基本的なレベルでは、あらゆる種類のハンドルは、ポインタへのポインタまたは

#define HANDLE void **

なぜそれを使いたいのかについて

設定してみましょう:

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);
}

したがって、objは値によって(コピーを作成して関数に渡す)fooに渡されたため、printfは元の値1を出力します。

fooを次のように更新すると、

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

printfが更新された値2を出力する可能性がありますが、fooが何らかの形式のメモリ破損または例外を引き起こす可能性もあります。

その理由は、ポインタを使用してobjを関数に渡し、メモリも2M割り当てているためです。これにより、OSがobjの場所を更新するためにメモリを移動する可能性があります。値でポインターを渡したため、objが移動された場合、OSはポインターを更新しますが、関数のコピーは更新せず、問題を引き起こす可能性があります。

fooの最終更新:

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

これにより、常に更新された値が出力されます。

コンパイラがポインタにメモリを割り当てると、それらは動かせないものとしてマークされます。そのため、ラージオブジェクトが割り当てられることによって引き起こされるメモリの再シャッフルは、関数に渡される値が正しいアドレスをポイントし、メモリ内の最終的な場所を見つけます。更新。

HANDLEの特定のタイプ(hWnd、FILEなど)はドメイン固有であり、メモリの破損を防ぐために特定のタイプの構造を指します。


1
これは欠陥のある推論です。Cメモリ割り当てサブシステムは、ポインタを自由に無効にすることはできません。そうしないと、CまたはC ++プログラムが証明できるほど正確である可能性はありません。さらに悪いことに、十分に複雑なプログラムは明らかに定義上正しくありません。その上、ポインタが実際に実際のメモリからの抽象化でない限り、直接指定されたメモリがプログラムの下を移動する場合、二重の間接参照は役に立ちません-これはハンドルになります。
Lawrence Dol、

1
Macintoshオペレーティングシステム(バージョン9または8まで)は、まさに上記を実行しました。何らかのシステムオブジェクトを割り当てた場合、そのオブジェクトへのハンドルを取得し、OSがオブジェクトを自由に移動できるようにすることがよくあります。かなり重要だった最初のMacの限られたメモリサイズ。
Rhialtoは、2015

5

ハンドルは、データベース内のレコードの主キー値のようなものです。

編集1:まあ、なぜダウン投票、主キーはデータベースレコードを一意に識別し、Windowsシステムのハンドルはウィンドウや開いているファイルなどを一意に識別します。それが私が言っていることです。


1
ハンドルが一意であると断言できるとは思いません。ユーザーのWindowsステーションごとに一意である場合がありますが、複数のユーザーが同じシステムに同時にアクセスしている場合は、一意であるとは限りません。つまり、複数のユーザーが数値的に同一のハンドル値を返す可能性がありますが、ユーザーのWindowsステーションのコンテキストでは、それらは異なるものにマップされます...
Nick

2
@nick特定のコンテキストで一意です。主キーは、異なるテーブル間で一意になることもありません...
ベニー・マックニー18/07/18

2

Windowsのウィンドウは、それを記述する構造体であると考えてください。この構造体はWindowsの内部部分であり、その詳細を知る必要はありません。代わりに、Windowsはその構造体の構造体へのポインターのtypedefを提供します。これが、ウィンドウをつかむ「ハンドル」です。


確かに、しかし、ハンドルは通常メモリアドレスではなく、1人のユーザーコードがそれを逆参照してはならないことを常に覚えておく価値があります。
シャープトゥース2009年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.