标题:VFP封装结构类型示例
只看楼主
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
结帖率:100%
已结贴  问题点数:20 回复次数:76 
VFP封装结构类型示例
近日,有贴谈到“调用Windows API时,如何正确地传递struct(结构体)参数”的问题。
连接:https://bbs.bccn.net/thread-508544-1-1.html

VFP调用API涉及到结构类型,通常是用字符串来表达结构类型成员数据,这方法看似简单,
但易读性差,很不好理解,尤其对初接触调用API的容易搞错。

封装结构类型的类,可提高VFP语言的表达能力,提高学习编程效率。

示例以 PARAFORMAT2 结构简单描述,对于复杂的结构体有待探讨


程序代码:
**    VFP封装结构类型示例
**    思路:
**    定义一个二维数组(aSTRUCT)用来描述结构体成员属性
**    每一行表达一个成员属性:名称(Name),类型(Type), 大小(Size),地址偏移量(Offset)
**    定义 STRUCT_ARRAY 类,提供初始化结构成员属性函数 stInit()
**    定义 STRUCT_CALSS 类,提供读写结构成员数据函数 getValue()、setValue()

DECLARE long malloc IN msvcrt as apiMalloc long
DECLARE long free IN msvcrt as apiFree long
DECLARE long SendMessage IN user32 as apiSendMessage long,long,long,long

of = CREATEOBJECT("form1")
of.show(1)
CLEAR ALL
RETURN

DEFINE CLASS form1 as Form 
    ADD OBJECT rich AS Olecontrol WITH top=10,left=10,width=200,height=200,OleClass="RICHTEXT.RichtextCtrl.1",visible=1
    PROCEDURE rich.init
        this.text = ""
        pf = CREATEOBJECT("PARAFORMAT2")
        st = CREATEOBJECT("STRUCT_CALSS", pf)
        st.setValue("cbSize", st.nSize)
        st.setValue("dwMask", 256)
        st.setValue("dyLineSpacing", 300)
        st.setValue("bLineSpacingRule", 4)
        #define WM_USER 0x0400
        #define EM_SETPARAFORMAT (WM_USER + 71)
        apiSendMessage(this.hWnd, EM_SETPARAFORMAT, 0, st.pBuffer)
        RELEASE st,pf
        this.text = "123456789"+0h0D0A+"abcdefghijk"+0h0D0A+"ABCDEFGHIJK"++0h0D0A
    ENDPROC
ENDDEFINE

DEFINE CLASS PARAFORMAT2 AS STRUCT_ARRAY
    PROCEDURE init
        DIMENSION this.aSTRUCT[24,4]
        this.stInit(1,  "cbSize",           "N",4)    && DWORD
        this.stInit(2,  "dwMask",           "N",4)
        this.stInit(3,  "wNumbering",       "N",2)    && WORD
        this.stInit(4,  "wEffects",         "N",2)
        this.stInit(5,  "dxStartIndent",    "N",4)
        this.stInit(6,  "dxRightIndent",    "N",4)
        this.stInit(7,  "dxOffset",         "N",4)
        this.stInit(8,  "wAlignment",       "N",2)
        this.stInit(9,  "cTabCount",        "N",2)
        this.stInit(10, "rgxTabs",          "C",128)  && DWORD[MAX_TAB_STOPS], MAX_TAB_STOPS=32
        this.stInit(11, "dySpaceBefore",    "N",4)
        this.stInit(12, "dySpaceAfter",     "N",4)
        this.stInit(13, "dyLineSpacing",    "N",4)
        this.stInit(14, "sStyle",           "N",2)
        this.stInit(15, "bLineSpacingRule", "N",1)    && BYTE
        this.stInit(16, "bOutlineLevel",    "N",1)
        this.stInit(17, "wShadingWeight",   "N",2)
        this.stInit(18, "wShadingStyle",    "N",2)
        this.stInit(19, "wNumberingStart",  "N",2)
        this.stInit(20, "wNumberingStyle",  "N",2)
        this.stInit(21, "wNumberingTab",    "N",2)
        this.stInit(22, "wBorderSpace",     "N",2)
        this.stInit(23, "wBorderWidth",     "N",2)
        this.stInit(24, "wBorders",         "N",2)
    ENDPROC
