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현재로드 된 모듈에 고유 식별자를 반환합니다. 리턴 된 핸들은 모듈 핸들을 허용하는 다른 함수에서 사용될 수 있습니다. 다른 유형의 핸들이 필요한 함수에는 제공 할 수 없습니다. 예를 들어, 당신은에서 반환 된 핸들주지 수 GetModuleHandle에를 HeapDestroy하고 뭔가 분별을 할 것으로 기대합니다.

HANDLE자체는 단지 필수적인 유형입니다. 일반적으로 반드시 그런 것은 아니지만 일부 기본 유형 또는 메모리 위치에 대한 포인터입니다. 예를 들어, HANDLEby 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를 사용하는 코드의 이식성을 높입니다.


5
의도적이든 아니든, 당신 말이 맞습니다. 개념은 확실히 불투명합니다 (적어도 나에게는 :-)
Al C

5
구체적인 예를 들어 원래의 대답을 확장했습니다. 바라건대 이것은 개념을 조금 더 투명하게 만들 것입니다.
Dan Molding

2
매우 유용한 확장 ... 감사합니다!
Al C

4
이것은 내가 본 한 질문에 대한 가장 깨끗하고 직접적이며 가장 잘 작성된 답변 중 하나 여야합니다. 시간을내어 작성해 주셔서 감사합니다.
Andrew

@DanMoulding : handle대신에 사용하는 주된 이유 void *사용자 코드가 void *가 가리키는 것을 정확하게 파악하지 못하도록 하는 것입니다 . 나 맞아?
사자 라이

37

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의 상태를 수정합니다.

자세한 내용은 핸들 및 데이터 형식 을 참조하십시오.


HANDLEADT를 통해 참조 된 개체 는 커널에 의해 관리됩니다. 반면 다른 이름 ( HWND, 등)은 USER 객체입니다. 그것들은 Windows 커널에 의해 관리되지 않습니다.
IInspectable

1
@IInspectable 추측은 User32.dll 것들에 의해 관리됩니까?
the_endian

8

핸들은 Windows에서 관리하는 개체의 고유 식별자입니다. 그것은 포인터 와 같지만 일부 데이터에 액세스하기 위해 사용자 코드가 역 참조 할 수있는 주소가 아니라는 점에서 포인터 는 아닙니다. 대신 핸들은 핸들이 식별하는 객체에 대해 작업을 수행 할 수있는 일련의 함수로 전달됩니다.


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는 원래 값 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
이것은 잘못된 추론이다. C 메모리 할당 서브 시스템은 포인터를 마음대로 무효화 할 수 없습니다. 그렇지 않으면 C 또는 C ++ 프로그램이 정확하지 않을 수도 있습니다. 충분히 복잡한 프로그램은 정의상 명백히 부정확 할 것이다. 그것을 만들 것이다 - 게다가, 이중 간접 포인터가 실제로 자체가 실제 메모리에서 추상화가 아닌 메모리에 직접 프로그램 아래의 주위에 이동 지적 아닌 경우 도움 않는 핸들을 .
Lawrence Dol

1
Macintosh 운영 체제 (최대 9 또는 8 버전)는 위와 동일합니다. 일부 시스템 객체를 할당 한 경우, 종종 객체를 처리하여 OS가 객체를 자유롭게 이동할 수 있습니다. 다소 중요한 최초의 Mac의 메모리 크기가 제한되었습니다.
Rhialto는 Monica

5

핸들은 데이터베이스에서 레코드의 기본 키 값과 같습니다.

편집 1 : 왜 downvote, 기본 키가 데이터베이스 레코드를 고유하게 식별하고 Windows 시스템의 핸들이 창, 열린 파일 등을 고유하게 식별하는 이유는 무엇입니까?


1
핸들이 독창적이라고 주장 할 수 있다고 생각하지 않습니다. 사용자의 Windows Station마다 고유 할 수 있지만, 동시에 동일한 시스템에 여러 사용자가 액세스하는 경우에는 고유하지 않을 수 있습니다. 즉, 여러 사용자가 수치 적으로 동일한 핸들 값을 다시 얻을 수 있지만 사용자의 Windows Station과 관련하여 다른 것으로 매핑됩니다.
Nick

2
@nick 주어진 맥락에서 독특합니다. 기본 키는 다른 테이블 사이에서 고유하지 않을 것입니다 ...
Benny Mackney

2

Windows의 창을 설명하는 구조체로 생각하십시오. 이 구조체는 Windows의 내부 부분이므로 자세한 내용을 알 필요가 없습니다. 대신 Windows는 해당 구조체의 구조체를 가리키는 포인터에 대한 typedef를 제공합니다. 그것이 창을 잡을 수있는 "손잡이"입니다.,


그러나 핸들은 일반적으로 메모리 주소가 아니며 하나의 사용자 코드는이를 참조하지 않아야한다는 것을 항상 기억해야합니다.
sharptooth
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.