回答:
これは、リソース、多くの場合はメモリや開いているファイル、またはパイプへの抽象的な参照値です。
正しくは、Windowsでは(そして一般にコンピューティングでは)ハンドルは、APIから実際のメモリアドレスを隠して、システムがプログラムに対して透過的に物理メモリを再編成できるようにする抽象化です。ハンドルをポインタに解決するとメモリがロックされ、ハンドルを解放するとポインタが無効になります。この場合、それをポインタのテーブルへのインデックスと考えてください...システムAPI呼び出しにインデックスを使用すると、システムはテーブル内のポインタを自由に変更できます。
あるいは、APIの作成者が、APIのユーザーが、返されたアドレスが指すものの詳細から隔離されることを意図している場合、実際のポインターをハンドルとして与えることができます。この場合、ハンドルが指すものはいつでも変更される可能性があることを考慮する必要があります(APIバージョンからバージョン、またはハンドルを返すAPIの呼び出しから呼び出しまで)-したがって、ハンドルは単に不透明な値として扱われる必要がありますAPIに対してのみ意味があります。
さらに、現代のオペレーティングシステムでは、いわゆる「実際のポインタ」でさえ、プロセスの仮想メモリ空間への不透明なハンドルであるため、O / Sはプロセス内のポインタを無効にすることなくメモリを管理および再配置できます。 。
A HANDLE
は、コンテキスト固有の一意の識別子です。コンテキスト固有とは、あるコンテキストから取得したハンドルを、HANDLE
s でも機能する他の任意のコンテキストで使用できるとは限らないことを意味します。
たとえばGetModuleHandle
、現在ロードされているモジュールに一意の識別子を返します。返されたハンドルは、モジュールハンドルを受け入れる他の関数で使用できます。他のタイプのハンドルを必要とする関数に与えることはできません。たとえば、GetModuleHandle
to から返されるハンドルを与えて、HeapDestroy
それが何か賢明なことを期待することはできません。
HANDLE
それ自体は単なる整数型です。通常、これは必須ではありませんが、基になる型またはメモリの場所へのポインタです。たとえば、によってHANDLE
返されるのGetModuleHandle
は、実際にはモジュールのベース仮想メモリアドレスへのポインタです。ただし、ハンドルはポインターでなければならないという規則はありません。ハンドルを単純な整数にすることもできます(Win32 APIで配列へのインデックスとして使用される可能性があります)。
HANDLE
sは、内部の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のバージョン間の移植性を高めます。
handle
代わりに使用する主な理由void *
は、ユーザーコードがvoid *が指すものを正確に理解しようとするのを妨げることです。私は正しいですか?
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関数はウィンドウハンドルの状態を変更します。
詳細については、ハンドルとデータ型を参照してください。
HANDLE
ADTを介して参照されるオブジェクトは、カーネルによって管理されます。HWND
一方、名前を付ける他のハンドルタイプ(など)は、USERオブジェクトです。これらはWindowsカーネルでは管理されません。
したがって、最も基本的なレベルでは、あらゆる種類のハンドルは、ポインタへのポインタまたは
#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:まあ、なぜダウン投票、主キーはデータベースレコードを一意に識別し、Windowsシステムのハンドルはウィンドウや開いているファイルなどを一意に識別します。それが私が言っていることです。
Windowsのウィンドウは、それを記述する構造体であると考えてください。この構造体はWindowsの内部部分であり、その詳細を知る必要はありません。代わりに、Windowsはその構造体の構造体へのポインターのtypedefを提供します。これが、ウィンドウをつかむ「ハンドル」です。