답변:
리소스, 종종 메모리 또는 열린 파일 또는 파이프에 대한 추상 참조 값입니다.
적절하게 Windows에서 (및 일반적으로 컴퓨팅에서) 핸들은 API 사용자로부터 실제 메모리 주소를 숨겨서 시스템이 실제 메모리를 프로그램에 투명하게 재구성 할 수 있도록하는 추상화입니다. 핸들을 포인터로 해석하면 메모리가 잠기고 핸들을 놓으면 포인터가 무효화됩니다. 이 경우에는 포인터 테이블에 대한 인덱스로 생각하십시오. 시스템 API 호출에 인덱스를 사용하면 시스템이 테이블의 포인터를 마음대로 변경할 수 있습니다.
대안으로, API 기록기가 API의 사용자가 주소가 가리키는 것의 세부 사항으로부터 격리되도록 의도 할 때 핸들로서 실제 포인터가 제공 될 수있다; 이 경우 핸들이 가리키는 내용이 언제든지 변경 될 수 있음을 고려해야합니다 (API 버전에서 버전으로 또는 핸들을 리턴하는 API의 호출에서 호출까지). 따라서 핸들은 단순히 불투명 한 값으로 취급되어야합니다. API 에만 의미가 있습니다.
현대의 모든 운영 체제에서 소위 "실제 포인터"라하더라도 프로세스의 가상 메모리 공간에 불투명 한 핸들이있어 O / S가 프로세스 내의 포인터를 무효화하지 않고 메모리를 관리하고 재 배열 할 수 있습니다. .
A HANDLE
는 상황 별 고유 식별자입니다. 문맥에 따라, 나는 하나의 문맥에서 얻은 핸들이 HANDLE
s에서 작동하는 다른 모든 문맥에서 반드시 사용될 수는 없다는 것을 의미합니다 .
예를 들어, GetModuleHandle
현재로드 된 모듈에 고유 식별자를 반환합니다. 리턴 된 핸들은 모듈 핸들을 허용하는 다른 함수에서 사용될 수 있습니다. 다른 유형의 핸들이 필요한 함수에는 제공 할 수 없습니다. 예를 들어, 당신은에서 반환 된 핸들주지 수 GetModuleHandle
에를 HeapDestroy
하고 뭔가 분별을 할 것으로 기대합니다.
그 HANDLE
자체는 단지 필수적인 유형입니다. 일반적으로 반드시 그런 것은 아니지만 일부 기본 유형 또는 메모리 위치에 대한 포인터입니다. 예를 들어, HANDLE
by by GetModuleHandle
는 실제로 모듈의 기본 가상 메모리 주소에 대한 포인터입니다. 그러나 핸들이 포인터 여야한다는 규칙은 없습니다. 핸들은 단순한 정수일 수도 있습니다 (일부 Win32 API에서 배열의 인덱스로 사용할 수 있음).
HANDLE
내부 Win32 리소스에서 캡슐화 및 추상화를 제공하는 의도적으로 불투명 한 표현입니다. 이런 식으로 Win32 API는 어떤 방식 으로든 사용자 코드에 영향을 미치지 않으면 서 HANDLE 뒤의 기본 유형을 잠재적으로 변경할 수 있습니다 (적어도 아이디어입니다).
난 그냥 만들어하는 Win32 API를이 세 가지 내부 구현을 고려하고, 그 가정 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
. 이것은 몇 가지 결과를 초래합니다 :
Widget
구조체 를 정의하는 헤더 파일에 액세스 할 수 있어야합니다.Widget
구조체 의 내부 부분을 잠재적으로 수정할 수 있습니다이러한 결과는 바람직하지 않습니다.
두 번째 예는 just을 반환하여이 내부 세부 정보를 사용자 코드에서 숨 깁니다 void *
. 사용자 코드는 Widget
구조체 를 정의하는 헤더에 액세스 할 필요가 없습니다 .
세 번째 예제는 두 번째 예제와 정확히 동일하지만 대신 void *
a HANDLE
를 호출합니다 . 아마도 이것은 사용자 코드가 void *
요점을 정확히 파악하려고 시도하는 것을 방해 합니다.
왜이 문제를 겪고 있습니까? 이 동일한 API의 최신 버전에 대한 네 번째 예를 고려하십시오.
typedef void * HANDLE;
HANDLE GetWidget (std::string name)
{
NewImprovedWidget *w;
w = findImprovedWidget(name);
return reinterpret_cast<HANDLE>(w);
}
함수의 인터페이스는 위의 세 번째 예와 동일합니다. 즉, "장면"구현이 NewImprovedWidget
구조체를 대신 사용하도록 변경 되었음에도 불구하고 사용자 코드는 새로운 버전의 API를 변경없이 계속 사용할 수 있습니다 .
이 예제의 핸들은 실제로 새롭고 친숙한 이름입니다. for Win32 API의 void *
정확한 이름입니다 HANDLE
( MSDN 참조 ). 사용자 코드와 Win32 라이브러리의 내부 표현 사이에 불투명 한 벽을 제공하여 Windows 버전간에 Win32 API를 사용하는 코드의 이식성을 높입니다.
handle
대신에 사용하는 주된 이유 void *
는 사용자 코드가 void *가 가리키는 것을 정확하게 파악하지 못하도록 하는 것입니다 . 나 맞아?
Win32 프로그래밍의 HANDLE은 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 함수는 Window HANDLE의 상태를 수정합니다.
자세한 내용은 핸들 및 데이터 형식 을 참조하십시오.
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는 원래 값 1을 인쇄합니다.
이제 foo를 다음과 같이 업데이트하면 :
void foo(Object * bar)
{
LargeObj lo = new LargeObj();
bar->val++;
}
printf가 2의 갱신 된 값을 인쇄 할 가능성이 있습니다. 그러나 foo가 어떤 형태의 메모리 손상이나 예외를 야기 할 가능성도 있습니다.
그 이유는 포인터를 사용하여 2 메그의 메모리를 할당하는 함수에 obj를 전달하는 동안 OS가 obj의 위치를 업데이트하는 주위로 메모리를 이동할 수 있습니다. 포인터를 값으로 전달 했으므로 obj가 이동하면 OS는 포인터를 업데이트하지만 함수의 복사본은 업데이트하지 않아 잠재적으로 문제가 발생할 수 있습니다.
foo에 대한 최종 업데이트 :
void foo(Object **bar){
LargeObj lo = LargeObj();
Object * b = &bar;
b->val++;
}
항상 업데이트 된 값을 인쇄합니다.
컴파일러가 포인터에 메모리를 할당 할 때 포인터를 움직일 수없는 것으로 표시하므로 함수에 전달 된 값에 할당 된 큰 객체로 인한 메모리 재 셔플 링은 메모리의 최종 위치를 찾기 위해 올바른 주소를 가리 킵니다. 최신 정보.
특정 유형의 핸들 (hWnd, FILE 등)은 도메인별로 다르며 메모리 손상을 방지하기 위해 특정 유형의 구조를 가리 킵니다.
핸들은 데이터베이스에서 레코드의 기본 키 값과 같습니다.
편집 1 : 왜 downvote, 기본 키가 데이터베이스 레코드를 고유하게 식별하고 Windows 시스템의 핸들이 창, 열린 파일 등을 고유하게 식별하는 이유는 무엇입니까?
Windows의 창을 설명하는 구조체로 생각하십시오. 이 구조체는 Windows의 내부 부분이므로 자세한 내용을 알 필요가 없습니다. 대신 Windows는 해당 구조체의 구조체를 가리키는 포인터에 대한 typedef를 제공합니다. 그것이 창을 잡을 수있는 "손잡이"입니다.,