1. Ponteiro de função
AddressOf obtém um ponteiro de função dentro do VB. Podemos passar esse ponteiro de função para a API que precisa chamar de volta esta função.
No entanto, a aplicação de ponteiros de função em VB não é tão extensa quanto em C, porque o documento VB apenas apresenta como passar ponteiros de função para APIs para implementar retornos de chamada e não aponta as muitas funções mágicas dos ponteiros de função, porque VB não incentiva Ao usar ponteiros, os ponteiros de função não são exceção.
Primeiro, vamos classificar como os ponteiros de função são usados.
1. Retorno de chamada. Esta é a função mais básica e importante. Por exemplo, o núcleo da tecnologia de derivação de subclasse introduzida na documentação do VB são duas APIs: SetWindowLong e CallWindowPRoc.
Podemos usar a API SetWindowLong para substituir o ponteiro de função da janela original por nosso próprio ponteiro de função e salvar o ponteiro de função da janela original. Dessa forma, as mensagens da janela podem ser enviadas para nossas próprias funções e podemos usar CallWindowProc para chamar o ponteiro da janela salvo anteriormente a qualquer momento para chamar a função da janela original. Dessa forma, podemos lidar com mensagens interceptadas sem destruir a funcionalidade da janela original.
Devemos estar familiarizados com o processamento específico, e o documento VB também explica isso de forma muito clara. O que precisa de atenção aqui é a API CallWindowProc, veremos seu uso maravilhoso mais tarde.
Aqui chamamos retornos de chamada para permitir "chamadas externas para ponteiros de função internos".
2. Para uso interno do programa. Por exemplo, em C, podemos passar o ponteiro de função C como parâmetro para uma função C que requer um ponteiro de função, como a função da biblioteca C qsort, que será discutida posteriormente.
Requer um ponteiro de função do tipo COMPARE para comparar os tamanhos de duas variáveis, de modo que a função de classificação possa chamar esse ponteiro de função para comparar diferentes tipos de variáveis, para que qsort possa classificar matrizes de variáveis de diferentes tipos.
Vamos chamar esse aplicativo de "chamando ponteiros de funções internas de dentro".
3. Chame funções externas
Você pode perguntar: usar API não está apenas chamando funções externas? Sim, mas às vezes ainda precisamos obter diretamente o ponteiro da função externa. Por exemplo, carregar dinamicamente uma DLL por meio de LoadLibrary e, em seguida, obter o ponteiro de entrada de função que precisamos por meio de GetProcAddress e, em seguida, chamar funções externas por meio desse ponteiro de função. Essa tecnologia de carregamento dinamicamente de DLL nos permite chamar funções externas de maneira mais flexível.
Chamamos esse método de "chamar um ponteiro de função externo de dentro"
4. Escusado será dizer que também podemos controlar "chamar ponteiros de função externa de fora". Não, por exemplo, podemos carregar várias DLLs e passar o ponteiro de função de uma DLL para a função de outra DLL.
As divisões "interna" e "externa" acima são todas termos relativos (a DLL ainda está no processo). Essa classificação nos ajudará a discutir problemas no futuro. Lembre-se da minha classificação acima, porque artigos futuros também usarão essa classificação. para analisar o problema.
O uso de ponteiros de função nada mais é do que as quatro maneiras acima. Mas no uso real é flexível e mutável. Por exemplo, herança e polimorfismo em C e interfaces em COM são aplicações inteligentes de uma tabela de ponteiros de função chamada vTable. O uso de ponteiros de função pode tornar o processamento do programa mais eficiente e flexível.
Exceto o primeiro método, o documento VB não introduz outros métodos e também afirma claramente que o ponteiro de função "Básico para Básico" não é suportado (ou seja, o segundo método mencionado acima, na verdade, através de determinado HACK,). todos os quatro métodos acima podem ser alcançados. Hoje, vamos dar uma olhada em como implementar o segundo método. Por ser relativamente simples de implementar, vamos começar com o mais simples. Quanto a como chamar ponteiros de função externos em VB e como realizar a aplicação inteligente de vários ponteiros de função processando a tabela de salto de ponteiro de função da interface vTable em VB, uma vez que isso envolverá os princípios internos do COM, irei elaborar em outro artigo .
Na verdade, a documentação do VB não está errada. O VB não suporta o ponteiro de função "Básico para Básico", mas podemos fazer um caminho indireto para alcançá-lo, ou seja, primeiro "Básico para API" e depois usar o primeiro método. "externo Chama o ponteiro da função interna" para ir de "API para BASIC", atingindo assim o objetivo da segunda via de "Básico para Básico". Podemos chamar essa tecnologia de "retorno de chamada forçado", que só está disponível em VB. tecnologia.
É um pouco complicado, mas pense cuidadosamente sobre CallWindowProc na tecnologia de derivação de subclasse de janela. Podemos usar CallWindowProc para forçar o sistema operacional externo a chamar nosso ponteiro de função de janela salvo original. ponteiro de função.
Haha, eu disse antes que deveríamos falar menos sobre princípios e mais sobre movimentos. Agora vamos começar a aprender os movimentos!
Considere que implementamos qsort em VB que suporta comparação de várias palavras-chave, assim como C. O código-fonte completo pode ser encontrado no código de suporte deste artigo. Somente o código relacionado ao aplicativo de ponteiro de função é fornecido aqui.
Vamos dar uma olhada final em nossa instrução qsort final.
O ArrayPtr acima é o ponteiro para o primeiro elemento do array que precisa ser classificado, nCount é o número de elementos no array, nElemSize é o tamanho de cada elemento e pfnCompare é nosso ponteiro de função de comparação. Esta declaração é muito semelhante ao qsort na função da biblioteca C.
Como C, podemos passar o ponteiro de função do Basic para a função qsort do Basic.
Como usar:
Amigos espertos, vocês já viram o mistério aqui? Como teste, agora você pode me dar uma maneira de usar ponteiros de função no qsort? Por exemplo, agora precisamos comparar o tamanho do i-ésimo elemento e do j-ésimo elemento do array chamando um ponteiro de função.
Sim, é claro que você deve usar a API Compare declarada anteriormente (na verdade, CallWindowProc) para executar retornos de chamada forçados.
A implementação específica é a seguinte:
Os movimentos são introduzidos, entendeu? Deixe-me explicar brevemente o significado de Compare acima. Ele faz um uso muito inteligente da API CallWindowProc. Esta API requer cinco parâmetros. O primeiro parâmetro é um ponteiro de função comum. Esta API pode retornar o ponteiro de função imediatamente e passar os últimos quatro parâmetros do tipo Long desta API para a função apontada pelo ponteiro de função. É por isso que nossa função de comparação deve ter quatro parâmetros, porque a API CallWindowProc exige que o ponteiro de função passado para ela esteja em conformidade com o protótipo da função WndProc. O protótipo de WndProc é o seguinte:
Os LRESULT, HWND, UINT, WPARAM e LPARAM acima podem corresponder ao tipo Long em VB. Isso é realmente ótimo, porque o tipo Long pode ser usado como um ponteiro!
Vejamos o fluxo de trabalho novamente. Quando chamamos qsort usando AddressOfCompareSalaryName como parâmetro de ponteiro de função, o parâmetro formal pfnCompare de qsort recebe o ponteiro de função do parâmetro real CompareSalaryName. Neste momento, chamar Compare para forçar um retorno de chamada para pfnCompare é equivalente a chamar a seguinte instrução VB:
Isso não causará um erro de incompatibilidade de tipo de parâmetro? Os dois primeiros parâmetros de CompareSalaryName não são do tipo TEmployee? Na verdade, tal chamada não é possível em VB, porque a verificação de tipo do VB não permitirá tal chamada. No entanto, esta chamada é na verdade um retorno de chamada feito pela API, e é impossível para o VB verificar se o tipo de parâmetro da função de retorno de chamada da API é um tipo numérico longo comum ou um ponteiro de estrutura, então também podemos dizer que temos ignoramos o controle dos parâmetros de função do VB Para verificação de tipo, podemos declarar este parâmetro Long como um ponteiro de qualquer tipo, o VB pensará que é. Portanto, devemos usar esta técnica com cuidado. Por exemplo, o parâmetro "ArrayPtr (i-1)*nElemSize" que eventualmente será passado para a função CompareSalaryName acima é apenas um endereço. O VB não verificará esse endereço. address é tratado como um ponteiro do tipo TEmployee se for usado acidentalmente como "ArrayPtr. i*nElemSize", então quando i for o último elemento, causaremos um erro de acesso à memória, então temos que prestar atenção aos problemas de limite, assim como quando lidamos com ponteiros em C.
A aplicação inteligente de ponteiros de função já pode ser vista aqui, mas o método apresentado aqui ainda tem grandes limitações. Nossa função deve ter quatro parâmetros. Uma abordagem mais limpa é escrever uma DLL em VC ou Delphi e fazer uma API mais compatível para implementar funções. semelhante a CallWindowProc. Rastreei a implementação interna do CallWindowProc. Ele faz muito trabalho relacionado às mensagens da janela, o que é redundante em nosso aplicativo. Na verdade, para implementar a API de retorno de chamada forçado, você só precisa colocar os últimos parâmetros na pilha e depois chamar o primeiro parâmetro, que são apenas algumas instruções de montagem.
É precisamente por causa das limitações de CallWindowProc que não podemos usá-lo para chamar ponteiros de função externos para implementar o terceiro método de chamada de ponteiro de função mencionado acima. Para implementar o terceiro método, Mestre MattCurland forneceu um método HACK semelhante a um pesadelo. Precisamos construir uma interface IUnknown do nada em VB e adicionar uma nova entrada após as três entradas originais da vTable da interface IUnknown. nova entrada Insira o código de máquina nele. Este código de máquina deve lidar com este ponteiro antes de finalmente chamar o ponteiro de função que fornecemos. Este ponteiro de função não é problema, seja ele interno ou externo. Voltarei a esse método quando nos aprofundarmos nos aspectos internos do COM.
Além disso, os algoritmos de classificação são uma questão de opinião. Originalmente, eu queria fornecer o algoritmo mais versátil e com melhor desempenho neste artigo. Embora essa ideia seja boa, não existe um algoritmo "melhor" em todas as situações. O método de classificação rápida implementado usando várias tecnologias de ponteiro fornecidas neste artigo deve ser muito mais rápido e ocupar muito menos memória do que usar a tecnologia de objetos para implementar a mesma função. Mas mesmo esse algoritmo de classificação rápida, que foi otimizado por mim, ainda não é tão bom quanto o ShellSort porque o ShellSort é simples de implementar. Teoricamente falando do algoritmo, qsort deveria ter desempenho médio melhor que ShellSort, mas este não é necessariamente o caso em VB (você pode ver o código de suporte deste artigo, que também fornece o código de suporte de uma coluna VBPJ, ShellSort, que é muito bom, e a ideia deste artigo foi tirada de This ShellSort).
No entanto, deve-se ressaltar que tanto Quick Sort quanto ShellSort aqui podem ser bastante melhorados, porque sua implementação requer uso extensivo de CopyMemroy para copiar dados (esta é uma das deficiências do uso de ponteiros em VB). Na verdade, temos uma maneira melhor, que é hackear a estrutura do array do VB, que é SafeArray na automação COM. Podemos colocar os ponteiros de cada elemento do array no SafeArray em um array longo de uma vez. para trocar os elementos do array Long para atingir o objetivo de trocar os ponteiros dos elementos do array SafeArray em tempo real. Os dados não são movidos, apenas os ponteiros são movidos.
->