亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

VisualBasic變態用法之函數指針

2019-11-18 17:54:50
字體:
來源:轉載
供稿:網友
一、函數指針

  AddressOf得到一個VB內部的函數指針,我們可以將這個函數指針傳遞給需要回調這個函數的API,它的作用就是讓外部的程序可以調用VB內部的函數。

  但是VB里函數指針的應用,遠不象C里應用那么廣泛,因為VB文檔里僅介紹了如何將函數指針傳遞給API以實現回調,并沒指出函數指針諸多神奇的功能,因為VB是不鼓勵使用指針的,函數指針也不例外。

  首先讓我們對函數指針的使用方式來分個類。

  1、回調。這是最基本也是最重要的功能。比如VB文檔里介紹過的子類派生技術,它的核心就是兩個API:SetWindowLong和CallWindow
  函數指針的使用不外乎上面四種方式。但在實際使用中卻是靈活多變的。比如在C 里繼承和多態,在COM里的接口,都是一種叫vTable的函數指針表的巧妙應用。使用函數指針,可以使程序的處理方式更加高效、靈活。

  VB文檔里除了介紹過第一方式外,對其它方式都沒有介紹,并且還明確指出不支持“Basic到Basic”的函數指針(也就是上面說的第二種方式),實際上,通過一定的HACK,上面四種方式均可以實現。今天,我們就來看看如何來實現第二種方式,因為實現它相對來說比較簡單,我們先從簡單的入手。至于如何在VB內調用外部的函數指針,如何在VB里通過處理vTable接口函數指針跳轉表來實現各種函數指針的巧妙應用,由于這將涉及COM內部原理,我將另文詳述。

  其實VB的文檔并沒有說錯,VB的確不支持“Basic到Basic”的函數指針,但是我們可以繞個彎子來實現,那就是先從"Basic到API",然后再用第一種方式"外部調用內部的函數指針"來從"API到BASIC",這樣就達到了第二種方式從"Basic到Basic"的目的,這種技術我們可以稱之為"強制回調",只有VB里才會有這種古怪的技術。

  說得有點繞口,但是仔細想想窗口子類派生技術里CallWindowProc,我們可以用CallWindowProc來強制外部的
操作系統調用我們原來的保存的窗口函數指針,同樣我們也完全可以用它來強制調用我們內部的函數指針。

  呵呵,前面說過要少講原理多講招式,現在我們就來開始學習招式吧!

  考慮我們在VB里來實現和C里一樣支持多關鍵字比較的qsort。完整的源代碼見本文配套代碼,此處僅給出函數指針應用相關的代碼。

->'當然少不了的CopyMemory,不用ANY的版本。
DeclareSubCopyMemoryLib"kernel32"Alias_
"RtlMoveMemory"(ByValdestAsLong,ByValsourceAsLong,_
ByValnumBytesAsLong)

'嘿嘿,看下面是如何將CallWindowProc的聲明做成Compare聲明的。
DeclareFunctionCompareLib"user32"Alias_
"CallWindowProcA"(ByValpfnCompareAsLong,ByValpElem1AsLong,_
ByValpElem2AsLong,ByValunused1AsLong,_
ByValunused2AsLong)AsInteger
'注:ByValxxxxxAsLong,還記得吧!這是標準的指針聲明方法。

'聲明需要比較的數組元素的結構
PublicTypeTEmployee
 NameAsString
 SalaryAsCurrency
EndType

'再來看看我們的比較函數
'先按薪水比較,再按姓名比較
FunctionCompareSalaryName(Elem1AsTEmployee,_
     Elem2AsTEmployee,_
     unused1AsLong,_
     unused2AsLong)AsInteger
 DimRetAsInteger
 Ret=Sgn(Elem1.Salary-Elem2.Salary)
 IfRet=0Then
  Ret=StrComp(Elem1.Name,Elem2.Name,vbTextCompare)
 EndIf
 CompareSalaryName=Ret
EndFunction

'先按姓名比較,再按薪水比較
FunctionCompareNameSalary(Elem1AsTEmployee,_
     Elem2AsTEmployee,_
     unused1AsLong,_
     unused2AsLong)AsInteger
 DimRetAsInteger
 Ret=StrComp(Elem1.Name,Elem2.Name,vbTextCompare)
 IfRet=0Then
  Ret=Sgn(Elem1.Salary-Elem2.Salary)
 EndIf
 CompareNameSalary=Ret
