标题:VFP封装结构类型示例
只看楼主
radiofan
Rank: 7Rank: 7Rank: 7
等 级:贵宾
威 望:11
帖 子:466
专家分:696
注 册:2006-7-30
得分:0 
以下是引用吹水佬在2022-3-17 11:15:45的发言:

调用 WinApi 回避不了指针,有必要讨论一下指针的问题。

不少初接触指针的人,也感觉指针不好学、难把握好。可能是因为一开始接触的编程语言太高级,对指针感觉太抽象。想当年DOS时代也有用debug命令来写汇编生成com,没有什么变量名、数据类型等等高级货,全是与寄存器、地址打交道。建议学编程的一定要了解一下汇编,最起码能看得明白基本常用的代码。不论什么编程语言写出来的程序,机器码(汇编)是他的最终归宿。

指针,不管对新手还是老手来说,都有犯错的可能,这不奇怪,很正常的事。有个“C0000005”的异常代号应该不陌生,还有以前的Wndows蓝屏也是常事,这大都是指针的开放性不受约束带来的问题,编程的一不小心指针就会犯傻。所以,现在的高级编程语言也有不引入指针的概念,希望少犯傻。VFP也有涉及到指针的命令,如SYS(2600,,),这命令会有可能犯傻的,之前就有贴讨论过这问题。

凡事要一分为二看,不要因指针会犯傻就不用。指针是个好东东,有时没他整不出好东东来。之前不久有贴讨论过从网页获取量大的JSON数据问题,用VFP字符串命令解释出全部数据要80多秒,放入JS对象解释要几秒,调用API用指针解释不到1秒,就是传说中的“秒杀”。所以,必要时该出手就要出手。

学习了!
2022-03-22 16:00
吹水佬
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
schtg
Rank: 11Rank: 11Rank: 11Rank: 11
来 自:https://t.me/pump_upp
等 级:贵宾
威 望:67
帖 子:1355
专家分:2534
注 册:2012-2-29
得分:0 
@吹版,学习啦,非常感谢!
2022-03-23 05:58
吹水佬
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
cssnet
Rank: 4
等 级:业余侠客
威 望:4
帖 子:317
专家分:203
注 册:2013-10-4
得分:0 
以下是引用吹水佬在2022-3-22 20:26:34的发言:

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


我顶你个肺啊!高手一出马,就知有没有!
真的很精简易懂哇,看得我老人家都不头晕了!
2022-03-23 18:15
吹水佬
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.030766 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved