1. 함수 포인터
AddressOf는 VB 내부에서 함수 포인터를 가져옵니다. 이 함수 포인터를 호출해야 하는 API에 전달할 수 있습니다. 그 기능은 외부 프로그램이 VB 내부에서 함수를 호출할 수 있도록 하는 것입니다.
그러나 VB에서 함수 포인터의 적용은 C만큼 광범위하지 않습니다. VB 문서에서는 콜백을 구현하기 위해 함수 포인터를 API에 전달하는 방법만 소개하고 함수 포인터의 많은 마법 같은 기능을 지적하지 않기 때문입니다. 포인터를 사용할 때 함수 포인터도 예외는 아닙니다.
먼저 함수 포인터가 어떻게 사용되는지 분류해 보겠습니다.
1. 콜백. 가장 기본적이고 중요한 기능입니다. 예를 들어 VB 문서에 소개된 서브클래스 파생 기술의 핵심은 SetWindowLong과 CallWindowPROc라는 두 가지 API입니다.
SetWindowLong API를 사용하여 원래 창 함수 포인터를 자체 함수 포인터로 바꾸고 원래 창 함수 포인터를 저장할 수 있습니다. 이러한 방식으로 창 메시지를 자체 함수로 보낼 수 있으며 CallWindowProc를 사용하여 언제든지 이전에 저장된 창 포인터를 호출하여 원래 창 함수를 호출할 수 있습니다. 이러한 방식으로 원래 창의 기능을 손상시키지 않고 후크된 메시지를 처리할 수 있습니다.
우리는 특정 처리에 대해 잘 알고 있어야 하며 VB 문서에서도 이를 매우 명확하게 설명합니다. 여기서 주의가 필요한 것은 CallWindowProc API입니다. 나중에 이 API의 멋진 사용법을 살펴보겠습니다.
여기서는 "내부 함수 포인터에 대한 외부 호출"을 허용하기 위해 콜백을 호출합니다.
2. 프로그램 내부용. 예를 들어 C에서는 나중에 설명할 C 라이브러리 함수 qsort와 같이 함수 포인터가 필요한 C 함수에 C 함수 포인터를 매개 변수로 전달할 수 있습니다.
두 변수의 크기를 비교하려면 COMPARE 유형 함수 포인터가 필요하므로 정렬 함수는 이 함수 포인터를 호출하여 다양한 유형의 변수를 비교할 수 있으므로 qsort는 다양한 유형의 변수 배열을 정렬할 수 있습니다.
이 애플리케이션을 "내부에서 내부 함수 포인터 호출"이라고 부르겠습니다.
3. 외부 함수 호출
API를 사용하여 외부 함수를 호출하는 것이 아닌가요? 예, 하지만 때로는 외부 함수의 포인터를 직접 가져와야 하는 경우도 있습니다. 예를 들어 LoadLibrary를 통해 DLL을 동적으로 로드한 다음 GetProcAddress를 통해 필요한 함수 항목 포인터를 가져온 다음 이 함수 포인터를 통해 외부 함수를 호출하면 DLL을 동적으로 로드하는 기술을 통해 외부 함수를 보다 유연하게 호출할 수 있습니다.
우리는 이 메소드를 "내부에서 외부 함수 포인터 호출"이라고 부릅니다.
4. 말할 필요도 없이 "외부에서 외부 함수 포인터 호출"을 제어할 수도 있습니다. 아니요, 예를 들어 여러 DLL을 로드하고 한 DLL의 함수 포인터를 다른 DLL의 함수에 전달할 수 있습니다.
위의 "내부" 및 "외부" 구분은 모두 상대적인 용어입니다(DLL은 실제로 아직 프로세스 내에 있습니다). 이 분류는 향후 문제를 논의하는 데 도움이 됩니다. 향후 기사에서도 이 분류를 사용할 것이기 때문입니다. 문제를 분석합니다.
함수 포인터의 사용은 위의 네 가지 방법에 지나지 않습니다. 그러나 실제 사용에서는 유연하고 변경 가능합니다. 예를 들어 C의 상속과 다형성, COM의 인터페이스는 모두 vTable이라는 함수 포인터 테이블을 영리하게 응용한 것입니다. 함수 포인터를 사용하면 프로그램 처리를 더욱 효율적이고 유연하게 만들 수 있습니다.
첫 번째 방법을 제외하고 VB 문서에서는 다른 방법을 소개하지 않으며 "Basic to Basic" 함수 포인터가 지원되지 않는다고 명시되어 있습니다(즉, 위에서 언급한 두 번째 방법은 실제로 특정 HACK을 통해 발생합니다. 위의 네 가지 방법을 모두 달성할 수 있습니다. 오늘은 두 번째 방법을 구현하는 방법을 살펴보겠습니다. 구현이 비교적 간단하기 때문에 간단한 것부터 시작하겠습니다. VB에서 외부 함수 포인터를 호출하는 방법과 VB에서 vTable 인터페이스 함수 포인터 점프 테이블을 처리하여 다양한 함수 포인터를 영리하게 적용하는 방법에 대해서는 COM의 내부 원리와 관련되므로 다른 기사에서 자세히 설명하겠습니다. .
실제로 VB 문서는 잘못된 것이 아닙니다. VB는 "Basic to Basic" 함수 포인터를 지원하지 않지만 이를 달성하기 위해 우회적인 방법, 즉 먼저 "Basic to API"를 만든 다음 첫 번째 방법을 사용할 수 있습니다. "외부" 내부 함수 포인터를 호출하여 "API에서 BASIC으로" 이동하여 "기본에서 기본으로" 두 번째 방법의 목적을 달성합니다. 이 기술을 VB에서만 사용할 수 있는 "강제 콜백"이라고 부를 수 있습니다. 기술.
다소 복잡하지만 창 하위 클래스 파생 기술의 CallWindowProc에 대해 신중하게 생각해 보세요. CallWindowProc를 사용하여 외부 운영 체제에서 원래 저장된 창 함수 포인터를 강제로 호출하도록 할 수도 있습니다. 함수 포인터.
하하, 아까부터 원리 얘기보다는 동작 얘기를 더 많이 하자고 했잖아요.
C와 마찬가지로 다중 키워드 비교를 지원하는 VB에서 qsort를 구현한다고 가정해 보겠습니다. 전체 소스 코드는 이 기사의 지원 코드에서 찾을 수 있습니다. 여기서는 함수 포인터 응용 프로그램과 관련된 코드만 제공됩니다.
최종 qsort 문을 마지막으로 살펴보겠습니다.
위의 ArrayPtr은 정렬해야 하는 배열의 첫 번째 요소에 대한 포인터이고, nCount는 배열의 요소 수, nElemSize는 각 요소의 크기, pfnCompare는 비교 함수 포인터입니다. 이 선언은 C 라이브러리 함수의 qsort와 매우 유사합니다.
C와 마찬가지로 Basic의 함수 포인터를 Basic의 qsort 함수에 전달할 수 있습니다.
그것을 사용하는 방법:
똑똑한 친구들, 여기서 미스터리를 이미 보셨나요? 테스트로서, 이제 qsort에서 함수 포인터를 사용하는 방법을 알려주실 수 있나요? 예를 들어, 이제 함수 포인터를 호출하여 배열의 i번째 요소와 j번째 요소의 크기를 비교해야 합니다.
예, 물론 강제 콜백을 수행하려면 이전에 선언한 Compare API(실제로는 CallWindowProc)를 사용해야 합니다.
구체적인 구현은 다음과 같습니다.
동작이 소개되었습니다. 이해하셨나요? 위에서 비교의 의미를 간략하게 설명하겠습니다. 이는 CallWindowProc API를 매우 영리하게 사용합니다. 이 API에는 5개의 매개변수가 필요합니다. 첫 번째 매개변수는 일반 함수 포인터입니다. 이 API는 함수 포인터를 즉시 콜백하고 이 API의 마지막 4개 Long 유형 매개변수를 함수 포인터가 가리키는 함수에 전달할 수 있습니다. CallWindowProc API에서는 전달된 함수 포인터가 WndProc 함수 프로토타입을 준수해야 하기 때문에 비교 함수에 4개의 매개변수가 있어야 하는 이유가 바로 이 때문입니다. WndProc의 프로토타입은 다음과 같습니다.
위의 LRESULT, HWND, UINT, WPARAM 및 LPARAM은 모두 VB의 Long 유형에 해당할 수 있습니다. Long 유형을 포인터로 사용할 수 있기 때문에 정말 좋습니다!
작업 흐름을 다시 살펴보겠습니다. AddressOfCompareSalaryName을 함수 포인터 매개변수로 사용하여 qsort를 호출하면 qsort의 형식 매개변수 pfnCompare에 실제 매개변수 CompareSalaryName의 함수 포인터가 할당됩니다. 현재 pfnCompare에 대한 콜백을 강제하기 위해 Compare를 호출하는 것은 다음 VB 문을 호출하는 것과 같습니다.
매개변수 유형 불일치 오류가 발생하지 않나요? CompareSalaryName의 처음 두 매개 변수가 TEmployee 유형이 아닌가요? 실제로 VB에서는 그러한 호출이 불가능합니다. 왜냐하면 VB의 유형 검사는 그러한 호출을 허용하지 않기 때문입니다. 그러나 이 호출은 실제로는 API에 의해 이루어진 콜백이고, VB에서는 API 콜백 함수의 매개변수 유형이 일반 Long 숫자 유형인지 구조체 포인터인지 확인하는 것이 불가능하므로, 유형 검사를 위해 이 Long 매개변수를 어떤 유형의 포인터로든 선언할 수 있습니다. 따라서 이 기술을 주의 깊게 사용해야 합니다. 예를 들어, 위의 CompareSalaryName 함수에 최종적으로 전달되는 매개변수 "ArrayPtr (i-1)*nElemSize"는 단지 주소일 뿐이며 항상 이 주소를 확인하지는 않습니다. 주소는 실수로 "ArrayPtr"로 사용된 경우 TEmployee 유형의 포인터로 처리됩니다. i*nElemSize"이면 i가 마지막 요소일 때 메모리 접근 오류가 발생하므로 C에서 포인터를 다룰 때와 마찬가지로 경계 문제에 주의해야 합니다.
함수 포인터의 영리한 적용은 여기에서 이미 볼 수 있지만 여기에 소개된 방법에는 여전히 큰 제한이 있습니다. 우리 함수에는 4개의 매개변수가 있어야 합니다. 보다 깔끔한 접근 방식은 VC 또는 Delphi에서 DLL을 작성하고 함수를 구현하기 위해 보다 호환되는 API를 만드는 것입니다. CallWindowProc과 유사합니다. CallWindowProc의 내부 구현을 추적했습니다. 이는 우리 응용 프로그램에서 중복되는 창 메시지와 관련된 많은 작업을 수행합니다. 실제로 강제 콜백 API를 구현하려면 마지막 몇 개의 매개변수를 스택에 푸시한 다음 첫 번째 매개변수(단지 몇 개의 어셈블리 명령)를 호출하면 됩니다.
위에서 언급한 세 번째 함수 포인터 호출 메서드를 구현하기 위해 외부 함수 포인터를 호출하는 데 사용할 수 없는 것은 바로 CallWindowProc의 제한 때문입니다. 세 번째 방법을 구현하려면 Master MattCurland가 악몽 같은 HACK 방법을 제공했습니다. VB에서 IUnknown 인터페이스를 구성하고 IUnknown 인터페이스의 vTable의 원래 세 항목 뒤에 새 항목을 추가해야 합니다. 새로운 항목 여기에 기계어 코드를 삽입하십시오. 이 기계어 코드는 우리가 제공한 함수 포인터를 최종적으로 호출하기 전에 이 포인터를 처리해야 합니다. 이 함수 포인터는 내부이든 외부이든 문제가 없습니다. COM 내부를 살펴볼 때 이 방법을 다시 다루겠습니다.
게다가, 정렬 알고리즘은 의견의 문제입니다. 원래 이 글에서는 가장 다재다능하고 최고의 성능을 지닌 알고리즘을 제공하고 싶었습니다. 비록 이 아이디어는 좋지만 모든 상황에 "최고의" 알고리즘은 없습니다. 본 글에서 제공하는 다양한 포인터 기술을 이용하여 구현한 퀵 정렬 방식은 객체 기술을 이용하여 동일한 기능을 구현하는 것보다 훨씬 빠르고 훨씬 적은 메모리를 차지할 것이다. 하지만 제가 최적화한 이 빠른 정렬 알고리즘도 ShellSort는 구현이 간단하기 때문에 여전히 ShellSort만큼 좋지 않습니다. 이론적으로 알고리즘에서 말하면 qsort는 ShellSort보다 더 나은 평균 성능을 가져야 하지만 VB에서는 반드시 그런 것은 아닙니다. 이 기사의 지원 코드를 볼 수 있으며 VBPJ 열 ShellSort의 지원 코드도 제공합니다. 이는 매우 좋습니다. , 그리고 이 글의 아이디어는 This ShellSort에서 따왔습니다.)
그러나 Quick Sort와 ShellSort를 구현하려면 데이터를 복사하기 위해 CopyMemroy를 광범위하게 사용해야 하기 때문에 여기서는 Quick Sort와 ShellSort 모두 크게 향상될 수 있다는 점을 지적해야 합니다(이는 VB에서 포인터를 사용할 때의 단점 중 하나입니다). 실제로 COM 자동화에서 SafeArray인 VB의 배열 구조를 해킹하는 더 좋은 방법이 있습니다. CopyMemroy가 없으면 SafeArray의 각 배열 요소 포인터를 한 번에 긴 배열에 넣을 수 있습니다. SafeArray 배열 요소 포인터를 실시간으로 교환하려는 목적을 달성하기 위해 Long 배열의 요소를 교환합니다. 데이터는 이동되지 않고 포인터만 이동되는 속도가 얼마나 빠른지 상상할 수 있습니다.
->