标题:VFP封装结构类型示例
取消只看楼主
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
其实VFP写的ASM码也是用几个API来处理,调用VFP的COM接口运行VFP函数。难道win7与win10的ASM码指令或寻址方式有所不同。


2022-03-20 13:55
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
以下是引用csyx在2022-3-20 23:46:38的发言:

有位前辈跟我说,不必浪费时间再造轮子。他老人家说 vfp2c32 中就有个函数叫 CreateCallbackFunc,这个 fll 可以到 github 上去搜索下载


一个用类来表达“结构类型”的话题,引来不少编程方面的讨论,感到有点意外和惊喜。

首先非常感谢 csyx 的关注和提出的问题,虽话语不多,但寓意深远。所以,很有必要对此问题展开一下讨论。

先说些看似与编程无相关的闲话,放松一下心情。再就编程方面说说自己的观点和看法。有点啰嗦,考验你的耐性了。

首先提到这位“老人家”,有话说“不听老人言,吃亏在眼前”,老人家的话一定要听进去,尤其是长辈的话。

记得做学生哥时听过一个关于两个数学家的故事,这两位数学家叫什么名字记不起来了,权当其中一位叫“高斯”吧:话说很久以前,有位数学家为解开一道数学难题,花了整整40年,难题终于被解开了,他兴奋之余,将解题过程和答案整理好寄给大数学家高斯。高斯收阅后即时回信,那位数学家收到高斯的回信,打开看后即时倒地不起了。据说回信内容是:我早在40多年前就解开这道难题了。这个故事大概内容就是这样,一笑过之。

相信不少FOX友是在新中国的前30年出生读书长大的,记得那句“造船不如买船,买船不如租船”被批判的洋奴哲学吗;还记得这30年之后的改革开放初期,曾经飞越过青藏高原的“运10”大飞机制造厂下马改造波音飞机的零部件;更还有的是又过30年之后,自己的大飞机又出厂起飞了,真是30年河东,30年河西。

法无定法,讨论问题不持肯定或否定态度。

“不听老人言,吃亏在眼前”,此言用在编程方面而论,这个 fll 会起到立杆见影的效果,搞钱来得快,不吃眼前亏。但是不是有 fll 用就可以安枕无忧、搞钱无尽呢? 如果是商品化的官方的 FLL 有保障性就好,否则就难保证出来的软件生命力有多长久,甚至是来历不明的 FLL,更有可能存在安全方面的问题。开发软件,这些问题都要得到重视,否则由此造成损失怕到时欲哭无泪。

经常也会见到使用 FLL 的人提起:以前用得好好的,系统更新了就不好使了。这是很正常的事,操作系统也没有百分百的兼容。但问题是此时一定要找到新版本的 FLL 支持,否则就如上术据说“欲哭无泪”,损失真是可大可小的。所以,在个人的立场上说,在欣赏人家美丽 FLL 的同时也要学点化妆技术来不断妆扮好自己,当感觉自己妆扮得比人家更美丽时,那种满足感不是能用时间或金钱换得来的。所以,“不必浪费时间再造轮子”,也是有局限性的,就如“运10”下马是为更快搞到钱,使老百姓先富起来,是历史的必然,好为现在大飞机重上天的技术储备提供支持。

说到写 Callback ,记得是好几年前了,在某论坛发表过一个贴是有关VFP多线程问题的,某论坛现在好象连自己发表的贴也查看不到了,是什么时候发表的无从证实。当时是用汇编写了个简单的函数接口给VFP作为线程过程接口来使用VFP的COM(支持多线程的COM)来运行VFP代码,使VFP也有多线程的能力。老实讲,这个多线程只是停在示例上,还真没实际应用过,因为没遇上必要性的机会。

VFP混合编程,目前常见类型有 DLL、FLL 和通过COM接口、脚本接口,如 JS、VBS 和 VBA 等。现在编程可真是门派林立,胶水式的编程语言成后起之秀,真是要集各门派之长方可成武林盟主。

无论是 DLL、FLL 还是之前回复的在VFP过程嵌入ASM,说到底原理都是一样的,都是在VFP进程里载入DLL、FLL、ASM的代码。也许有人当初认为在VFP过程写ASM有点不理解,我也说是另类,皆因写VFP的对ASM有兴趣的极少。编程方面肯定要优先考虑用DLL或FLL,易读好理解易调试好用开发高效;在PRG里体现ASM好处只有一个,短小精干,明码示众,源码开放,无安全性问题,缺点就不说了,就一句“乜都难”。





[此贴子已经被作者于2022-3-21 14:41编辑过]