EndFunction->

  最后再看看我們來看看我們最終的qsort的聲明。

->Subqsort(ByValArrayPtrAsLong,ByValnCountAsLong,_
ByValnElemSizeAsInteger,ByValpfnCompareAsLong)->

  上面的ArrayPtr是需要排序數組的第一個元素的指針,nCount是數組的元素個數,nElemSize是每個元素大小,pfnCompare就是我們的比較函數指針。這個聲明和C庫函數里的qsort是極為相似的。

  和C一樣,我們完全可以將Basic的函數指針傳遞給Basic的qsort函數。

  使用方式如下:

->DimEmployees(1To10000)AsTEmployee
'假設下面的調用對Employees數組進行了賦值初始化。
CallInitArray()
'現在就可以調用我們的qsort來進行排序了。
Callqsort(VarPtr(Employees(1)),UBound(Employees),_
LenB(Employees(1)),AddressOfCompareSalaryName)
'或者先按姓名排,再按薪水排
Callqsort(VarPtr(Employees(1)),UBound(Employees),_
LenB(Employees(1)),AddressOfCompareNameSalary)->

  聰明的朋友們,你們是不是已經看出這里的奧妙了呢?作為一個測驗,你能現在就給出在qsort里使用函數指針的方法嗎?比如現在我們要通過調用函數指針來比較數組的第i個元素和第j個元素的大小。

  沒錯,當然要使用前面聲明的Compare(其實就是CallWindowProc)這個API來進行強制回調。

  具體的實現如下:

->Subqsort(ByValArrayPtrAsLong,ByValnCountAsLong,_
ByValnElemSizeAsInteger,ByValpfnCompareAsLong)
 DimiAsLong,jAsLong
 '這里省略快速排序算法的具體實現,僅給出比較兩個元素的方法。
 IfCompare(pfnCompare,ArrayPtr (i-1)*nElemSize,_
   ArrayPtr (j-1)*nElemSize,0,0)>0Then
   '如果第i個元素比第j個元素大則用CopyMemory來交換這兩個元素。
 EndIF
EndSub->

  招式介紹完了,明白了嗎?我再來簡單地講解一下上面Compare的意思,它非常巧妙地利用了CallWindowProc這個API。這個API需要五個參數,第一個參數就是一個普通的函數指針,這個API能夠強馬上回調這個函數指針,并將這個API的后四個Long型的參數傳遞給這個函數指針所指向的函數。這就是為什么我們的比較函數必須要有四個參數的原因,因為CallWindowProc這個API要求傳遞給的函數指針必須符合WndProc函數原形,WndProc的原形如下:

->LRESULT(CALLBACK*WNDPROC)(HWND,UINT,WPARAM,LPARAM);->

  上面的LRESULT、HWND、UINT、WPARAM、LPARAM都可以對應于VB里的Long型,這真是太好了,因為Long型可以用來作指針嘛!

  再來看看工作流程,當我們用AddressOfCompareSalaryName做為函數指針參數來調用qsort時,qsort的形參pfnCompare被賦值成了實參CompareSalaryName的函數指針。這時,調用Compare來強制回調pfnCompare,就相當于調用了如下的VB語句:

