以下是引用cssnet在2022-3-16 10:53:44的发言:
其实当时我说的指针,指的是回调函数指针。
一般若遇太过复杂的Windows API调用,在VFP中我会选择直接放弃。
倘若有可能,则会在VC写的DLL中新增一个函数,用C处理好,然后返回结果让VFP程序直接使用。
如此一来,就不必非要勉强VFP去处理它本就不擅长的、或处理起来相当困难、相当笨拙的、涉及到Windows系统核心部分的API调用。
当然,这就要求VFPer应当具备最起码的VC编程基础。
不过,既然已经要用到比较复杂的Api了,那么掌握C应当也算是最起码的程序员基本功罢?
有时有些事情不好分得清高低,曾经有个人去看医生,问医生他身高算不算正常,医生说:脚能着地就正常。
也试试VFP的“回调函数指针”问题
只贴主要代码,打包下载
程序代码:
**
** StructCalss_DirDialog.prg
**
#INCLUDE StructCalss.h
SET PROCEDURE TO StructCalss.prg ADDITIVE
LoadApi()
? myGetDir("c:\temp\test")
SET PROCEDURE TO
CLEAR ALL
RETURN
FUNCTION myGetDir(cDefDir)
LOCAL bi, fa, pDir, pDefDir, cDir, pci, pit
pDir = apiMalloc(MAX_PATH)
pDefDir = myStrptr(cDefDir)
fa = CREATEOBJECT("FUNCTIONADDRESS")
bi = CREATEOBJECT("BROWSEINFOA")
bi.setValue("pszDisplayName", pDir)
bi.setValue("lpfn", fa.GetFunAddr("CallbackProc",4))
bi.setValue("lParam", pDefDir)
pit = apiSHBrowseForFolder(bi.pBuffer)
cDir = ""
IF pit > 0
apiSHGetPathFromIDList(pit, pDir)
cDir = SYS(2600, pDir, apiStrlen(pDir))
apiCoTaskMemFree(pit)
ENDIF
apiFree(pDefDir)
apiFree(pDir)
RETURN cDir
ENDFUNC
** 回调函数
FUNCTION CallbackProc(hWnd, Msg, wParam, lParam)
IF Msg == BFFM_INITIALIZED
* BFFM_SETSELECTIONA 消息自动选择初始路径
apiSendMessage(hWnd, BFFM_SETSELECTIONA, BFFM_INITIALIZED, lParam)
ENDIF
RETURN 0
ENDFUNC
* 获取调用VFP函数的函数指针
DEFINE CLASS FUNCTIONADDRESS as Session
DIMENSION aPFunction[1]
hUser32 = 0
fMessageBox = 0
pMsg = 0
pTitle = 0
hMSvcrt = 0
fSwprintf = 0
hOleaut32 = 0
fSysAllocString = 0
fSysFreeString = 0
pFunName = 0
PROCEDURE Init
this.hUser32 = apiLoadLibrary("user32.dll")
this.fMessageBox = apiGetProcAddress(this.hUser32, "MessageBoxA")
this.pMsg = myStrptr("调用 FUNCTIONADDRESS 类 GetFunAddr() 失败")
this.pTitle = myStrptr("提示")
this.hMSvcrt = apiLoadLibrary("msvcrt.dll")
this.fSwprintf = apiGetProcAddress(this.hMSvcrt, "swprintf")
this.hOleaut32 = apiLoadLibrary("oleaut32.dll")
this.fSysAllocString = apiGetProcAddress(this.hOleaut32, "SysAllocString")
this.fSysFreeString = apiGetProcAddress(this.hOleaut32, "SysFreeString")
ENDPROC
PROCEDURE Destroy
LOCAL pf
FOR EACH pf IN this.aPFunction
apiFree(pf)
ENDFOR
apiFree(this.pMsg)
apiFree(this.pTitle)
apiFree(this.pFunName)
apiFreeLibrary(this.hUser32)
apiFreeLibrary(this.hMSvcrt)
apiFreeLibrary(this.hOleaut32)
ENDPROC
FUNCTION GetFunAddr(cFunName, nParameters) && 函数名,参数个数
LOCAL nCount, cCode, cMsgCode
IF EMPTY(cFunName)
RETURN 0
ENDIF
* 格式:函数名(%d,%d,......%d,%d)
cFunName = STRCONV(cFunName + "(%d" + REPLICATE(",%d", nParameters - 1) + ")"+0h00, 5)
this.pFunName = myStrptr(cFunName)
* 出错提示
cMsgCode = ""
cMsgCode = cMsgCode + 0h6A00 && push 0 MessageBox()第4个参数
cMsgCode = cMsgCode + 0h68 + BINTOC(this.pTitle,"4RS") && push pTitle MessageBox()第3个参数
cMsgCode = cMsgCode + 0h68 + BINTOC(this.pMsg, "4RS") && push pMsg MessageBox()第2个参数
cMsgCode = cMsgCode + 0h6A00 && push 0 MessageBox()第1个参数
cMsgCode = cMsgCode + 0hB8 + BINTOC(this.fMessageBox,"4RS") && move eax, fMessageBox
cMsgCode = cMsgCode + 0hFFD0 && call MessageBox
cMsgCode = cMsgCode + 0hB801000000 && mov eax,00000001h 返回1(.T.)
*
* 函数的二进制代码
*
* 保存栈顶指针 ebp <- esp, 调整栈顶指针esp <- (esp-2000)预留足够栈空间
cCode = 0h55 && PUSH EBP 保存ebp
cCode = cCode + 0h8BEC && MOV EBP,ESP 栈顶指针保存到ebp作为栈基址
cCode = cCode + 0h81EC + BINTOC(2000, "4RS") && SUB ESP,000007D0h 预留2000字节足够栈空间
* swprintf(pOut, "<VFP函数名>(%d,%d,...,%d,%d)", ect1, ect2,..., ectn-1, ectn)
* 调用swprintf(pOut, pFmt, ect),将输出参数 pOut 的内容作为VFP函数表达式
* pOut,输出,指向VFP函数表达式的指针, 表达式: "<VFP函数名>(<数值1>,<数值2>,...,<数值n>)"
* pFmt,输入,指向VFP函数格式字符串的指针, 表达式: "<VFP函数名>(%d,%d,...,%d)"
* ect, 输入,参数表,<数值1>,<数值2>,...,<数值n>
*
* 函数的输入参数作为swprintf()的ect参数,输出转换为VFP函数的输入参数
* 参数入栈代码
FOR i = nParameters TO 1 STEP -1
cCode = cCode;
+ 0h8B45 + CHR(4 + i * 4);
+ 0h50
ENDFOR
* ectn
* ...... ectn-1
*
* 0h8B450C && MOV EAX, DWORD PTR [EBP+0C] ect2
* 0h50 && PUSH EAX
* 0h8B4508 && MOV EAX, DWORD PTR [EBP+08] ect1
* 0h50 && PUSH EAX
cCode = cCode + 0hB8 + BINTOC(this.pFunName, "4RS") && MOV EAX, pVFP函数 pFmt
cCode = cCode + 0h50 && PUSH EAX
cCode = cCode + 0h8D45A0 && LEA EAX, [EBP-60h] pOut
cCode = cCode + 0h50 && PUSH EAX
cCode = cCode + 0hB8 + BINTOC(this.fSwprintf, "4RS") && MOV EAX, fSwprintf
cCode = cCode + 0hFFD0 && CALL EAX 调用swprintf()
* 因 swfprintf() 不会自动恢复堆栈指针
* esp 恢复到调用swfprintf()前状态,esp <- esp + (n参数个数+2)*4)
cCode = cCode + 0h83C4 + CHR((nParameters + 2) * 4) && ADD ESP,(n参数个数+2)*4)
* [EBP-10h] = SysAllocString([EBP-60h])
* 调用SysAllocString()将VFP函数表达式字符串转换成BSTR字符串
cCode = cCode + 0h8D45A0 && LEA EAX,[EBP-60h] 指向VFP函数表达式字符串指针
cCode = cCode + 0h50 && PUSH EAX 作为SysAllocString()输入参数
cCode = cCode + 0hB8 + BINTOC(this.fSysAllocString, "4RS") && MOV EAX, fSysAllocString
cCode = cCode + 0hFFD0 && CALL EAX 调用SysAllocString()
* 保存SysAllocString()返回的BSTR字符串指针,之后由SysFreeString()释放
cCode = cCode + 0h8945F0 && MOV [EBP-10h],EAX
* _VFP.DoCmd(<VFP函数表达式>)
* 转换成BSTR字符串的VFP函数表达式, 作为调用_VFP对象DoCmd方法的输入参数
cCode = cCode + 0h50 && PUSH EAX
cCode = cCode + 0hB8 + BINTOC(SYS(3095, _VFP), "4RS") && MOV EAX, SYS(3095, _VFP) 获取_VFP对象的IDispatch指针
cCode = cCode + 0h50 && PUSH EAX _VFP指针
cCode = cCode + 0h8B00 && MOV EAX,[EAX] 获取_VFP的函数表指针
cCode = cCode + 0h0584000000 && ADD EAX,00000084h _函数表指针偏移 84h 是_VFP.DoCmd函数的指针
cCode = cCode + 0hFF10 && CALL [EAX] 调用_VFP.DoCmd(<VFP函数表达式>)
**cCode = cCode + 0hB800000000 && MOV EAX,00000000h eax <- 0 (DoCmd()返回NULL)
* 是否成功返回0
cCode = cCode + 0h83F800 && CMP EAX, 0
* 是,成功返回,跳过出错提示。
cCode = cCode + 0h74 + CHR(LEN(cMsgCode)) && je len(cMsgCode), je=0h74(等于), jne=0h75(不等于)
cCode = cCode + cMsgCode && 出错提示代码
* SysFreeString([EBP-10h])
* 释放由SysAllocString()分配生成BSTR字符串的空间
cCode = cCode + 0h8B45F0 && MOV EAX,[EBP-10h]
cCode = cCode + 0h50 && PUSH EAX
cCode = cCode + 0hB8 + BINTOC(this.fSysFreeString, "4RS") && MOV EAX, fSysFreeString
cCode = cCode + 0hFFD0 && CALL EAX 调用SysFreeString()
cCode = cCode + 0hB801000000 && MOV EAX,00000001h 返回1(.T.)
**cCode = cCode + 0hB800000000 && MOV EAX,00000000h 返回0(.F.)
cCode = cCode + 0h8BE5 && MOV ESP,EBP 恢复栈顶指针
cCode = cCode + 0h5D && POP EBP 恢复ebp
* 返回,按“堆栈平衡”原则,被调用者把堆栈指针修正到调用前的状态。
* RET n参数个数*4,即 esp <- (esp + n参数个数*4),因调用者首先把参数压入堆栈
cCode = cCode + 0hC2 + BINTOC(nParameters * 4, "4RS") && RET n参数个数*4
nCount = ALEN(this.aPFunction) + IIF(!EMPTY(this.aPFunction[1]), 1, 0)
DIMENSION this.aPFunction[nCount] && 函数指针数组
this.aPFunction[nCount] = apiMalloc(LEN(cCode)) && 分配函数代码内存空间
SYS(2600, this.aPFunction[nCount], LEN(cCode), cCode) && 载入函数代码
RETURN this.aPFunction[nCount] && 返回函数指针
ENDFUNC
ENDDEFINE
[此贴子已经被作者于2022-3-20 14:57编辑过]