ENDDEFINE

DEFINE CLASS STRUCT_ARRAY AS Session
    DIMENSION aSTRUCT[1,4]
    
    PROCEDURE stInit(n, cName, cType, nSize)
        this.aSTRUCT[n,1] = cName
        this.aSTRUCT[n,2] = cType
        this.aSTRUCT[n,3] = nSize
        this.aSTRUCT[n,4] = IIF(n>1, this.aSTRUCT[n-1,3]+this.aSTRUCT[n-1,4], 0)
    ENDFUNC 
ENDDEFINE

DEFINE CLASS STRUCT_CALSS AS Session
    stObj = NULL
    pBuffer = 0
    nSize = 0
    
    PROCEDURE init(stObj)
        this.stObj = stObj
        LOCAL nRowCount
        nRowCount = ALEN(this.stObj.aSTRUCT, 1)
        this.nSize = this.stObj.aSTRUCT[nRowCount,3] + this.stObj.aSTRUCT[nRowCount,4]
        this.pBuffer = apiMalloc(this.nSize)
        SYS(2600, this.pBuffer, this.nSize, REPLICATE(0h00,this.nSize))
    ENDPROC
    
    PROCEDURE Destroy
        apiFree(this.pBuffer)
    ENDPROC
    
    HIDDEN FUNCTION getRow(cName)
        LOCAL nRow
        nRow = ASCAN(this.stObj.aSTRUCT, cName)
        RETURN IIF(nRow>0, ASUBSCRIPT(this.stObj.aSTRUCT,nRow,1), 0)
    ENDFUNC 
    
    FUNCTION getValue(cName)
        LOCAL n, nSize, nOffset, ret
        n = this.getRow(cName)
        IF n == 0
            RETURN NULL
        ENDIF 
        nSize   = this.stObj.aSTRUCT[n,3]
        nOffset = this.stObj.aSTRUCT[n,4]
        RETURN ICASE(this.stObj.aSTRUCT[n,2]=="N", CTOBIN(SYS(2600, this.pBuffer+nOffset, nSize), TRANSFORM(nSize)+"RS"),;
                     this.stObj.aSTRUCT[n,2]=="C", SYS(2600, this.pBuffer+nOffset, nSize),;
                     NULL)
    ENDFUNC 
    
    FUNCTION setValue(cName, vValue)
        LOCAL n, nSize, nOffset, ret
        n = this.getRow(cName)
        IF n == 0
            RETURN ""
        ENDIF 
        nSize   = this.stObj.aSTRUCT[n,3]
        nOffset = this.stObj.aSTRUCT[n,4]
        IF this.stObj.aSTRUCT[n,2]=="N" AND VARTYPE(vValue)=="N"
            RETURN SYS(2600, this.pBuffer+nOffset, nSize, BINTOC(vValue, TRANSFORM(nSize)+"RS"))
        ELSE
            IF this.stObj.aSTRUCT[n,2]=="C" AND VARTYPE(vValue)=="C"
                vValue = LEFT(vValue, nSize)
                nSize = LEN(vValue)
                RETURN SYS(2600, this.pBuffer+nOffset, nSize, vValue)
            ENDIF
        ENDIF    
        RETURN ""
    ENDFUNC 
ENDDEFINE
搜索更多相关主题的帖子: 结构 this RETURN 类型 long 
2022-03-14 21:53
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
将 STRUCT_ARRAY 整合到 STRUCT_CALSSS,这样简洁些
程序代码:
**    VFP封装结构类型示例
**    思路:
**    定义一个二维数组(aSTRUCT)用来描述结构体成员属性
**    每一行表达一个成员属性:名称(Name),类型(Type), 大小(Size),地址偏移量(Offset)
**    定义 STRUCT_CALSS 类,提供初始化结构成员属性、读写结构成员数据函数 stInit()、getValue()、setValue()

DECLARE long malloc IN msvcrt as apiMalloc long
DECLARE long free IN msvcrt as apiFree long
DECLARE long SendMessage IN user32 as apiSendMessage long,long,long,long

of = CREATEOBJECT("form1")
of.show(1)
CLEAR ALL
RETURN

DEFINE CLASS form1 as Form 
    ADD OBJECT rich AS Olecontrol WITH top=10,left=10,width=200,height=200,OleClass="RICHTEXT.RichtextCtrl.1",visible=1
    PROCEDURE rich.init
        this.text = ""
        pf = CREATEOBJECT("PARAFORMAT2")
        pf.setValue("cbSize", pf.nSize)
        pf.setValue("dwMask", 256)
        pf.setValue("dyLineSpacing", 300)
        pf.setValue("bLineSpacingRule", 4)
        #define WM_USER 0x0400
        #define EM_SETPARAFORMAT (WM_USER + 71)
        apiSendMessage(this.hWnd, EM_SETPARAFORMAT, 0, pf.pBuffer)
        RELEASE pf
        this.text = "123456789"+0h0D0A+"abcdefghijk"+0h0D0A+"ABCDEFGHIJK"++0h0D0A
    ENDPROC
ENDDEFINE

DEFINE CLASS PARAFORMAT2 AS STRUCT_CALSS
    PROCEDURE init
        DIMENSION this.aSTRUCT[24,4]
        this.stInit(1,  "cbSize",           "N",4)    && DWORD
        this.stInit(2,  "dwMask",           "N",4)
        this.stInit(3,  "wNumbering",       "N",2)    && WORD
        this.stInit(4,  "wEffects",         "N",2)
        this.stInit(5,  "dxStartIndent",    "N",4)
        this.stInit(6,  "dxRightIndent",    "N",4)
        this.stInit(7,  "dxOffset",         "N",4)
        this.stInit(8,  "wAlignment",       "N",2)
        this.stInit(9,  "cTabCount",        "N",2)
        this.stInit(10, "rgxTabs",          "C",128)  && DWORD[MAX_TAB_STOPS], MAX_TAB_STOPS=32
        this.stInit(11, "dySpaceBefore",    "N",4)
        this.stInit(12, "dySpaceAfter",     "N",4)
        this.stInit(13, "dyLineSpacing",    "N",4)
        this.stInit(14, "sStyle",           "N",2)
        this.stInit(15, "bLineSpacingRule", "N",1)    && BYTE
        this.stInit(16, "bOutlineLevel",    "N",1)
        this.stInit(17, "wShadingWeight",   "N",2)
        this.stInit(18, "wShadingStyle",    "N",2)
        this.stInit(19, "wNumberingStart",  "N",2)
        this.stInit(20, "wNumberingStyle",  "N",2)
        this.stInit(21, "wNumberingTab",    "N",2)
        this.stInit(22, "wBorderSpace",     "N",2)
        this.stInit(23, "wBorderWidth",     "N",2)
        this.stInit(24, "wBorders",         "N",2)
        STRUCT_CALSS::init
    ENDPROC
ENDDEFINE

