标题:VFP封装结构类型示例
只看楼主
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
以下是引用cssnet在2022-3-15 15:48:19的发言:

你比方说,结构体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就能直接用了,试问——还花时间精力搞“类封装”作甚?!


只针对个别的事情可以这样做.
成员复杂数据变动性大时,这样静态不断去截糊修改怕耗费也不小。
要对各成员非常了解才不容易搞错,因为可读性差(可以说是无可读性),搞错了也不容易发现和调试,以后更新维护就更不用说了,怕到时连自己也看不明。

封装成类,不是针对某一具体事情来做,是一个系统工程,一经封装就可以代代相传。
其实写个结构类也不是什么难的事,如:
程序代码:
MASM代码
PARAFORMAT2 STRUCT
  cbSize            DWORD    ?
  dwMask            DWORD    ?
  wNumbering        WORD     ?
  wEffects          WORD     ?

VFP代码
DEFINE CLASS PARAFORMAT2 AS STRUCT_CALS
    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)

形式都差不多,再看看 OPENFILENAME 结构类
DEFINE CLASS OPENFILENAME AS STRUCT_CALSS
    PROCEDURE init
        DIMENSION this.aSTRUCT[20,4]
        this.stInit(1,  "lStructSize",       "N",4)
        this.stInit(2,  "hwndOwner",         "N",4)
        this.stInit(3,  "hInstance",         "N",4)
        this.stInit(4,  "lpstrFilter",       "N",4)

PARAFORMAT2 与 OPENFILENAME 是不是有好多相似的地方,编写时好多都可以复制粘贴或搜索替换,稍作修改就OK。
使用时直接按成员名称读写就可以,清晰明了,易学易用不容易搞错,构造“伪字符串”是没法比的。






[此贴子已经被作者于2022-3-15 18:30编辑过]

2022-03-15 18:28
cssnet
Rank: 4
等 级:业余侠客
威 望:4
帖 子:317
专家分:203
注 册:2013-10-4
得分:0 
以下是引用吹水佬在2022-3-15 18:28:21的发言:
要对各成员非常了解才不容易搞错,因为可读性差(可以说是无可读性),搞错了也不容易发现和调试,以后更新维护就更不用说了,怕到时连自己也看不明。


吹版说得对!
确实先前光顾着一个人爽啦,未考虑日后维护与可读性。
其实或许可以折中一下,兼顾可读性。
试考虑,这样子处理如何:
*---------------------------------
* 参照API的struct PARAFORMAT2,命名所需变量:
cbSize = 0x000000BC  &&(4字节:其值为结构体总长度 = 十进制188)
dwMask = 0x00000100  &&(4字节:其值为修改行距所对应的Mask = 十进制256)
dyLineSpacing = 0x0000012C  &&(4字节:其值为自己设置的行距值 = 十进制300,单位为twips)
bLineSpacingRule = 0x04  &&(1字节:其值意思是确定改行距的单位为twips)
* 用命名参数填充所需的结构成员,至于与我无关者一概忽略:
pf = BINTOC(cbSize,"4RS") + BINTOC(dwMask,"4RS") + REPLICATE(0h00,156);
   + BINTOC(dyLineSpacing,"4RS") + 0h0000 + BINTOC(bLineSpacingRule,"1RS") + REPLICATE(0h00,17)
*---------------------------------

如此,简洁明了且变量有注释,变量命名直指结构成员,而"4RS"、"1RS"也明确标注了字节长度,估计十年后也看得分明,看得懂啦——不知君意如何?
2022-03-15 21:15
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
以下是引用cssnet在2022-3-15 21:15:13的发言:



吹版说得对!
确实先前光顾着一个人爽啦,未考虑日后维护与可读性。
其实或许可以折中一下,兼顾可读性。
试考虑,这样子处理如何:
*---------------------------------
* 参照API的struct PARAFORMAT2,命名所需变量:
cbSize = 0x000000BC  &&(4字节:其值为结构体总长度 = 十进制188)
dwMask = 0x00000100  &&(4字节:其值为修改行距所对应的Mask = 十进制256)
dyLineSpacing = 0x0000012C  &&(4字节:其值为自己设置的行距值 = 十进制300,单位为twips)
bLineSpacingRule = 0x04  &&(1字节:其值意思是确定改行距的单位为twips)
* 用命名参数填充所需的结构成员,至于与我无关者一概忽略:
pf = BINTOC(cbSize,"4RS") + BINTOC(dwMask,"4RS") + REPLICATE(0h00,156);
   + BINTOC(dyLineSpacing,"4RS") + 0h0000 + BINTOC(bLineSpacingRule,"1RS") + REPLICATE(0h00,17)