2022-03-21 12:17
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
以下是引用cssnet在2022-3-21 14:55:41的发言:
大佬啊,vfp2c32是开源的!我觉得,与其一门心思地、执拗地、不管不顾地、大无畏地、一条道走到黑地……斋用VFP去辛辛苦苦摸索、实现,莫如直接将vfp2c32的CreateCallbackFunc源代码参参透,哪怕单独将这一小块抽离出来,写一个开源的Callback.fll,那也好过啊!

是吧,亲。

首先声明一点,从未针对 vfp2c32 说过什么,提到的 FLL 不是针对 vfp2c32,只是对 FLL 这东东提出自己的观点,这有何不妥?

重在理解,希望你重温我写的东东,注意不要断章取意就好。

VFP多语言编程有何不好? 我之前也有强调过,用在其他语言学到的东西应用到VFP里,这算是“一条道走到黑地……斋用VFP去辛辛苦苦摸索”吗? 这不是VFP论坛吗?不是重点探讨VFP编程的地方吗? 对你的理解表示遗憾!

如果不想讨论VFP的问题,可以到其他版块讨论,我可以陪同参与,那个版块也行,反正我是来学习的,编程是我的爱好。但有个小小要求:既然是讨论编程问题,尽量少用文字长篇大论,多用代码来表达体现编程的思想,这样可以高效地探讨交流编程心得。

自己开的贴啰嗦多几句也无可奈何,回复参与人的问题是态度问题,尽量抽空回敬。


2022-03-21 15:28
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
在BCCN有点看不明,VFP这个老古董的版块热度还真不小,最多人参与讨论实际问题的。曾经在C版块呆过一段时间,觉得连这C大佬的地盘讨论和解决实际问题的机会极少,多是出题目考人家的。
2022-03-21 15:37
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
以下是引用cssnet在2022-3-21 15:22:25的发言:

完啦!vfp2ccallback.cpp + vfp2ccallback.h,光代码就有1300+行,看着眼睛矇、脑壳疼——果断放弃!
今时今日,于我而言:
VFP是玩具,不再是揾食架撑(生产工具)。

vfp真的极少必需要用到callback,API的callback通常都可以NULL,就算是多线程的线程过程,也有其他方法代替多线程。
既然在这提到 vfp to c 的 callback,就来点最精简示例代码参阅
vfpCallback_demo
vfpCallback_demo.rar (27.35 KB)

程序代码:
CLEAR
CLEAR ALL
cDefPath = ADDBS(JUSTPATH(SYS(16)))
SET DEFAULT TO (cDefPath)
DECLARE Integer EnumFontFamilies IN gdi32 As EnumFontFamiliesA Integer hdc ,String lpszFamily ,Integer lpEnumFontFamProc,Integer lParam
DECLARE INTEGER GetDC IN WIN32API INTEGER hwnd
DECLARE LONG vfpGetFunAddr IN vfpCallback long,string@,long
pCallBackFontProc = vfpGetFunAddr(SYS(3095,_VFP), "FontProc", 4)
EnumFontFamiliesA(GetDC(_vfp.hWnd),NULL,pCallBackFontProc,0)
CLEAR ALL
RETURN

FUNCTION FontProc
    lparameters lpelfe as long,lpntme as long,fonttype as integer, lparam as long
    *!*?lpelfe,lpntme,fonttype,lparam
    logfont=sys(2600,lpelfe,28+33)
    newtextmetric=sys(2600,lpntme,17*4+1)
    facename=alltrim(right(logfont,33))
    facename=substr(facename,1,at(0h00,facename)-1)
    ? facename
    return 1
ENDFUNC

程序代码:
/*
    vfpCallback.cpp
    链接库 ole32、oleaut32
*/
//#define DLLIMPORT __declspec(dllexport)

#include <windows.h>
#include <stdio.h>

#define BUFSIZE 2048

GUID iid_IDispatch = {0x00020400,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
GUID iid_NULL      = {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}};

IDispatch*  vfpIDispatch;
char        vfpFunname[BUFSIZE];
char        cmdbuf[BUFSIZE];

wchar_t* CharToWChar(const char *pChar, wchar_t *buf, size_t bufSize)
{
    size_t len = MultiByteToWideChar(CP_ACP, 0, pChar, strlen(pChar), NULL, 0);
    len = (bufSize<1 ? 0 : (len>bufSize-1 ? bufSize : len));
    MultiByteToWideChar(CP_ACP, 0, pChar, strlen(pChar), buf, len);
    buf[len] = '\0';
    return buf;
}