->CallCompareSalaryName(ArrayPtr (i-1)*nElemSize,_
ArrayPtr (j-1)*nElemSize,0,0)->

  這不會引起參數類型不符錯誤嗎?CompareSalaryName的前兩個參數不是TEmployee類型嗎?的確,在VB里這樣調用是不行的,因為VB的類型檢查不會允許這樣的調用。但是,實際上這個調用是API進行的回調,而VB不可能去檢查API回調的函數的參數類型是一個普通的Long數值類型還是一個結構指針,所以也可以說我們繞過了VB對函數參數的類型檢查,我們可以將這個Long型參數聲明成任何類型的指針,我們聲明成什么,VB就認為是什么。所以,我們要小心地使用這種技術,如上面最終會傳遞給CompareSalaryName函數的參數"ArrayPtr (i-1)*nElemSize"只不過是一個地址,VB不會對這個地址進行檢查,它總是將這個地址當做一個TEmployee類型的指針,如果不小心用成了"ArrayPtr i*nElemSize",那么當i是最后一個元素時,我們就會引起內存越權訪問錯誤,所以我們要和在C里處理指針一樣注意邊界問題。

  函數指針的巧妙應用這里已經可見一斑了,但是這里介紹的方法還有很大的局限性,我們的函數必須要有四個參數,更干凈的做法還是在VC或Delphi里寫一個DLL,做出更加符合要求的API來實現和CallWindowProc相似的功能。我跟蹤過CallWindowProc的內部實現,它要做許多和窗口消息相關的工作,這些工作在我們這個應用中是多余的。其實實現強制回調API只需要將后幾個參數壓棧,再call第一個參數就行了,不過幾條匯編指令而已。

  正是因為CallWindowProc的局限性,我們不能夠用它來調用外部的函數指針,以實現上面說的第三種函數指針調用方式。要實現第三種方式,MattCurland大師提供了一個噩夢一般的HACK方式,我們要在VB里憑空構造一個IUnknown接口,在IUnknown接口的vTable原有的三個入口后再加入一個新入口,在新入口里插入機器代碼,這個機器代碼要處理掉this指針,最后才能調用到我們給的函數指針,這個函數指針無論是內部的還是外部的都一樣沒問題。在我們深入討論COM內部原理時我會再來談這個方法。

  另外,排序算法是個見仁見智的問題,我本來想,在本文提供一個最通用性能最好的算法,這種想法雖好,但是不可能有在任何情況下都“最好”的算法。本文提供的用各種指針技術來實現的快速排序方法,應該比用對象技術來實現同樣功能快不少,內存占用也少得多。可是就是這個已經經過了我不少優化的快速排序算法,還是比不了ShellSort,因為ShellSort實現上簡單。從算法的理論上來講qsort應該比ShellSort平均性能好,但是在VB里這不一定(可見本文配套代碼,里面也提供了VBPJ一篇專欄的配套代碼ShellSort,非常得棒,本文的思想就取自這個ShellSort)。

  但是應當指出無論是這里的快速排序還是ShellSort,都還可以大大改進,因為它們在實現上需要大量使用CopyMemroy來拷貝數據(這是VB里使用指針的缺點之一)。其實,我們還有更好的方法,那就是Hack一下VB的數組結構,也就是COM自動化里的SafeArray,我們可以一次性的將SafeArray里的各個數組元素的指針放到一個long型數組里,我們無需CopyMemroy,我們僅需交換Long型數組里的元素就可以達到實時地交換SafeArray數組元素指針的目的,數據并沒有移動,移動的僅僅是指針,可以想象這有快多。