*---------------------------------

如此,简洁明了且变量有注释,变量命名直指结构成员,而"4RS"、"1RS"也明确标注了字节长度,估计十年后也看得分明,看得懂啦——不知君意如何?

如果经常会用到结构类型都这样子来写,功夫还真有点强劲,少点气力也不成。那个字符串中的每个用“+”间隔的内容都要想清楚、计准确,够费神的。

2022-03-15 21:26
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
以下是引用cssnet在2022-3-15 11:18:48的发言:
倘若是相对复杂的struct,比如指针成员,即使是用类封装,也十分困难,几近徒劳。

就“指针成员”问题也写个示例,觉得没什么特别。

程序代码:
**     
**    StructCalss.h
**     
#define WM_USER             0x0400
#define MAX_PATH            260
#define EM_SETPARAFORMAT    (WM_USER + 71)

程序代码:
**     
**    StructCalss.prg
**     
DEFINE CLASS OPENFILENAME AS STRUCT_CALSS
    PROCEDURE init
        DIMENSION this.aSTRUCT[20,4]
        this.stInit(1,  "lStructSize",       "N",4)
        this.stInit(2,  "hwndOwner",         "N",4)
        this.stInit(3,  "hInstance",         "N",4)
        this.stInit(4,  "lpstrFilter",       "N",4)
        this.stInit(5,  "lpstrCustomFilter", "N",4)
        this.stInit(6,  "nMaxCustFilter",    "N",4)
        this.stInit(7,  "nFilterIndex",      "N",4)
        this.stInit(8,  "lpstrFile",         "N",4)
        this.stInit(9,  "nMaxFile",          "N",4)
        this.stInit(10, "lpstrFileTitle",    "N",4)
        this.stInit(11, "nMaxFileTitle",     "N",4)
        this.stInit(12, "lpstrInitialDir",   "N",4)
        this.stInit(13, "lpstrTitle",        "N",4)
        this.stInit(14, "Flags",             "N",4)
        this.stInit(15, "nFileOffset",       "N",2)
        this.stInit(16, "nFileExtension",    "N",2)
        this.stInit(17, "lpstrDefExt",       "N",4)
        this.stInit(18, "lCustData",         "N",4)
        this.stInit(19, "lpfnHook",          "N",4)
        this.stInit(20, "lpTemplateName",    "N",4)
        STRUCT_CALSS::init
    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

FUNCTION LoadApi()
    DECLARE long malloc  IN msvcrt as apiMalloc long
    DECLARE long calloc  IN msvcrt as apiCalloc long,long
    DECLARE long free    IN msvcrt as apiFree   long
    DECLARE long _strdup IN msvcrt as apiStrdup string@
    DECLARE long strlen  IN msvcrt as apiStrlen long
    
    DECLARE long SendMessage IN user32 as apiSendMessage long,long,long,long
    
    DECLARE long GetOpenFileName IN comdlg32 long
    DECLARE long GetSaveFileName IN comdlg32 long
ENDFUNC

程序代码:
**     
**    StructCalss_demo.prg 
**     
#INCLUDE StructCalss.h
SET PROCEDURE TO StructCalss.prg ADDITIVE
LoadApi() 

? myGetFileName(0, "C:\TEMP\__temp.txt", "Text Files (*.txt)|*.txt|All Files (*.*)|*.*", "打开文件", 0)
*? myGetFileName(0, "C:\TEMP\__temp.txt", "Text Files (*.txt)|*.txt|All Files (*.*)|*.*", "另存文件", 1, 0x80806)

SET PROCEDURE TO
CLEAR ALL
RETURN