DEFINE CLASS STRUCT_CALSS AS Session
    DIMENSION aSTRUCT[1,4]
    pBuffer = 0
    nSize = 0
    
    PROCEDURE init
        LOCAL nRowCount
        nRowCount = ALEN(this.aSTRUCT, 1)
        this.nSize = this.aSTRUCT[nRowCount,3] + this.aSTRUCT[nRowCount,4]
        this.pBuffer = apiMalloc(this.nSize)
        SYS(2600, this.pBuffer, this.nSize, REPLICATE(0h00,this.nSize))
    ENDPROC
    
    PROCEDURE Destroy
        apiFree(this.pBuffer)
    ENDPROC
    
    PROCEDURE stInit(n, cName, cType, nSize)
        this.aSTRUCT[n,1] = cName
        this.aSTRUCT[n,2] = cType
        this.aSTRUCT[n,3] = nSize
        this.aSTRUCT[n,4] = IIF(n>1, this.aSTRUCT[n-1,3]+this.aSTRUCT[n-1,4], 0)
    ENDFUNC  
    
    HIDDEN FUNCTION getRow(cName)
        LOCAL nRow
        nRow = ASCAN(this.aSTRUCT, cName)
        RETURN IIF(nRow>0, ASUBSCRIPT(this.aSTRUCT,nRow,1), 0)
    ENDFUNC 
    
    FUNCTION getValue(cName)
        LOCAL n, nSize, nOffset
        n = this.getRow(cName)
        IF n == 0
            RETURN NULL
        ENDIF 
        nSize   = this.aSTRUCT[n,3]
        nOffset = this.aSTRUCT[n,4]
        RETURN ICASE(this.aSTRUCT[n,2]=="N", CTOBIN(SYS(2600, this.pBuffer+nOffset, nSize), TRANSFORM(nSize)+"RS"),;
                     this.aSTRUCT[n,2]=="C", SYS(2600, this.pBuffer+nOffset, nSize),;
                     NULL)
    ENDFUNC 
    
    FUNCTION setValue(cName, vValue)
        LOCAL n, nSize, nOffset
        n = this.getRow(cName)
        IF n == 0
            RETURN ""
        ENDIF 
        nSize   = this.aSTRUCT[n,3]
        nOffset = this.aSTRUCT[n,4]
        IF this.aSTRUCT[n,2]=="N" AND VARTYPE(vValue)=="N"
            RETURN SYS(2600, this.pBuffer+nOffset, nSize, BINTOC(vValue, TRANSFORM(nSize)+"RS"))
        ELSE
            IF this.aSTRUCT[n,2]=="C" AND VARTYPE(vValue)=="C"
                vValue = LEFT(vValue, nSize)
                nSize = LEN(vValue)
                RETURN SYS(2600, this.pBuffer+nOffset, nSize, vValue)
            ENDIF
        ENDIF    
        RETURN ""
    ENDFUNC 
ENDDEFINE

2022-03-15 08:12
cssnet
Rank: 4
等 级:业余侠客
威 望:4
帖 子:317
专家分:203
注 册:2013-10-4
得分:20 
用字符串来伪装struct,只要5行代码,而用类来封装模拟struct,则需要100+行代码,而且类代码比较艰深晦涩,还用到比较危险的malloc和free,我自己写C代码,都竭力避开手动内存分配,我敢打赌,至少80%、甚至90%以上的VFPer,看不懂这种类C代码;而且,比较要命的一点是:STRUCT_CALSS.init()需要VFPer手动填充,一个疏忽,也会跟构造“伪字符串”一样,因对不准结构成员数据宽度而出错。比如:

this.stInit(10, "rgxTabs",          "C",128)  && DWORD[MAX_TAB_STOPS], MAX_TAB_STOPS=32

这一行已超出了一般初学者能力范围,掰着手指头也数不过来它是4*32=128字节,一个不小心便会数错。说实话,当初我就根本就没查MAX_TAB_STOPS的值,仅仅笼统地计算了一下,用sizeof(PARAFORMAT2) - sizeof(PARAFORMAT) = 32,便“偷懒”跳过了PARAFORMAT结构体数据成员的大部分细节。对于相对简单的struct,比如PARAFORMAT2,小心翼翼构造出一个“伪字符串”来模拟struct,仍是“性价比”较高的做法;倘若是相对复杂的struct,比如指针成员,即使是用类封装,也十分困难,几近徒劳。

综上,个人初步结论是:

用类封装模拟struct,大大增加了代码长度与复杂度,而收效则并不特别明显——这投入与产出之间,不成比例,不是一件划算的交易……有些不太值得。
收到的鲜花
  • 厨师王德榜2022-03-15 16:10 送鲜花  1朵   附言:吹版分享是好事,你的观点也是对
2022-03-15 11:18
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
回复 3楼 cssnet
从某方面说确实如此,尤其是不太熟悉调用 API 函数的人,甚至不清楚“结构类型”是什么也不足为奇,因VFP没有这方面是概念。

  
2022-03-15 11:51
antony521
Rank: 3Rank: 3
等 级:论坛游侠
威 望:1
帖 子:168
专家分:161
注 册:2009-8-20
得分:0 
回复 3楼 cssnet
那就再复杂点,设计容错性更好的class.
2022-03-15 11:53
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
回复 3楼 cssnet
“用字符串来伪装struct,只要5行代码,而用类来封装模拟struct,则需要100+行代码,而且类代码比较艰深晦涩”
编程不是代码越少越好是吧? 易读好理解的代码对程序的更新维护和交流好处多多,一个软件的发展过程更新维护占不少耗费的。

