1. Pointeur de fonction
AddressOf obtient un pointeur de fonction dans VB. Nous pouvons transmettre ce pointeur de fonction à l'API qui doit rappeler cette fonction. Sa fonction est de permettre aux programmes externes d'appeler des fonctions dans VB.
Cependant, l'application des pointeurs de fonction en VB n'est pas aussi étendue qu'en C, car le document VB présente uniquement comment transmettre les pointeurs de fonction aux API pour implémenter les rappels, et ne souligne pas les nombreuses fonctions magiques des pointeurs de fonction, car VB n'encourage pas Lors de l'utilisation de pointeurs, les pointeurs de fonction ne font pas exception.
Tout d’abord, classifions la manière dont les pointeurs de fonction sont utilisés.
1. Rappel. C'est la fonction la plus fondamentale et la plus importante. Par exemple, le cœur de la technologie de dérivation de sous-classe présentée dans la documentation VB est constitué de deux API : SetWindowLong et CallWindowPRoc.
Nous pouvons utiliser l'API SetWindowLong pour remplacer le pointeur de fonction de fenêtre d'origine par notre propre pointeur de fonction et enregistrer le pointeur de fonction de fenêtre d'origine. De cette façon, les messages de fenêtre peuvent être envoyés à nos propres fonctions et nous pouvons utiliser CallWindowProc pour appeler à tout moment le pointeur de fenêtre précédemment enregistré pour appeler la fonction de fenêtre d'origine. De cette façon, nous pouvons gérer les messages accrochés sans détruire la fonctionnalité de la fenêtre d'origine.
Nous devons être familiers avec le traitement spécifique, et le document VB l'explique également très clairement. Ce qui mérite attention ici, c'est l'API CallWindowProc, nous verrons sa merveilleuse utilisation plus tard.
Ici, nous appelons des rappels pour autoriser les « appels externes vers des pointeurs de fonction internes ».
2. Pour un usage interne du programme. Par exemple, en C, nous pouvons passer le pointeur de fonction C en paramètre à une fonction C qui nécessite un pointeur de fonction, comme la fonction qsort de la bibliothèque C dont nous parlerons plus tard. Sa déclaration est la suivante :
Il nécessite un pointeur de fonction de type COMPARE pour comparer les tailles de deux variables, afin que la fonction de tri puisse appeler ce pointeur de fonction pour comparer différents types de variables, afin que qsort puisse trier des tableaux de variables de différents types.
Appelons cette application "appeler des pointeurs de fonctions internes depuis l'intérieur".
3. Appeler des fonctions externes
Vous vous demandez peut-être si l’utilisation de l’API n’appelle pas simplement des fonctions externes ? Oui, mais parfois il faut quand même obtenir directement le pointeur de la fonction externe. Par exemple, charger dynamiquement une DLL via LoadLibrary, puis obtenir le pointeur d'entrée de fonction dont nous avons besoin via GetProcAddress, puis appeler des fonctions externes via ce pointeur de fonction. Cette technologie de chargement dynamique de DLL nous permet d'appeler des fonctions externes de manière plus flexible.
Nous appelons cette méthode "appeler un pointeur de fonction externe depuis l'intérieur"
4. Inutile de dire que nous pouvons également contrôler « l'appel de pointeurs de fonctions externes depuis l'extérieur ». Non, par exemple, nous pouvons charger plusieurs DLL et transmettre le pointeur de fonction dans une DLL à la fonction dans une autre DLL.
Les divisions « interne » et « externe » ci-dessus sont toutes des termes relatifs (la DLL est en fait encore en cours de processus). Cette classification nous aidera à discuter des problèmes à l'avenir. N'oubliez pas ma classification ci-dessus, car les prochains articles utiliseront également cette classification. pour analyser le problème.
L'utilisation de pointeurs de fonction n'est rien de plus que les quatre méthodes ci-dessus. Mais en pratique, il est flexible et modifiable. Par exemple, l'héritage et le polymorphisme en C, ainsi que les interfaces en COM sont tous des applications intelligentes d'une table de pointeurs de fonctions appelée vTable. L'utilisation de pointeurs de fonction peut rendre le traitement du programme plus efficace et plus flexible.
À l'exception de la première méthode, le document VB n'introduit pas d'autres méthodes, et il indique également clairement que le pointeur de fonction "Basic to Basic" n'est pas pris en charge (c'est-à-dire la deuxième méthode mentionnée ci-dessus, en fait via certains HACK). les quatre méthodes ci-dessus peuvent être réalisées. Aujourd’hui, voyons comment mettre en œuvre la deuxième méthode. Parce qu’elle est relativement simple à mettre en œuvre, commençons par la plus simple. Quant à savoir comment appeler des pointeurs de fonction externes en VB et comment réaliser l'application intelligente de divers pointeurs de fonction en traitant la table de saut de pointeur de fonction de l'interface vTable en VB, puisque cela impliquera les principes internes de COM, je développerai dans un autre article .
En fait, la documentation VB n'a pas tort. VB ne prend pas en charge le pointeur de fonction "Basic to Basic", mais nous pouvons faire un chemin détourné pour y parvenir, c'est-à-dire d'abord "Basic to API", puis utiliser la première méthode. "externe Appelez le pointeur de fonction interne "pour passer de "API à BASIC", atteignant ainsi l'objectif de la deuxième voie de "Basic à Basic". Nous pouvons appeler cette technologie "rappel forcé", qui n'est disponible qu'en VB. C'est tellement bizarre. technologie.
C'est un peu compliqué, mais réfléchissez bien à CallWindowProc dans la technologie de dérivation de sous-classe de fenêtre. Nous pouvons utiliser CallWindowProc pour forcer le système d'exploitation externe à appeler notre pointeur de fonction de fenêtre enregistré d'origine. De même, nous pouvons également l'utiliser pour forcer l'appel à notre pointeur de fonction de fenêtre interne. pointeur de fonction.
Haha, j’ai déjà dit que nous devrions parler moins de principes et plus de mouvements. Maintenant, commençons à apprendre les mouvements !
Considérez que nous implémentons qsort dans VB qui prend en charge la comparaison de plusieurs mots-clés, tout comme C. Le code source complet peut être trouvé dans le code de support de cet article. Seul le code lié à l'application du pointeur de fonction est donné ici.
Jetons un dernier coup d'œil à notre instruction finale qsort.
ArrayPtr ci-dessus est le pointeur vers le premier élément du tableau qui doit être trié, nCount est le nombre d'éléments dans le tableau, nElemSize est la taille de chaque élément et pfnCompare est notre pointeur de fonction de comparaison. Cette déclaration est très similaire à la fonction qsort de la bibliothèque C.
Comme C, nous pouvons transmettre le pointeur de fonction de Basic à la fonction qsort de Basic.
Comment l'utiliser :
Amis malins, avez-vous déjà vu le mystère ici ? À titre de test, pouvez-vous maintenant me donner un moyen d'utiliser les pointeurs de fonction dans qsort ? Par exemple, nous devons maintenant comparer la taille du i-ème élément et du j-ème élément du tableau en appelant un pointeur de fonction.
Oui, bien sûr, vous devez utiliser l'API Compare déclarée précédemment (en fait CallWindowProc) pour effectuer des rappels forcés.
La mise en œuvre spécifique est la suivante :
Les mouvements sont introduits, vous comprenez ? Permettez-moi d'expliquer brièvement la signification de Compare ci-dessus. Il utilise très intelligemment l'API CallWindowProc. Cette API nécessite cinq paramètres.Le premier paramètre est un pointeur de fonction ordinaire.Cette API peut rappeler le pointeur de fonction immédiatement et transmettre les quatre derniers paramètres de type Long de cette API à la fonction pointée par le pointeur de fonction. C'est pourquoi notre fonction de comparaison doit avoir quatre paramètres, car l'API CallWindowProc exige que le pointeur de fonction qui lui est passé soit conforme au prototype de la fonction WndProc. Le prototype de WndProc est le suivant :
Les LRESULT, HWND, UINT, WPARAM et LPARAM ci-dessus peuvent tous correspondre au type Long en VB. C'est vraiment génial, car le type Long peut être utilisé comme pointeur !
Regardons à nouveau le workflow. Lorsque nous appelons qsort en utilisant AddressOfCompareSalaryName comme paramètre de pointeur de fonction, le paramètre formel pfnCompare de qsort se voit attribuer le pointeur de fonction du paramètre réel CompareSalaryName. À l’heure actuelle, appeler Compare pour forcer un rappel à pfnCompare équivaut à appeler l’instruction VB suivante :
Cela ne provoquera-t-il pas une erreur de non-concordance de type de paramètre ? Les deux premiers paramètres de CompareSalaryName ne sont-ils pas de type TEmployee ? En effet, un tel appel n'est pas possible en VB, car la vérification de type de VB ne permettra pas un tel appel. Cependant, cet appel est en fait un rappel effectué par l'API, et il est impossible pour VB de vérifier si le type de paramètre de la fonction de rappel de l'API est un type numérique Long ordinaire ou un pointeur de structure, on peut donc également dire que nous avons contourné le contrôle des paramètres de fonction par VB. Pour la vérification de type, nous pouvons déclarer ce paramètre Long comme un pointeur de n'importe quel type, quoi que nous déclarions, VB le pensera. Par conséquent, nous devons utiliser cette technique avec précaution. Par exemple, le paramètre "ArrayPtr (i-1)*nElemSize" qui sera éventuellement transmis à la fonction CompareSalaryName ci-dessus n'est qu'une adresse. VB ne vérifiera pas toujours cette adresse. L'adresse est traitée comme un pointeur de type TEmployee si elle est accidentellement utilisée comme "ArrayPtr". i*nElemSize", alors quand i est le dernier élément, nous provoquerons une erreur d'accès à la mémoire, nous devons donc faire attention aux problèmes de limites, tout comme lorsqu'il s'agit de pointeurs en C.
L'application intelligente des pointeurs de fonction peut déjà être vue ici, mais la méthode présentée ici a encore de grandes limites. Notre fonction doit avoir quatre paramètres. Une approche plus propre consiste à écrire une DLL en VC ou Delphi et à créer une API plus conforme pour implémenter les fonctions. similaire à CallWindowProc. J'ai retracé l'implémentation interne de CallWindowProc. Il effectue beaucoup de travail lié aux messages de fenêtre, ce qui est redondant dans notre application. En fait, pour implémenter l'API de rappel forcé, il vous suffit de placer les derniers paramètres sur la pile, puis d'appeler le premier paramètre, qui ne contient que quelques instructions d'assemblage.
C'est précisément à cause des limitations de CallWindowProc que nous ne pouvons pas l'utiliser pour appeler des pointeurs de fonction externes afin d'implémenter la troisième méthode d'appel de pointeur de fonction mentionnée ci-dessus. Pour implémenter la troisième méthode, Maître MattCurland a fourni une méthode HACK de type cauchemar. Nous devons construire une interface IUnknown à partir de rien dans VB et ajouter une nouvelle entrée après les trois entrées originales de la vTable de l'interface IUnknown. nouvelle entrée Insérez-y du code machine. Ce code machine doit gérer ce pointeur avant d'appeler finalement le pointeur de fonction que nous avons donné. Ce pointeur de fonction ne pose aucun problème, qu'il soit interne ou externe. Je reviendrai sur cette méthode lorsque nous plongerons dans les composants internes de COM.
De plus, les algorithmes de tri sont une question d'opinion.Je voulais à l'origine fournir dans cet article l'algorithme le plus polyvalent avec les meilleures performances. Bien que cette idée soit bonne, il n'y a pas de « meilleur » algorithme dans chaque situation. La méthode de tri rapide implémentée à l'aide de diverses technologies de pointeur fournies dans cet article devrait être beaucoup plus rapide et occuper beaucoup moins de mémoire que l'utilisation de la technologie objet pour implémenter la même fonction. Mais même cet algorithme de tri rapide, que j'ai optimisé, n'est toujours pas aussi bon que ShellSort car ShellSort est simple à mettre en œuvre. Théoriquement parlant d'après l'algorithme, qsort devrait avoir de meilleures performances moyennes que ShellSort, mais ce n'est pas nécessairement le cas en VB (vous pouvez voir le code de support de cet article, qui fournit également le code de support de la colonne VBPJ ShellSort, qui est très bon , et l'idée de cet article est tirée de This ShellSort).
Cependant, il convient de souligner que Quick Sort et ShellSort peuvent être grandement améliorés ici, car leur implémentation nécessite une utilisation intensive de CopyMemroy pour copier les données (c'est l'un des inconvénients de l'utilisation de pointeurs dans VB). En fait, nous avons un meilleur moyen, qui consiste à pirater la structure du tableau de VB, qui est SafeArray dans l'automatisation COM. Nous pouvons placer les pointeurs de chaque élément du tableau dans SafeArray dans un long tableau à la fois, sans CopyMemroy. pour échanger les éléments du tableau Long pour atteindre l'objectif d'échanger les pointeurs d'éléments du tableau SafeArray en temps réel. Les données ne sont pas déplacées, seuls les pointeurs sont déplacés. Vous pouvez imaginer à quel point cela est plus rapide.
->