DISPID GetDispIDByName(IDispatch* This, const char* Name)
{
    LCID lcid = GetUserDefaultLCID();
    DISPID rgDispId;
    wchar_t pwStr[BUFSIZE];
    CharToWChar(Name, pwStr, BUFSIZE);
    BSTR rgszNames = SysAllocString(pwStr);
    HRESULT ret = This->GetIDsOfNames(iid_NULL,(LPOLESTR*)&rgszNames,1,lcid,&rgDispId);
    SysFreeString(rgszNames);
    if (ret == S_OK)
        return rgDispId;
    return 0;
}

BOOL Invoke(IDispatch* This, DISPID pid, DISPID* NamedArg, WORD wFlags, VARIANT *varg, UINT Args, VARIANT *var)
{
    LCID lcid = GetUserDefaultLCID();
    DISPPARAMS params = {NULL,NULL,0,0};
    if (varg != NULL)
    {
        params.rgvarg = varg;
        params.rgdispidNamedArgs = NamedArg;
        params.cArgs = Args;
         = (NamedArg ? 1 : 0);
    }
    return (This->Invoke(pid,iid_NULL,lcid,wFlags,&params,var,NULL,NULL)==S_OK);
}

BOOL InvokeMethod(IDispatch* This, const char* funName, VARIANT *varg, UINT Args, VARIANT *var)
{
    DISPID pid = GetDispIDByName(This, funName);
    if (pid < 0)
        return FALSE;
    return Invoke(This, pid, NULL, DISPATCH_METHOD, varg, Args, var);
}

LONG vfpDoCmd(IDispatch* vfpIDispatch, char* vfpCmd)
{
    char buffer[BUFSIZE];
    VARIANT var, params[10];
    LONG ret;
    //执行 _VFP 方法 DoCmd()
    params[0].vt      = VT_BSTR;
    params[0].bstrVal = SysAllocString(CharToWChar(vfpCmd, (wchar_t *)buffer, BUFSIZE));
    ret = InvokeMethod(vfpIDispatch, "DoCmd", params, 1, &var);
    SysFreeString(params[0].bstrVal);
    return ret;
}

BOOL CALLBACK vfpCallback(int p1,int p2,int p3,int p4)
{
    sprintf(cmdbuf, "%s(%d,%d,%d,%d)", vfpFunname,p1,p2,p3,p4);
    return vfpDoCmd(vfpIDispatch, cmdbuf);
}