“还用到比较危险的malloc和free,我自己写C代码,都竭力避开手动内存分配”
malloc和free没那么恐怖吧,这对家伙是C语言最基本的东东,好多高级指令都是基于他的。只要能用好编程语言,不管是那门语言,提供是指令都可用。
当然,作为示例只能点到即止,简单调用malloc和free,没有考虑异常情况。

“比较要命的一点是:STRUCT_CALSS.init()需要VFPer手动填充,一个疏忽,也会跟构造“伪字符串”一样,因对不准结构成员数据宽度而出错。”
这点确实有点难为VFPer,看看C的头文件一大堆、里面声明的结构类型就明白,这不是三两日就写得出来、写得好的,所以说用C编程高大尚是有原因的。
平时用到的类封装起来,以后再用就简单了,如示例PARAFORMAT2类,再次引用直接CREATEOBJECT就OK了,不用每次都去构造“伪字符串”。

综上,个人初步结论是:
要扩展VFP这个老古董,就要下苦功。封装类,就是先苦后甜,不管学什么编程语言,道理一样。
2022-03-15 12:23
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
以下是引用cssnet在2022-3-15 11:18:48的发言:
倘若是相对复杂的struct,比如指针成员,即使是用类封装,也十分困难,几近徒劳。

虽然VFP没有指针的概念,但指针也好理解,当他是一个long变量也可以。
指针主要理解其两大特性:地址属性和大小属性。
不同类型的指针差别主要是大小属性,如字符类型指针大小属性为1byte、32位整数型指针大小属性为4byte
2022-03-15 13:07
laowan001
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:54
帖 子:802
专家分:1914
注 册:2015-12-30
得分:0 
苦活累活交给类或函数去做,多花点精力也值得,省下了大把代码时间
当然,做成类、函数或是控件,得看具体要处理的问题而定,不能一概而论,只要做出来的东东快捷健壮就好
2022-03-15 14:37
cssnet
Rank: 4
等 级:业余侠客
威 望:4
帖 子:317
专家分:203
注 册:2013-10-4
得分:0 
以下是引用吹水佬在2022-3-15 12:23:52的发言:
要扩展VFP这个老古董,就要下苦功。封装类,就是先苦后甜,不管学什么编程语言,道理一样。


其实最初我精心构造的“伪字符串”,本身就是正确无误的,后来吹版的回帖提醒了我:
敢情是我一不小心将SendMessage()声明写错了,最后一个参数应当是string@,而我随手复制/粘贴旧代码,平时用的一直是long。
故而哪怕调试到地老天荒,肯定一直报错“数据类型不匹配”!

之所以对于“用VFP类封装struct”持保留态度,那是因为后来冰雪聪明的我,找到了特别省事儿的独门秘笈(千万保密!千万保密!一般人我不告诉他!!切切。)——
只需打开VS调试一下C代码,在SendMessage()之前设个断点,先执行完前边几句:
  PARAFORMAT2 pf;
  pf.cbSize=sizeof(PARAFORMAT2); //识别paraformat与paraformat2
  pf.dwMask=PFM_LINESPACING;
  pf.dyLineSpacing=300; //行距在此设置
  pf.bLineSpacingRule=4;
然后,将结构pf的十六进制内存值的188字节复制下来,就能放到VFP中直接调用了——你说,那还搞甚么“类封装”那么复杂的东东作啥劳什子?!

聪明的,我问你。
2022-03-15 15:28
cssnet
Rank: 4
等 级:业余侠客
威 望:4
帖 子:317
专家分:203
注 册:2013-10-4
得分:0 
你比方说,结构体pf的16进制值如下:

bc 00 00 00 00 01 00 00 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc
cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc
cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc 2c 01 00 00 cc cc 04 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc

将空格移除,头上再加个0h就能直接用了,试问——还花时间精力搞“类封装”作甚?!

2022-03-15 15:48



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




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

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