自己實(shí)現(xiàn)IDispatch::Invoke方法因?yàn)榉N種原因,在只能得到一個(gè)IWebBrowser指針的情況下要接收javascript的window.external.XXX調(diào)用, 不得已自己實(shí)現(xiàn)了IDocHostUIHandler和IDispatch,為了使用方便,自己又需要實(shí)現(xiàn)類似MFC的DISPATCH_MAP: 首先用一個(gè)結(jié)構(gòu)體保存每個(gè)DISPATCH方法的ID,名字,this指針,函數(shù)地址,返回值類型,參數(shù)類型列表。這些都沒(méi)有什么難度。 然后在IDispatch::GetIDsOfNames里面通過(guò)方法名稱查找到方法ID返回。 最關(guān)鍵的是IDispatch::Invoke方法的實(shí)現(xiàn),怎么實(shí)現(xiàn)可變參數(shù)調(diào)用。 一、根據(jù)Invoke方法的參數(shù)與對(duì)應(yīng)的保存的結(jié)構(gòu)體信息對(duì)參數(shù)個(gè)數(shù),參數(shù)類型,返回值類型做安全校驗(yàn)。 二、因?yàn)楸4娴暮瘮?shù)地址指針,沒(méi)法確實(shí)函數(shù)原型(和MFC的DISPATCH_MAP一樣,參數(shù)個(gè)數(shù)、類型和返回值類型都是不定的), 只好想用匯編的call指令來(lái)直接調(diào)用,但參數(shù)入棧又是一個(gè)難題。 三、后來(lái)想了一個(gè)辦法,因?yàn)閂C++里面標(biāo)準(zhǔn)__thiscall調(diào)用約定,會(huì)按順序把參數(shù)壓入棧,把this指針寫入ECX寄存器, 所以我先根據(jù)參數(shù)類型把所有參數(shù)序列化到一個(gè)buffer里,得到所有參數(shù)總長(zhǎng)度size,然后在棧上分配長(zhǎng)度為size的空間,把buffer直接copy到分配好的棧里。然后直接用call指令調(diào)用函數(shù),這其中還有要注意的一些東西,一是原寄存器的保存,及調(diào)用后的還原。二是在分配??臻g后,由于修改了ESP,不能在后面的匯編指令里直接使用原來(lái)的臨時(shí)變量了,需要在分配??臻g前把這些需要用到的變量存到寄存器。三是保持棧平衡,寄存器入棧、出棧,分配的??臻g釋放等。 四、返回值的獲取,32位返回值會(huì)保存在EAX里,64位返回值分別在EDX和EAX保存高低32位,我的做法是直接用一個(gè)64位變量取出EDX和EAX值,然后根據(jù)函數(shù)實(shí)際的返回值類型取對(duì)應(yīng)的8/32/64位,因?yàn)樵趨R編里做這些判斷是很麻煩的,而這個(gè)取EDX和EAX的值對(duì)程序又不會(huì)有影響。 下面是關(guān)鍵代碼:
|
|