Respostas:
É um valor de referência abstrato para um recurso, geralmente memória ou arquivo aberto ou canal.
Adequadamente , no Windows (e geralmente na computação), um identificador é uma abstração que oculta um endereço de memória real do usuário da API, permitindo que o sistema reorganize a memória física de forma transparente para o programa. A resolução de um identificador em um ponteiro bloqueia a memória e a liberação do identificador invalida o ponteiro. Nesse caso, pense nele como um índice em uma tabela de ponteiros ... você usa o índice para as chamadas à API do sistema, e o sistema pode alterar o ponteiro na tabela à vontade.
Como alternativa, um ponteiro real pode ser fornecido como identificador quando o gravador da API pretende que o usuário da API seja isolado das especificidades para as quais o endereço retornado aponta; nesse caso, deve-se considerar que o que o identificador aponta pode mudar a qualquer momento (de versão da API para versão ou mesmo de chamada para chamada da API que retorna o identificador) - o identificador deve ser tratado simplesmente como um valor opaco significativo apenas para a API.
Devo acrescentar que, em qualquer sistema operacional moderno, mesmo os chamados "ponteiros reais" ainda são opacos no espaço de memória virtual do processo, o que permite que o sistema operacional gerencie e reorganize a memória sem invalidar os ponteiros no processo .
A HANDLE
é um identificador exclusivo específico do contexto. Por contexto específico, quero dizer que um identificador obtido de um contexto não pode necessariamente ser usado em qualquer outro contexto arbitrário que também funcione em HANDLE
s.
Por exemplo, GetModuleHandle
retorna um identificador exclusivo para um módulo carregado no momento. O identificador retornado pode ser usado em outras funções que aceitam identificadores de módulo. Não pode ser atribuído a funções que requerem outros tipos de identificadores. Por exemplo, você não pode dar um identificador retornado de GetModuleHandle
para HeapDestroy
e esperar que ele faça algo sensato.
O HANDLE
próprio é apenas um tipo integral. Geralmente, mas não necessariamente, é um ponteiro para algum tipo ou local de memória subjacente. Por exemplo, o HANDLE
retornado por GetModuleHandle
é na verdade um ponteiro para o endereço de memória virtual base do módulo. Mas não há regra afirmando que os identificadores devem ser ponteiros. Um identificador também pode ser apenas um número inteiro simples (que pode ser usado por alguma API do Win32 como um índice em uma matriz).
HANDLE
s são representações intencionalmente opacas que fornecem encapsulamento e abstração de recursos internos do Win32. Dessa forma, as APIs do Win32 podem potencialmente alterar o tipo subjacente por trás de um HANDLE, sem afetar o código do usuário de qualquer forma (pelo menos essa é a ideia).
Considere estas três implementações internas diferentes de uma API do Win32 que acabei de criar e suponha que Widget
seja 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);
}
O primeiro exemplo expõe os detalhes internos sobre a API: permite que o código do usuário saiba que GetWidget
retorna um ponteiro para a struct Widget
. Isso tem algumas consequências:
Widget
estruturaWidget
estrutura retornadaAmbas as consequências podem ser indesejáveis.
O segundo exemplo oculta esse detalhe interno do código do usuário, retornando apenas void *
. O código do usuário não precisa de acesso ao cabeçalho que define a Widget
estrutura.
O terceiro exemplo é exatamente o mesmo que o segundo, mas apenas chamar a void *
uma HANDLE
vez. Talvez isso desencoraje o código do usuário de tentar descobrir exatamente para que void *
serve.
Por que passar por esse problema? Considere este quarto exemplo de uma versão mais recente dessa mesma API:
typedef void * HANDLE;
HANDLE GetWidget (std::string name)
{
NewImprovedWidget *w;
w = findImprovedWidget(name);
return reinterpret_cast<HANDLE>(w);
}
Observe que a interface da função é idêntica ao terceiro exemplo acima. Isso significa que o código do usuário pode continuar usando essa nova versão da API, sem nenhuma alteração, mesmo que a implementação "nos bastidores" tenha mudado para usar a NewImprovedWidget
estrutura.
Os identificadores neste exemplo são realmente apenas um novo nome, provavelmente mais amigável, void *
que é exatamente o que HANDLE
existe na API do Win32 (consulte o MSDN ). Ele fornece uma parede opaca entre o código do usuário e as representações internas da biblioteca Win32 que aumentam a portabilidade, entre versões do Windows, do código que usa a API do Win32.
handle
vez de, void *
é desencorajar o código do usuário de tentar descobrir exatamente o que o vazio * aponta . Estou correcto?
Um HANDLE na programação do Win32 é um token que representa um recurso gerenciado pelo kernel do Windows. Um identificador pode ser para uma janela, um arquivo etc.
Os identificadores são simplesmente uma maneira de identificar um recurso particulado com o qual você deseja trabalhar usando as APIs do Win32.
Por exemplo, se você deseja criar uma janela e mostrá-la na tela, você pode fazer o seguinte:
// Create the window
HWND hwnd = CreateWindow(...);
if (!hwnd)
return; // hwnd not created
// Show the window.
ShowWindow(hwnd, SW_SHOW);
No exemplo acima, HWND significa "um identificador para uma janela".
Se você está acostumado a uma linguagem orientada a objetos, pode pensar em HANDLE como uma instância de uma classe sem métodos cujo estado é modificável apenas por outras funções. Nesse caso, a função ShowWindow modifica o estado do punho da janela.
Consulte Identificadores e tipos de dados para obter mais informações.
HANDLE
ADT são gerenciados pelo kernel. Os outros tipos de identificador que você nomeia ( HWND
, etc.), por outro lado, são objetos USER. Esses não são gerenciados pelo kernel do Windows.
Um identificador é um identificador exclusivo para um objeto gerenciado pelo Windows. É como um ponteiro , mas não um ponteiro, na medida em que não é um endereço que possa ser desreferenciado pelo código do usuário para obter acesso a alguns dados. Em vez disso, um identificador deve ser passado para um conjunto de funções que podem executar ações no objeto que o identificador identifica.
Portanto, no nível mais básico, um HANDLE de qualquer tipo é um ponteiro para um ponteiro ou
#define HANDLE void **
Agora, por que você gostaria de usá-lo
Vamos fazer uma configuração:
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);
}
Portanto, como obj foi passado por valor (faça uma cópia e dê isso à função) para foo, o printf imprimirá o valor original de 1.
Agora, se atualizarmos foo para:
void foo(Object * bar)
{
LargeObj lo = new LargeObj();
bar->val++;
}
Há uma chance de que o printf imprima o valor atualizado de 2. Mas também há a possibilidade de que o foo cause algum tipo de corrupção ou exceção na memória.
O motivo é que, enquanto você está usando um ponteiro para passar obj à função, você também está alocando 2 Megs de memória, isso pode fazer com que o sistema operacional mova a memória atualizando o local da obj. Como você passou o ponteiro por valor, se obj for movido, o SO atualizará o ponteiro, mas não a cópia na função e, potencialmente, causando problemas.
Uma atualização final para foo de:
void foo(Object **bar){
LargeObj lo = LargeObj();
Object * b = &bar;
b->val++;
}
Isso sempre imprimirá o valor atualizado.
Veja, quando o compilador aloca memória para ponteiros, ele os marca como imóveis, portanto, qualquer reorganização da memória causada pelo objeto grande que está sendo alocado, o valor passado para a função apontará para o endereço correto para descobrir o local final na memória para atualizar.
Quaisquer tipos específicos de HANDLEs (hWnd, FILE etc.) são específicos do domínio e apontam para um determinado tipo de estrutura para proteção contra corrupção de memória.
Um identificador é como um valor de chave primária de um registro em um banco de dados.
edit 1: bem, por que o voto negativo, uma chave primária identifica exclusivamente um registro do banco de dados, e um identificador no sistema Windows identifica exclusivamente uma janela, um arquivo aberto etc., é o que estou dizendo.
Pense na janela do Windows como sendo uma estrutura que a descreve. Essa estrutura é uma parte interna do Windows e você não precisa saber os detalhes. Em vez disso, o Windows fornece um typedef para o ponteiro estruturar para essa estrutura. Essa é a "alça" pela qual você pode se segurar na janela.,