extern "C" __declspec(dllexport) LONG vfpGetFunAddr(IDispatch* vfp, char* funname, BYTE params)
{
    vfpIDispatch = vfp;
    sprintf(vfpFunname, "%s", funname);
    return (LONG)vfpCallback;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
{
    return TRUE;
}

2022-03-22 20:26
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
对于FOX友初学C的用 DEV-CPP IDE 就基本可以满足要求。
用 DEV-CPP IDE 环境编译了一下,可以参考一下 Makefile.win 文件
程序代码:
# Project: vfpCallback
# Makefile created by Dev-C++ 5.11

CPP      = g++.exe
CC       = gcc.exe
WINDRES  = windres.exe
OBJ      = vfpCallback.o
LINKOBJ  = vfpCallback.o
LIBS     = -L"E:/__lyl/lyl/__C、C++/My Program/编程工具/Dev-Cpp/MinGW64/lib" -L"E:/__lyl/lyl/__C、C++/My Program/编程工具/Dev-Cpp/MinGW64/x86_64-w64-mingw32/lib32" -static-libgcc "E:/__lyl/lyl/__C、C++/My Program/编程工具/dev-cpp/mingw64/x86_64-w64-mingw32/LIB32/libole32.a" "E:/__lyl/lyl/__C、C++/My Program/编程工具/dev-cpp/mingw64/x86_64-w64-mingw32/LIB32/liboleaut32.a" -m32
INCS     = -I"E:/__lyl/lyl/__C、C++/My Program/编程工具/Dev-Cpp/MinGW64/include" -I"E:/__lyl/lyl/__C、C++/My Program/编程工具/Dev-Cpp/MinGW64/x86_64-w64-mingw32/include" -I"E:/__lyl/lyl/__C、C++/My Program/编程工具/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include"
CXXINCS  = -I"E:/__lyl/lyl/__C、C++/My Program/编程工具/Dev-Cpp/MinGW64/include" -I"E:/__lyl/lyl/__C、C++/My Program/编程工具/Dev-Cpp/MinGW64/x86_64-w64-mingw32/include" -I"E:/__lyl/lyl/__C、C++/My Program/编程工具/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include" -I"E:/__lyl/lyl/__C、C++/My Program/编程工具/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include/c++"
BIN      = vfpCallback.dll
CXXFLAGS = $(CXXINCS) -m32 -DBUILDING_DLL=1
CFLAGS   = $(INCS) -m32 -DBUILDING_DLL=1
RM       = rm.exe -f
DEF      = libvfpCallback.def
STATIC   = libvfpCallback.a

.PHONY: all all-before all-after clean clean-custom

all: all-before $(BIN) all-after

clean: clean-custom
    ${RM} $(OBJ) $(BIN) $(DEF) $(STATIC)

$(BIN): $(LINKOBJ)
    $(CPP) -shared $(LINKOBJ) -o $(BIN) $(LIBS) -Wl,--output-def,$(DEF),--out-implib,$(STATIC),--add-stdcall-alias

vfpCallback.o: vfpCallback.cpp
    $(CPP) -c vfpCallback.cpp -o vfpCallback.o $(CXXFLAGS)

2022-03-23 07:42
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
以下是引用cssnet在2022-3-23 18:15:18的发言:

我顶你个肺啊!高手一出马,就知有没有!
真的很精简易懂哇,看得我老人家都不头晕了!

本来想再精简些,考虑到VFP不同版本的兼容性,还是从VFP的标准接口做起。
黑,继续黑下去,有必要考虑一下 Callback 过程的返回值。
通常 Callback 过程返回一个非零值(TRUE),Callback过程继续工作。返回零(FALSE),则Callback过程停止工作。
怎样从VFP的Callback函数处理Callback事件后将返回值返回给C的Callback过程作为 C Callback 的返回值?
简单做法:在C的Callback过程用一个存放返回值(ret)的地址连同其他参数一起传给VFP的Callback函数,VFP的Callback函数返回时将返回值写入这个ret地址空间。
vfpCallback_demo.rar (27.57 KB)

VFP Callback
程序代码:
FUNCTION FontProc 
    lparameters pRet as long,lpelfe as long,lpntme as long,fonttype as integer, lparam as long
    *!*?lpelfe,lpntme,fonttype,lparam
    logfont=sys(2600,lpelfe,28+33)
    newtextmetric=sys(2600,lpntme,17*4+1)
    facename=alltrim(right(logfont,33))
    facename=substr(facename,1,at(0h00,facename)-1)
    ? CTOBIN(SYS(2600,pRet,1),"1RS"), facename
    ret = 0  && 0 or 1
    SYS(2600, pRet, 1, BINTOC(ret,"1RS"))
    return ret
ENDFUNC

C Callback
程序代码:
BOOL CALLBACK vfpCallback(int p1,int p2,int p3,int p4)
{
    BOOL ret = 1;
    sprintf(cmdbuf, "%s(%u,%d,%d,%d,%d)", vfpFunname,&ret,p1,p2,p3,p4);    //vfp函数格式:fun(pRet,p1,p2,p3,p4)
    vfpDoCmd(vfpIDispatch, cmdbuf);
    return ret;    //返回非零值(TRUE),Callback过程继续工作。返回零(FALSE),则Callback过程停止工作。
}




2022-03-25 08:39
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
回复 39楼 csyx
简单整了个选择文件名或文件夹的对话框,没你用的好看。
vfpGetfile.rar (26.64 KB)


程序代码:
cDefPath = ADDBS(JUSTPATH(SYS(16)))
SET DEFAULT TO (cDefPath)

DECLARE long strlen      IN msvcrt  as apiStrlen long
DECLARE long GetFileName IN vfpGetfile as apiGetFileName long,string@,string@

*
* 打开对话框
* GetFileName(hWnd, DefPath, Filter)
*
* 参数:hWnd ...... 所有者窗口句柄, 没有时为0。
*       DefPath ... 默认路径
*       Filter .... 文件类型格式过滤字符串。
*                   如文件格式选择列表:
*                       文本文件 ([color=#808080]*.txt)[/color]
*                       全部文件 ([color=#808080]*.*)[/color]
*                       Filter:"文本文件([color=#808080]*.txt)\0*.txt\0全部文件(*.*)\0*.*\0"[/color]
*
* 返回:返回选择的文件名或文件夹,“取消”或选择无效的文件名时返回空串。
*
cFilter = "文本文件(*.txt)"+0h0+"*.txt"+0h0+"全部文件(*.*)"+0h0+"*.*"+0h0
pFile = apiGetFileName(_screen.hWnd, "c:\temp\test", cFilter)
? SYS(2600, pFile, apiStrlen(pFile))

CLEAR ALL
RETURN


2022-03-29 12:14



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-508553-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.158655 second(s), 9 queries.
Copyright©2004-2025, BCCN.NET, All Rights Reserved