->


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲欧美日韩精品久久奇米色影视| 日韩在线观看免费高清完整版| 国产精品久久久久久久久久久不卡| 日韩在线高清视频| 成人免费高清完整版在线观看| 亚洲性xxxx| 久久国产精品99国产精| 中文字幕在线亚洲| 日韩精品中文字幕久久臀| 国产精品女主播| 国产精品视频最多的网站| 日韩精品极品在线观看| 97香蕉久久超级碰碰高清版| 日韩精品免费视频| 国产精欧美一区二区三区| 国产精品精品一区二区三区午夜版| 欧美中文字幕精品| 亚洲国产91精品在线观看| 日本精品久久久久影院| 国产精品igao视频| 国产日韩在线观看av| 亚洲综合精品伊人久久| www.xxxx欧美| 成人国产精品免费视频| 久久99久久99精品免观看粉嫩| 色妞欧美日韩在线| 亚洲片国产一区一级在线观看| 日韩av在线播放资源| 久久久久久久久国产| 欧美第一淫aaasss性| 最近2019中文字幕在线高清| 欧美巨猛xxxx猛交黑人97人| 久久99热这里只有精品国产| 91丨九色丨国产在线| 国模私拍一区二区三区| 粗暴蹂躏中文一区二区三区| 国产美女扒开尿口久久久| 一区二区三区久久精品| 欧美精品性视频| 亚洲女人初尝黑人巨大| 国产精品xxxxx| 欧美性videos高清精品| 亚洲电影成人av99爱色| 成人欧美一区二区三区在线湿哒哒| 成人中心免费视频| 亚洲美女动态图120秒| 精品国产91乱高清在线观看| 国模极品一区二区三区| 成人激情av在线| 国产精品99导航| 欧美精品在线播放| 欧美理论片在线观看| 97婷婷大伊香蕉精品视频| 国产精品国产自产拍高清av水多| 亚洲精品成人免费| 91日韩在线播放| 日韩av综合中文字幕| 国产欧美日韩中文| 久久久久久久爱| 亚洲欧美精品suv| 亚洲色在线视频| 丁香五六月婷婷久久激情| 欧美黑人极品猛少妇色xxxxx| 亚洲第一福利网站| 亚洲欧美成人精品| 中文日韩电影网站| 中文字幕欧美日韩va免费视频| 欧美视频在线观看免费| 亚洲成人黄色在线| 欧美大学生性色视频| 国产香蕉精品视频一区二区三区| 国产成人aa精品一区在线播放| 91亚洲精品在线观看| 97人人模人人爽人人喊中文字| 欧美国产亚洲视频| 美女av一区二区| 一本大道亚洲视频| 黑人狂躁日本妞一区二区三区| 国产精品久久一区主播| 欧美大成色www永久网站婷| 亚洲精品wwww| 亚洲欧美精品中文字幕在线| 欧美最顶级的aⅴ艳星| 国产亚洲精品综合一区91| 国产成人jvid在线播放| 国产精品最新在线观看| 久久精品国产免费观看| 欧美精品videossex88| 日韩电影免费在线观看| 久久精品国产69国产精品亚洲| 麻豆精品精华液| 色噜噜狠狠狠综合曰曰曰| 欧美日韩精品在线观看| 亚洲伊人成综合成人网| 欧美午夜片欧美片在线观看| 另类少妇人与禽zozz0性伦| 国产精品27p| 亚洲mm色国产网站| 一区二区亚洲欧洲国产日韩| 日本一区二区三区四区视频| 亚洲欧美国产视频| 欧美在线视频观看免费网站| 亚洲精品videossex少妇| 国产精品久久久久影院日本| 久久久人成影片一区二区三区| 国产精品第一页在线| 日韩视频免费在线观看| 亚洲精品日产aⅴ| 亚洲最大福利视频| 性色av一区二区三区在线观看| 日本高清+成人网在线观看| 国产成人精彩在线视频九色| 久99久在线视频| 成人精品视频99在线观看免费| 国产原创欧美精品| 一区二区三区高清国产| 欧美激情精品久久久| 国产精品v日韩精品| 国产精品日韩在线播放| 日韩电影大片中文字幕| 国产成人精品免高潮在线观看| 欧美激情欧美激情在线五月| 日韩免费在线免费观看| 成人激情视频在线观看| 91精品久久久久久久久中文字幕| 日韩中文字幕免费| 国产精品午夜国产小视频| 亚洲精品成a人在线观看| 亚洲国产精品免费| 2019中文字幕全在线观看| 久久久久免费视频| 久久免费视频这里只有精品| 久久精品色欧美aⅴ一区二区| 成人妇女淫片aaaa视频| 国产精品美女久久| 国产精品麻豆va在线播放| 亚洲天堂网站在线观看视频| 福利一区福利二区微拍刺激| 日韩一区二区精品视频| 国产成人精品久久亚洲高清不卡| 美女啪啪无遮挡免费久久网站| 欧美激情在线播放| 久久精品国产精品| 国产99久久精品一区二区永久免费| 亚洲免费一在线| 成人免费xxxxx在线观看| 91精品国产777在线观看| 亚洲成人激情视频| 欧美专区在线播放| 国产精品亚洲网站| 日韩av在线导航| 久久青草精品视频免费观看| 中文国产亚洲喷潮| 久久久久久久久久久网站| 欧美另类在线观看| 日韩电影大全免费观看2023年上| 色噜噜国产精品视频一区二区| 97视频在线播放| 国产精品久久久久久亚洲调教| 91久久久在线| 另类视频在线观看| 欧美激情网友自拍| 亚洲国产欧美一区二区丝袜黑人| 深夜福利亚洲导航|