*
* 打开对话框
* myGetFileName(hWnd, cDefFile, cType, cTitle, nDialogType, nFlags)
*
* 参数:hWnd .....。.... 指向所有者对话框窗口的句柄, 没有时为0。
*       cDefFile ....... 默认指定文件名
*       cType .......... 文件类型格式过滤字符串。
*                        如文件格式选择列表:
*
*                           [color=#0000FF]Text Files (*.txt)    [/color]
*                           [color=#808080]All Files (*.*)[/color]
*                   
*                           cType:"[color=#0000FF]Text Files (*.txt)|*.txt|All Files (*.*)|*.*"[/color]
*
*       cTitle ........ 对话框标题 
*                     
*       nDialogType ... 对话框类型,[color=#800000]0 选择打开文件, 1 选择另存文件[/color]
*
*       nFlags ........ 对话框的选项,参考 OPENFILENAME 结构成员 Flags 的描述。
*                       如不选择则按默认设置。
*
* 返回:返回选择的文件名,“取消”或选择无效的文件名时返回空串。
*
FUNCTION myGetFileName(hWnd, cDefFile, cType, cTitle, nDialogType, nFlags)
    LOCAL nBuffSize, of, pFile, pType, pTitle, ret, nLen, cPaht, cFiles

    nBuffSize = 80 * MAX_PATH             && 要有足够空间存放多个文件名。
    pFile = apiMalloc(nBuffSize)
    SYS(2600, pFile, LEN(cDefFile)+1, cDefFile+0h00)
    pType = apiMalloc(LEN(cType)+1)
    cType = STRTRAN(cType, "|", 0h00)     && 转为以0h00分隔
    SYS(2600, pType, LEN(cType)+1, cType+0h00)
    pTitle = apiStrdup(cTitle)
    
    IF EMPTY(nFlags)    && 设置默认值 
        * 打开,0x80000|0x1000|0x4|0x200 = OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY|OFN_ALLOWMULTISELECT
        * 另存,0x80000|0x800 |0x4|0x2   = OFN_EXPLORER|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT
        nFlags = IIF(nDialogType==0, 0x81204, 0x80806)
    ENDIF

    of = CREATEOBJECT("OPENFILENAME")
    of.setValue("lStructSize", of.nSize)
    of.setValue("hwndOwner",   hWnd)
    of.setValue("lpstrFilter", pType)
    of.setValue("lpstrFile",   pFile)
    of.setValue("nMaxFile",    nBuffSize)
    of.setValue("lpstrTitle",  pTitle)
    of.setValue("Flags",       nFlags)

    IF nDialogType==0
        ret = GetOpenFileName(of.pBuffer)
    ELSE
        ret = GetSaveFileName(of.pBuffer)
    ENDIF

    cFiles = ""
    
    IF ret > 0
        LOCAL ptr
        ptr = pFile  
        nLen   = apiStrlen(ptr)    && pFile 以0h00分隔,以0h0000结束
        cFiles = SYS(2600, ptr, nLen)
        ptr = ptr + nLen + 1
        nLen   = apiStrlen(ptr)
        IF nDialogType==0 AND nLen > 0
            cPaht = ADDBS(cFiles)
            cFiles = ""
            DO WHILE nLen > 0        && 多选
                cFiles = cFiles  + cPaht + SYS(2600, ptr, nLen) + 0h0D0A
                ptr = ptr + nLen + 1
                nLen   = apiStrlen(ptr)
            ENDDO
            cFiles = RTRIM(cFiles,0h0D0A)
        ENDIF   
    ENDIF

    apiFree(pFile)
    apiFree(pType)
    apiFree(pTitle)
    RETURN cFiles
ENDFUNC





[此贴子已经被作者于2022-3-17 15:13编辑过]

2022-03-15 21:36
cssnet
Rank: 4
等 级:业余侠客
威 望:4
帖 子:317
专家分:203
注 册:2013-10-4
得分:0 
服——就一个字!
能将VFP玩到这样的程度,真心让人叹为观止!
讲真,今时今日,VFP对于我——相信对于论坛上很大一部分VFPer而言——也仅仅就是个仅供消遣的玩具,已不再能做到像十几、二十年前那样,费心费力去钻研、去挖掘VFP的艰深功能——事关,动力已没有,精力也不再允许了。
可,吹版展露出来的VFP功力,确是令人佩服啊!
2022-03-15 22:50
easyppt
Rank: 3Rank: 3
等 级:论坛游侠
威 望:1
帖 子:119
专家分:169
注 册:2021-11-24
得分:0 
吹版 威武!
2022-03-16 08:27
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
以下是引用cssnet在2022-3-15 22:50:09的发言:

服——就一个字!
能将VFP玩到这样的程度,真心让人叹为观止!
讲真,今时今日,VFP对于我——相信对于论坛上很大一部分VFPer而言——也仅仅就是个仅供消遣的玩具,已不再能做到像十几、二十年前那样,费心费力去钻研、去挖掘VFP的艰深功能——事关,动力已没有,精力也不再允许了。
可,吹版展露出来的VFP功力,确是令人佩服啊!

VFP是面向对象编程,面向对象编程的核心是“类”,可以说无类不欢。

VFP原本只是一个桌面数据库集成系统,说是“集成系统”皆因其集数据库、人机交互(UI)于一身,麻雀虽小五脏俱全,基本运行库也只是几M,短小精干,运行效率高,易学易用,是一款开发高效的较为全面的入门学习编程语言。桌面数据库,他叫第二,就没叫第一。

只可惜,他英年早废,被时代抛弃。但只要windows支持32位应用,VFP还是会继续发光。

VFP编程语言与其他编程语言从语法结构上看本质上没什么区别,区别在于语言表达能力。如指针,VFP语言自身就没这说法,要VFP能使用指针就要提高VFP的表达能力。

幸运的是,VFP有表达 DLL 和 COM 的能力,windwos就有一大堆DLL和COM。可以说,理论上只要在windows能做到的,VFP也能做得到,能做多少就看对windows认识多少了。

也许看到这个时候,即时会问:VFP搞得那么深有必要吗?确实没必要,理性的就要选择更优秀开发平台。但,感性的对VFF爱不释手的通过在其他开发平台学到的返来帮助VFP提高表达能力,如写DLL扩展VFP功能,真是一家便宜两家着数,何乐而不为?

在BCCN发的处女贴,唠唠叨叨,啰啰嗦嗦,见谅!


2022-03-16 09:12
cssnet
Rank: 4
等 级:业余侠客
威 望:4
帖 子:317
专家分:203
注 册:2013-10-4
得分:0 
以下是引用吹水佬在2022-3-16 09:12:24的发言:

如指针,VFP语言自身就没这说法,要VFP能使用指针就要提高VFP的表达能力。


其实当时我说的指针,指的是回调函数指针。
一般若遇太过复杂的Windows API调用,在VFP中我会选择直接放弃。
倘若有可能,则会在VC写的DLL中新增一个函数,用C处理好,然后返回结果让VFP程序直接使用。
如此一来,就不必非要勉强VFP去处理它本就不擅长的、或处理起来相当困难、相当笨拙的、涉及到Windows系统核心部分的API调用。
当然,这就要求VFPer应当具备最起码的VC编程基础。
不过,既然已经要用到比较复杂的Api了,那么掌握C应当也算是最起码的程序员基本功罢?
2022-03-16 10:53
sam_jiang
Rank: 8Rank: 8
等 级:贵宾
威 望:10
帖 子:542
专家分:781
注 册:2021-10-13
得分:0 
类和结构有相似处,我觉得吹水佬提供的思路非常好。可以让人更好理解结构。
2022-03-16 19:00
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:432
帖 子:10064
专家分:41463
注 册:2014-5-20
得分:0 
以下是引用吹水佬在2022-3-15 21:36:41的发言:
程序代码:
    nBuffSize = 80 * MAX_PATH             && 要有足够空间存放多个文件名。
    pFile = apiMalloc(nBuffSize)
    SYS(2600, pFile, LEN(cDefFile)+1, cDefFile+0h00)
...............

    cFiles = ""
    
    IF ret > 0
        nLen   = apiStrlen(pFile)    && pFile 以0h00分隔,以0h0000结束
        cFiles = SYS(2600, pFile, nLen)
        pFile  = pFile + nLen + 1
        nLen   = apiStrlen(pFile)
        IF nDialogType==0 AND nLen > 0
            cPaht = cFiles
            cFiles = ""
            DO WHILE nLen > 0        && 多选
                cFiles = cFiles  + cPaht + "\" +  SYS(2600, pFile, nLen) + 0h0D0A
                pFile  = pFile + nLen + 1
                nLen   = apiStrlen(pFile)
            ENDDO
            cFiles = RTRIM(cFiles,0h0D0A)
        ENDIF     
    ENDIF

    apiFree(pFile)

ENDFUNC

注意了!先截取上述示例部分相关代码,存在BUG。
有兴趣的可以先看看BUG在哪里
2022-03-17 06:46



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




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

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