1. Function pointer
AddressOf gets a function pointer inside VB. We can pass this function pointer to the API that needs to call back this function. Its function is to allow external programs to call functions inside VB.
However, the application of function pointers in VB is not as extensive as that in C, because the VB document only introduces how to pass function pointers to APIs to implement callbacks, and does not point out the many magical functions of function pointers, because VB does not encourage When using pointers, function pointers are no exception.
First, let's classify how function pointers are used.
1. Callback. This is the most basic and important function. For example, the core of the subclass derivation technology introduced in the VB documentation is two APIs: SetWindowLong and CallWindowPRoc.
We can use the SetWindowLong API to replace the original window function pointer with our own function pointer, and save the original window function pointer. In this way, window messages can be sent to our own functions, and we can use CallWindowProc to call the previously saved window pointer at any time to call the original window function. In this way, we can handle hooked messages without destroying the functionality of the original window.
We should be familiar with the specific processing, and the VB document also explains it very clearly. What needs attention here is the CallWindowProc API, we will see its wonderful use later.
Here we call callbacks to allow "external calls to internal function pointers".
2. For internal use of the program. For example, in C, we can pass the C function pointer as a parameter to a C function that requires a function pointer, such as the C library function qsort that will be discussed later. Its declaration is as follows:
It requires a COMPARE type function pointer to compare the sizes of two variables, so that the sorting function can call this function pointer to compare different types of variables, so qsort can sort variable arrays of different types.
Let's call this application "calling internal function pointers from within".
3. Call external functions
You may ask, isn't using API just calling external functions? Yes, but sometimes we still need to directly obtain the pointer of the external function. For example, dynamically loading a DLL through LoadLibrary, and then getting the function entry pointer we need through GetProcAddress, and then calling external functions through this function pointer. This technology of dynamically loading DLL allows us to call external functions more flexibly.
We call this method "calling an external function pointer from the inside"
4. Needless to say, we can also control "calling external function pointers from the outside". No, for example, we can load multiple DLLs and pass the function pointer in one DLL to the function in another DLL.
The "internal" and "external" divisions above are all relative terms (the DLL is actually still within the process). This classification will help us discuss issues in the future. Please remember my above classification, because future articles will also Use this classification to analyze the problem.
The use of function pointers is nothing more than the above four ways. But in actual use it is flexible and changeable. For example, inheritance and polymorphism in C, and interfaces in COM are all clever applications of a function pointer table called vTable. Using function pointers can make program processing more efficient and flexible.
Except for the first method, the VB document does not introduce other methods, and it also clearly states that the "Basic to Basic" function pointer is not supported (that is, the second method mentioned above). In fact, through certain HACK, all the above four methods can be achieved. Today, let’s take a look at how to implement the second method. Because it is relatively simple to implement, let’s start with the simple one. As for how to call external function pointers in VB, and how to realize the clever application of various function pointers by processing the vTable interface function pointer jump table in VB, since this will involve the internal principles of COM, I will elaborate in another article.
In fact, the VB documentation is not wrong. VB does not support the "Basic to Basic" function pointer, but we can make a roundabout way to achieve it, that is, first "Basic to API", and then use the first method "external Call the internal function pointer "to go from "API to BASIC", thus achieving the purpose of the second way from "Basic to Basic". We can call this technology "forced callback", which is only available in VB Such weird technology.
It’s a bit convoluted, but think carefully about CallWindowProc in the window subclass derivation technology. We can use CallWindowProc to force the external operating system to call our original saved window function pointer. Similarly, we can also use it to force the call to our internal function pointer.
Haha, I said before that we should talk less about principles and more about moves. Now let’s start learning the moves!
Consider that we implement qsort in VB that supports multi-keyword comparison just like C. The complete source code can be found in the supporting code of this article. Only the code related to function pointer application is given here.
Let's take a final look at our final qsort statement.
The ArrayPtr above is the pointer to the first element of the array that needs to be sorted, nCount is the number of elements in the array, nElemSize is the size of each element, and pfnCompare is our comparison function pointer. This declaration is very similar to the qsort in the C library function.
Like C, we can pass Basic's function pointer to Basic's qsort function.
How to use it:
Smart friends, have you already seen the mystery here? As a test, can you now give me a way to use function pointers in qsort? For example, now we need to compare the size of the i-th element and the j-th element of the array by calling a function pointer.
Yes, of course you must use the Compare API declared earlier (actually CallWindowProc) to perform forced callbacks.
The specific implementation is as follows:
The moves are introduced, do you understand? Let me briefly explain the meaning of Compare above. It makes very clever use of the CallWindowProc API. This API requires five parameters. The first parameter is an ordinary function pointer. This API can call back the function pointer immediately and pass the last four Long type parameters of this API to the function pointed to by the function pointer. This is why our comparison function must have four parameters, because the CallWindowProc API requires that the function pointer passed to it must conform to the WndProc function prototype. The prototype of WndProc is as follows:
The above LRESULT, HWND, UINT, WPARAM, and LPARAM can all correspond to the Long type in VB. This is really great, because the Long type can be used as a pointer!
Let's look at the workflow again. When we call qsort using AddressOfCompareSalaryName as the function pointer parameter, the formal parameter pfnCompare of qsort is assigned the function pointer of the actual parameter CompareSalaryName. At this time, calling Compare to force a callback to pfnCompare is equivalent to calling the following VB statement:
Won't this cause a parameter type mismatch error? Aren't the first two parameters of CompareSalaryName of type TEmployee? Indeed, such a call is not possible in VB, because VB's type check will not allow such a call. However, this call is actually a callback made by the API, and it is impossible for VB to check whether the parameter type of the API callback function is an ordinary Long numerical type or a structure pointer, so it can also be said that we have bypassed VB's control of function parameters. For type checking, we can declare this Long parameter as a pointer of any type. Whatever we declare, VB will think it is. Therefore, we must use this technique carefully. For example, the parameter "ArrayPtr (i-1)*nElemSize" that will eventually be passed to the CompareSalaryName function above is just an address. VB will not check this address. It will always This address is treated as a pointer of type TEmployee. If it is accidentally used as "ArrayPtr i*nElemSize", then when i is the last element, we will cause a memory access error, so we have to pay attention to boundary issues just like when dealing with pointers in C.
The clever application of function pointers can already be seen here, but the method introduced here still has great limitations. Our function must have four parameters. A cleaner approach is to write a DLL in VC or Delphi and make A more compliant API to implement functions similar to CallWindowProc. I have traced the internal implementation of CallWindowProc. It does a lot of work related to window messages, which is redundant in our application. In fact, to implement the forced callback API, you only need to push the last few parameters onto the stack and then call the first parameter, which is just a few assembly instructions.
It is precisely because of the limitations of CallWindowProc that we cannot use it to call external function pointers to implement the third function pointer calling method mentioned above. To implement the third method, Master MattCurland provided a nightmare-like HACK method. We need to construct an IUnknown interface out of thin air in VB, and add a new entry after the original three entries of the vTable of the IUnknown interface. In the new entry Insert machine code into it. This machine code must deal with this pointer before finally calling the function pointer we gave. This function pointer is no problem whether it is internal or external. I'll come back to this method when we dive into COM internals.
In addition, sorting algorithms are a matter of opinion. I originally wanted to provide the most versatile algorithm with the best performance in this article. Although this idea is good, there is no "best" algorithm in every situation. The quick sorting method implemented using various pointer technologies provided in this article should be much faster and take up much less memory than using object technology to implement the same function. But even this quick sort algorithm, which has been optimized by me, is still not as good as ShellSort because ShellSort is simple to implement. Theoretically speaking from the algorithm, qsort should have better average performance than ShellSort, but this is not necessarily the case in VB (you can see the supporting code of this article, which also provides the supporting code of a VBPJ column, ShellSort, which is very good, and the idea of this article is taken from This ShellSort).
However, it should be pointed out that both Quick Sort and ShellSort here can be greatly improved, because their implementation requires extensive use of CopyMemroy to copy data (this is one of the shortcomings of using pointers in VB). In fact, we have a better way, which is to Hack the array structure of VB, which is SafeArray in COM automation. We can put the pointers of each array element in SafeArray into a long array at once. Without CopyMemroy, we only need to exchange the elements in the Long array to achieve the purpose of exchanging the SafeArray array element pointers in real time. The data is not moved, only the pointers are moved. You can imagine how much faster this is.
->