1. Funktionszeiger
AddressOf erhält einen Funktionszeiger innerhalb von VB. Wir können diesen Funktionszeiger an die API übergeben, die diese Funktion zurückrufen muss. Seine Funktion besteht darin, externen Programmen den Aufruf von Funktionen innerhalb von VB zu ermöglichen.
Die Anwendung von Funktionszeigern in VB ist jedoch nicht so umfangreich wie in C, da das VB-Dokument nur die Übergabe von Funktionszeigern an APIs zur Implementierung von Rückrufen vorstellt und nicht auf die vielen magischen Funktionen von Funktionszeigern hinweist, da VB ermutigt nicht Bei der Verwendung von Zeigern sind Funktionszeiger keine Ausnahme.
Lassen Sie uns zunächst klassifizieren, wie Funktionszeiger verwendet werden.
1. Rückruf. Dies ist die grundlegendste und wichtigste Funktion. Der Kern der in der VB-Dokumentation eingeführten Unterklassen-Ableitungstechnologie besteht beispielsweise aus zwei APIs: SetWindowLong und CallWindowPRoc.
Wir können die SetWindowLong-API verwenden, um den ursprünglichen Fensterfunktionszeiger durch unseren eigenen Funktionszeiger zu ersetzen und den ursprünglichen Fensterfunktionszeiger zu speichern. Auf diese Weise können Fensternachrichten an unsere eigenen Funktionen gesendet werden, und wir können mit CallWindowProc jederzeit den zuvor gespeicherten Fensterzeiger aufrufen, um die ursprüngliche Fensterfunktion aufzurufen. Auf diese Weise können wir mit Hook-Nachrichten umgehen, ohne die Funktionalität des ursprünglichen Fensters zu zerstören.
Wir sollten mit der spezifischen Verarbeitung vertraut sein und das VB-Dokument erklärt es auch sehr anschaulich. Was hier Aufmerksamkeit erfordert, ist die CallWindowProc-API, deren wunderbare Verwendung wir später sehen werden.
Hier rufen wir Rückrufe auf, um „externe Aufrufe interner Funktionszeiger“ zu ermöglichen.
2. Zur internen Nutzung des Programms. In C können wir beispielsweise den C-Funktionszeiger als Parameter an eine C-Funktion übergeben, die einen Funktionszeiger erfordert, wie z. B. die C-Bibliotheksfunktion qsort, die später besprochen wird. Ihre Deklaration lautet wie folgt:
Zum Vergleichen der Größen zweier Variablen ist ein Funktionszeiger vom Typ COMPARE erforderlich, damit die Sortierfunktion diesen Funktionszeiger aufrufen kann, um verschiedene Variablentypen zu vergleichen, sodass qsort Variablenarrays unterschiedlicher Typen sortieren kann.
Nennen wir diese Anwendung „interne Funktionszeiger aufrufen“.
3. Rufen Sie externe Funktionen auf
Sie fragen sich vielleicht: Ist die Verwendung einer API nicht nur der Aufruf externer Funktionen? Ja, aber manchmal müssen wir den Zeiger der externen Funktion trotzdem direkt abrufen. Laden Sie beispielsweise eine DLL dynamisch über LoadLibrary, rufen Sie dann den benötigten Funktionseintragszeiger über GetProcAddress ab und rufen Sie dann externe Funktionen über diesen Funktionszeiger auf. Diese Technologie zum dynamischen Laden von DLLs ermöglicht es uns, externe Funktionen flexibler aufzurufen.
Wir nennen diese Methode „Aufruf eines externen Funktionszeigers von innen“.
4. Selbstverständlich können wir auch den „Aufruf externer Funktionszeiger von außen“ steuern. Nein, wir können beispielsweise mehrere DLLs laden und den Funktionszeiger in einer DLL an die Funktion in einer anderen DLL übergeben.
Die oben genannten Unterteilungen „intern“ und „extern“ sind allesamt relative Begriffe (die DLL befindet sich tatsächlich noch im Prozess). Bitte denken Sie an meine obige Klassifizierung, da zukünftige Artikel diese Klassifizierung ebenfalls verwenden werden um das Problem zu analysieren.
Die Verwendung von Funktionszeigern ist nichts anderes als die oben genannten vier Möglichkeiten. Aber im tatsächlichen Einsatz ist es flexibel und wandelbar. Beispielsweise sind Vererbung und Polymorphismus in C sowie Schnittstellen in COM allesamt clevere Anwendungen einer Funktionszeigertabelle namens vTable. Durch die Verwendung von Funktionszeigern kann die Programmverarbeitung effizienter und flexibler gestaltet werden.
Mit Ausnahme der ersten Methode werden im VB-Dokument keine anderen Methoden eingeführt, und es wird auch eindeutig darauf hingewiesen, dass der Funktionszeiger „Basic to Basic“ nicht unterstützt wird (dh die oben erwähnte zweite Methode). Alle oben genannten vier Methoden können erreicht werden. Schauen wir uns heute an, wie die zweite Methode implementiert wird. Da sie relativ einfach zu implementieren ist, beginnen wir mit der einfachen. Wie man externe Funktionszeiger in VB aufruft und wie man die clevere Anwendung verschiedener Funktionszeiger durch die Verarbeitung der Funktionszeiger-Sprungtabelle der vTable-Schnittstelle in VB realisiert, da dies die internen Prinzipien von COM beinhaltet, werde ich in einem anderen Artikel näher erläutern .
Tatsächlich ist die VB-Dokumentation nicht falsch. VB unterstützt den Funktionszeiger „Basic to Basic“, aber wir können einen Umweg machen, um dies zu erreichen, nämlich zuerst „Basic to API“ und dann die erste Methode verwenden „external Rufen Sie den internen Funktionszeiger auf“, um von „API zu BASIC“ zu wechseln und so den Zweck des zweiten Weges von „Basic zu Basic“ zu erreichen. Wir können diese Technologie „erzwungener Rückruf“ nennen, der nur in VB verfügbar ist So seltsam Technologie.
Es ist etwas kompliziert, aber denken Sie sorgfältig über CallWindowProc in der Fensterunterklassen-Ableitungstechnologie nach. Wir können CallWindowProc verwenden, um das externe Betriebssystem zum Aufrufen unseres ursprünglich gespeicherten Fensterfunktionszeigers zu zwingen Funktionszeiger.
Haha, ich habe vorhin gesagt, dass wir weniger über Prinzipien und mehr über Bewegungen reden sollten. Jetzt fangen wir an, die Bewegungen zu lernen!
Bedenken Sie, dass wir qsort in VB implementieren, das den Vergleich mehrerer Schlüsselwörter genau wie C unterstützt. Der vollständige Quellcode ist im unterstützenden Code dieses Artikels zu finden. Hier wird nur der Code für die Funktionszeigeranwendung angegeben.
Werfen wir einen letzten Blick auf unsere letzte qsort-Anweisung.
Das obige ArrayPtr ist der Zeiger auf das erste Element des Arrays, das sortiert werden muss, nCount ist die Anzahl der Elemente im Array, nElemSize ist die Größe jedes Elements und pfnCompare ist unser Vergleichsfunktionszeiger. Diese Deklaration ist dem qsort in der C-Bibliotheksfunktion sehr ähnlich.
Wie in C können wir den Funktionszeiger von Basic an die qsort-Funktion von Basic übergeben.
So verwenden Sie es:
Kluge Freunde, habt ihr das Geheimnis hier schon gesehen? Können Sie mir jetzt als Test eine Möglichkeit nennen, Funktionszeiger in qsort zu verwenden? Jetzt müssen wir beispielsweise die Größe des i-ten Elements und des j-ten Elements des Arrays vergleichen, indem wir einen Funktionszeiger aufrufen.
Ja, natürlich müssen Sie die zuvor deklarierte Compare-API (eigentlich CallWindowProc) verwenden, um erzwungene Rückrufe durchzuführen.
Die spezifische Implementierung ist wie folgt:
Die Bewegungen werden vorgestellt, verstehst du? Lassen Sie mich oben kurz die Bedeutung von Compare erläutern. Es nutzt die CallWindowProc-API sehr geschickt. Diese API erfordert fünf Parameter. Der erste Parameter ist ein gewöhnlicher Funktionszeiger. Diese API kann den Funktionszeiger sofort zurückrufen und die letzten vier Long-Typ-Parameter dieser API an die Funktion übergeben, auf die der Funktionszeiger zeigt. Aus diesem Grund muss unsere Vergleichsfunktion vier Parameter haben, da die CallWindowProc-API erfordert, dass der an sie übergebene Funktionszeiger dem WndProc-Funktionsprototyp entspricht. Der Prototyp von WndProc lautet wie folgt:
Die oben genannten Werte LRESULT, HWND, UINT, WPARAM und LPARAM können alle dem Long-Typ in VB entsprechen. Das ist wirklich großartig, da der Long-Typ als Zeiger verwendet werden kann!
Schauen wir uns den Workflow noch einmal an. Wenn wir qsort mit AddressOfCompareSalaryName als Funktionszeigerparameter aufrufen, wird dem formalen Parameter pfnCompare von qsort der Funktionszeiger des tatsächlichen Parameters CompareSalaryName zugewiesen. Zu diesem Zeitpunkt entspricht der Aufruf von Compare, um einen Rückruf an pfnCompare zu erzwingen, dem Aufruf der folgenden VB-Anweisung:
Wird dies nicht zu einem Fehler bei der Nichtübereinstimmung des Parametertyps führen? Sind die ersten beiden Parameter von CompareSalaryName nicht vom Typ TEmployee? Tatsächlich ist ein solcher Aufruf in VB nicht möglich, da die Typprüfung von VB einen solchen Aufruf nicht zulässt. Bei diesem Aufruf handelt es sich jedoch tatsächlich um einen von der API durchgeführten Rückruf, und VB kann nicht überprüfen, ob der Parametertyp der API-Rückruffunktion ein gewöhnlicher numerischer Long-Typ oder ein Strukturzeiger ist. Daher kann man auch sagen, dass dies der Fall ist Wir haben die Kontrolle über Funktionsparameter durch VB umgangen und können diesen Long-Parameter als Zeiger eines beliebigen Typs deklarieren. Daher müssen wir diese Technik sorgfältig anwenden. Der Parameter „ArrayPtr (i-1)*nElemSize“, der letztendlich an die Funktion „CompareSalaryName“ übergeben wird, überprüft diese Adresse nicht immer Adresse wird als Zeiger vom Typ TEmployee behandelt. Wenn sie versehentlich als „ArrayPtr i*nElemSize“, wenn i das letzte Element ist, verursachen wir einen Speicherzugriffsfehler, daher müssen wir auf Grenzprobleme achten, genau wie beim Umgang mit Zeigern in C.
Die clevere Anwendung von Funktionszeigern ist hier bereits zu sehen, aber die hier vorgestellte Methode weist immer noch große Einschränkungen auf. Unsere Funktion muss über vier Parameter verfügen. Ein saubererer Ansatz besteht darin, eine DLL in VC oder Delphi zu schreiben und eine kompatiblere API zur Implementierung von Funktionen zu erstellen ähnlich wie CallWindowProc. Ich habe die interne Implementierung von CallWindowProc verfolgt. Es erledigt eine Menge Arbeit im Zusammenhang mit Fenstermeldungen, die in unserer Anwendung überflüssig sind. Um die API für erzwungene Rückrufe zu implementieren, müssen Sie tatsächlich nur die letzten paar Parameter auf den Stapel verschieben und dann den ersten Parameter aufrufen, bei dem es sich lediglich um einige Assembleranweisungen handelt.
Gerade wegen der Einschränkungen von CallWindowProc können wir es nicht zum Aufrufen externer Funktionszeiger verwenden, um die oben erwähnte dritte Methode zum Aufrufen von Funktionszeigern zu implementieren. Um die dritte Methode zu implementieren, hat Master MattCurland eine albtraumartige HACK-Methode bereitgestellt. Wir müssen in VB eine IUnknown-Schnittstelle aus dem Nichts erstellen und nach den ursprünglichen drei Einträgen der vTable der IUnknown-Schnittstelle hinzufügen neuer Eintrag Fügen Sie Maschinencode ein. Dieser Maschinencode muss diesen Zeiger verarbeiten, bevor er den von uns angegebenen Funktionszeiger endgültig aufruft. Dieser Funktionszeiger ist kein Problem, egal ob er intern oder extern ist. Ich werde auf diese Methode zurückkommen, wenn wir uns mit COM-Interna befassen.
Darüber hinaus sind Sortieralgorithmen Ansichtssache. Ich wollte in diesem Artikel ursprünglich den vielseitigsten Algorithmus mit der besten Leistung bereitstellen. Obwohl diese Idee gut ist, gibt es nicht in jeder Situation den „besten“ Algorithmus. Die in diesem Artikel bereitgestellte schnelle Sortiermethode, die mithilfe verschiedener Zeigertechnologien implementiert wird, sollte viel schneller sein und viel weniger Speicher beanspruchen als die Verwendung von Objekttechnologie zur Implementierung derselben Funktion. Aber selbst dieser von mir optimierte Schnellsortieralgorithmus ist immer noch nicht so gut wie ShellSort, da ShellSort einfach zu implementieren ist. Theoretisch sollte qsort vom Algorithmus her eine bessere durchschnittliche Leistung haben als ShellSort, aber das ist in VB nicht unbedingt der Fall (Sie können den unterstützenden Code dieses Artikels sehen, der auch den unterstützenden Code einer VBPJ-Spalte, ShellSort, bereitstellt sehr gut, und die Idee dieses Artikels stammt von This ShellSort).
Es sollte jedoch darauf hingewiesen werden, dass sowohl Quick Sort als auch ShellSort hier erheblich verbessert werden können, da ihre Implementierung den umfassenden Einsatz von CopyMemroy zum Kopieren von Daten erfordert (dies ist einer der Nachteile der Verwendung von Zeigern in VB). Tatsächlich haben wir eine bessere Möglichkeit, die Array-Struktur von VB zu hacken, die in der COM-Automatisierung SafeArray ist. Wir können die Zeiger jedes Array-Elements in SafeArray auf einmal in ein langes Array einfügen, was wir nur brauchen um die Elemente im Long-Array auszutauschen, um den Zweck des Austauschs der SafeArray-Array-Elementzeiger in Echtzeit zu erreichen. Es werden nicht die Daten verschoben, sondern nur die Zeiger. Sie können sich vorstellen, wie viel schneller das geht.
->