回 帖 发 新 帖 刷新版面

主题:[转帖]VFP经典问题集(中)

[color=000080]本文选自《我整理的一些VFP资料》chm文档,不是从论坛上下载的,所以实在不知道这位作者的姓名,向他的辛勤劳动致以最高的敬意!网络互联,资源共享,共同提高,从我做起。[/color]

<一>:如何让程序自动设定路径?

      答:一般运行程序的目录并非固定不变,因此一般在程序启动时

都要查询当前运行程序的目录。下面这段程序给出当前路径的查询

方法:

    Function SetPath()
       LOCAL lcSys16, lcProgram
       lcSys16 = SYS(16) &&查询当前运行程序名
       lcProgram = SUBSTR(lcSys16, AT(":", lcSys16) - 1)

       CD LEFT(lcProgram, RAT("\", lcProgram))
       *-- If we are running MAIN.PRG directly, then
       *-- CD up to the parent directory
       IF RIGHT(lcProgram, 3) = "FXP"
            CD ..
       ENDIF
       SET PATH TO PROGS, FORMS, LIBS, ;
           MENU, DATA, ;
           REPORTS, INCLUDE, HELP, ;
           BITMAPS
       SET CLASSLIB TO MAIN ,vfptool
    ENDFUNC

<二>:如何对字符串进行加密、解密?

答:在VFP中,加解密字符串其实有很多种方法,在这里我作了一个函

     数,希望对您有所帮助。

     本函数功能: 1.加解密字符串。

                 2.如果有人非法修改密码,将提示非法修改。

          入口:   * LCZIP --判断是加密还是解密,.T. --加密,.F.解密。

                               *Lcpass--要加解密的字符串

          返回:  *加解密后字符串。注意:如果返回的字符串为“ERROR”

                             *证明密码出错或着密码被非法修改过

FunCtion CalcPassWord
Para Lczip,Lcpass   

lCPASS=Allt(Lcpass)
IF LcZip      &&加密
    Lcpass="DALY"+Lcpass
    LCNewPass=CHR(Len(Lcpass))  

  *处理字符串,使它不为空。因为我要把加密后的字符放到数据库,为防他人直接打开

  数据库看出密码为空。
  For i=1 To Len(Lcpass)
        LCNewPass=LCNewPass+Chr(asc(SubStr(Lcpass,i,1))/32);
                +Chr(asc(SubStr(Lcpass,i,1))%32)

  EndFor
  *取出字符串中的单个字符,并把它转为ASCII码。

  *把ASCII码除于32得到的整数跟余数转为字符,再把两个字符和合为一个字符串

  *经这种处理后,已经无法得知原来的字符,只有经计算机用反方法解开。
  LCNewPass=PADR(LCNewPass,50,CHR(1))  &&返回50个字符长度,不足的用CHR(1)补足。
Else
    IF LEN(lCPASS)#50       &&如果长度出错,证明非法修改过
        =MESSAGEbOX("密码被非法修改",0,_screen.caption)
        Return "ERROR"
    ENDIF
    For i=1 TO 50
       IF asc(SubStr(Lcpass,i,1))>=32  &&检查密码是否被非法修改
           =MESSAGEbOX("密码被非法修改",0,_screen.caption)
           Return "ERROR"
       ENDIF
    EndFor
    LCNewPass=""  &&解密
    For I=0 TO (asc(SubStr(Lcpass,1,1))*2)-9
        LCNewPass=LCNewPass+Chr(asc(SubStr(Lcpass,i+10,1))*32;
             +asc(SubStr(Lcpass,i+11,1)))
        i=i+1
    EndFor
EndIF
Return LcNewPass

<三>:怎样控制程序的退出?

答:在VFP中当要退出程序时,将会执行Shut 事件,这个事件可以
用On ShutDown 命令设定它的执行程序。默认程序是显示 “不能退
出 VFP”信息框。下面这个函数是询问您是否想退出程序。

**************************************
* Usage: On ShutDown do OnShutDown
*************************************
FUNCTION OnShutdown()
    Yes_No = 4
    Questionicon=32
    DownNO=7
    ASSKEY=MESSAGEBOX("您是否要退出这个程序", YES_NO+Questionicon , _screen.caption )
    IF Asskey=DownNO
        return .F.
    Else
        &&这里做退出程序的处理
    EndIf
ENDFUNC
<四>:在屏幕中央显示提示框?

   答:一般在处理数据时,都在屏幕中间显示一个提示正在处理数据

    的提示框,Vfp 能否作到了。

     当然能,而且很简单,只要您跟着下面的做法,一定能成在处理之前先做

    lcMsg = "Processing, Please Wait......."
    WAIT WINDOW lcMsg NOCLEAR NOWAIT AT SROW()/2, (SCOLS()-LEN(lcMsg))/2
    处理后用Wait clear 清除等待框即可达到效果。

<五>:"&"符号是什么意思,该如何使用?

答:先用下面的例子说明“&”宏替换指令的基本功能

      CC="ABC"
     M_C="cc"
     ?&M_C &&显示结果为ABC

 
     根据以上的例子说明“&”宏替换指令是一个取地址的指令。
    下面是一些应用“&”宏替换指令的常用命令

     a: olddbf = alia() && 贮存当前工作区的数据库名称到变量olddbf
         &&其他命令,可能改变当前工作区.......
        sele &olddbf &&用“&”宏替换指令把工作区返回原先的工作区

    b: Oldfilter =set("filter")
    * 贮存当前工作区数据库的过滤条件到变量Oldfilter
    set filter to * 去掉当前工作区数据库的过滤条件
    set filter to &Oldfilter

    * 设定当前工作区数据库的过滤条件到原来的过滤条件

<六>:在运行中,我怎样测试变量是否存在?

答:您可以用 TYPE() 函数来测试变量是否存在,例如

IF TYPE("lMyVariable") # "U" 
  *变量存在, 
ENDIF 

您也可以检查 TYPE() = "U" (变量不存在)

<七>:传递给函数的参数是局部参数还是私有参数?

      答:所有传递给函数的参数都是局部变量。

<八>:头文件有什么作用,该如何在程序或者表单中使用它?

答:头文件可以用来预先定义一些如公司名称、货币名称等固定不变的常量

       在程序中,你可以用命令#include 头文件 来包含头文件。

      在表单或者类设计时,你可以用Form/Include或者 Class/Include菜单来包含一个头文件。

      如果你用的是Form/Include或者 Class/Include菜单来包含头文件,那么这个表单或者类的所有控件都可以对这个头文件的内容进行存取。如果你是在一个方法里用     #include来包含头文件,那么,只有这个方法可以存取它。

<九>:在运行期间,我如何才能知道文件是否存在?

答:你可以用函数 File("文件名"),如果文件存在,函数返回.T.,否则返回.F.

<十>:我每次启动编译后的VFP程序,VFP的系统菜单总是先出现,我该怎样避免?

答:在您的CONFIG.FPW文件中假如SYSMENU=OFF 的命令。

<十一>:当我的程序启动以后,应该怎样才能把VFP的主屏幕消掉?

答: 1:在您的CONFIG.FPW文件中假如Screen=OFF 的命令。

          2:在表单的 Init 事件中,包含下列代码行:Application.Visible = .F.


<十二>:如何播放一个声音文件?

答:您可以利用API 或者ActiveX来调用,也可以用下面的命令来播放:

    Set Bell TO "C:\sound.Wav"  ,1

                ??Chr(7)

            Set BELL TO

<十三>:我怎样在程序中设定多个处理程序或者类文件的关联?

答:用ADDITIVE 语句,例如

    SET   Procedure TO Proc.prg ADDITIVE

       SET Library To Class.Vcx ADDITIVE

<十四>:如何在VFP的程序中,取得目前已经被用及可用的磁碟机代号?

答:可以利用API--GetLogicalDrives()来完成这项任务。

  GetLogicalDrives()会传回一个数值,数值中的每一个BIT其值若为1 ,则表示该磁碟机为已用,其值若为0 ,则表示该磁碟机为可用。

Declare integer GetLogicalDrives in kernel32.dll AS Is_Driver_ready &&定义API函数 
nReslut =Is_Driver_Ready() 
cUsed_Driver="已经被用的磁碟机代号有:" 
cAvail_Driver="可用的磁碟机代号有:" 
nMask=1 
FOR i=1 To 26  &&因为最多只有26个磁碟代号 
   IF Bitand(nReslut,nMask)!=0 
                     cUsed_Driver=cUsed_Driver+CHR(64+i)+"," 
            Else 
                     cAvail_Driver=cAvail_Driver+CHR(64+i)+"," 
           ENDIF 
          nMask=BitLShift(nMask,1) 
Next 
Wait cUsed_Driver+CHR(13)+cAvail_Driver Window 

<十五>:如何得到符合条件的记录数?

答:VFP的Reccount()只能取到数据库全部记录数,但是无法得到过滤后数据的准确记录数。

    这里利用Count 写了一个函数,可以对取得过滤后数据的准确记录数。

    调用方式: Recordcount("表名")    &&对表名进行记数

                Recordcount()    &&对当前表进行记数

Func Recordcount   &&Edit By daly
    para Ntable       
    Olddbf = alia()
    IF !empty(ntable)
                sele &ntable
    Endif
    count        &&计算数目
    IF !empty(olddbf)
                sele &olddbf
    Else
                Sele 0
    EndIF   
return _tally      &&_tally 是Count处理数

<十六>:如何用按键中断循环运行中的程序?

答:可以利用chrsaw() 和 inkey()来抓取键盘缓冲区的键值来达到目的

    例如: 本函数用CTRL+C 来中断程序。

function chkbreak       &&Edit By daly
para b_msg      &&传入中断提示信息
    if chrsaw()     &&设定等待时间
        t0=inkey()  &&等待按键
        clear typeahead  &&清除键盘缓冲区
        if t0=3 OR t0=17
            if messagebox(b_msg,36,"信息提示")=6
                RETURN .f.
            endif
        endif
    endif
return .t.

回复列表 (共9个回复)

沙发

<十七>:如何判断一条记录是否全空?

答:用afields 来取得表的所有字段名称 ,在利用宏指令&查看当前字段是否为空。

    备注: 此函数只适于参考

function Isemptyrecord &&Edit By daly
para m_data
dime fieldarray(1)
=afields(fieldarray)
For i=1 to alen(fieldarray)/16
        Do case
            Case fieldarray(i,2)="C"     &&如果是字符型
                IF !empty(&fieldarray(i,1))
                    return .F.
                EndIF
            Case fieldarray(i,2)="N"
                IF !&fieldarray(i,1)=0
                    return .F.
                EndIF
            Case fieldarray(i,2)="L"
        EndCase
EndFOr
return .T.


<十八>:如何把数值转换为字符并加“,”号?

    答: 跟着下面的函数做

***************************
*把数值转为字符,并加“,”号
***************************
func addsym
para m_int
    mcc=allt(str(m_int))
    IF at('.',mcc)>0
            m_n=at('.',mcc)-1
    Else
            m_n=len(mCC)    
    EndiF   
    FOr i=m_n to 2 step -1
        IF (m_n-i)%3=2
                mcc=stuff(mcc,i,0,",")
        EndIF
    EndFOr
return mcc


<十九>:如何判断记录是否为第一条记录?

答:可以利用EOF()和BOF()函数来判断

****************************************
* 是否最后一条记录
****************************************
function endrecord
m_cc=.T.
IF !eof()
skip 1
IF !eof()
    m_cc=.F.
EndIF
    IF !bof()
    skip -1
    EndIF
    EndIF
    IF eof()
        go bott
    EndIF
Return m_Cc
****************************************
* 是否第一条记录
****************************************
function beginrecord
m_cc=.T.
IF !bof()
skip -1
IF !bof()
    m_cc=.F.
        IF !eof()
        skip 1
        EndIF
    ENDIF
    EndIF
    IF eof()
        go bott
    EndIF
Return m_Cc

板凳

<二十>:如何把数字金额转为人民币大写格式?

答:在财务软件中经常要把把数字金额转为人民币大写格式,下面的函数将实现这个功能

*/!********************************************************
*/! Function Name: CaseMoney
*/! Description: 把数字金额转为人民币大写格式
*/! Usage: =caseMoney(金额) 金额是数字型
*/!********************************************************
Function CaseMoney &&Edit By daly
PARA Money
*辨别是否是数字金额
IF TYPE("Money") #"N"
    =messagebox(" 金额类型出错",0,_screen.caption)
    Return " "
EndIF
*转换金额为字符型
IF Money>9999999999999.99
    =messagebox(" 数值太大,无法处理",0,_screen.caption)
    Return " "
EndIF
CMoney=Allt(Str(Money,16,2))

*定义数组
DIME CaseFormat(10)
CaseFormat(1) ="壹"
CaseFormat(2) ="贰"
CaseFormat(3) ="叁"
CaseFormat(4) ="肆"
CaseFormat(5) ="伍"
CaseFormat(6) ="陆"
CaseFormat(7) ="柒"
CaseFormat(8) ="捌"
CaseFormat(9) ="玖"
Dime Unit(3)
Unit(1) ="拾"
Unit(2) ="百"
Unit(3) ="千"
*开始转换
M_Cmoney=""
MoneyLen=len(CMoney)
J=0
For i=MoneyLen To 1 step -1
    Nowmoney=val(substr(CMoney,i,1))
    IF Nowmoney>0
        do case
            Case i = MoneyLen
                M_Cmoney=CaseFormat(Nowmoney)+"分"
            Case i = MoneyLen-1
                M_Cmoney="元"+CaseFormat(Nowmoney)+"角"+M_Cmoney
            Case i = MoneyLen-3
                M_Cmoney=CaseFormat(Nowmoney)+M_Cmoney
            Case i < MoneyLen-3
                IF mod((J+1),4)>0
                    M_Cmoney=CaseFormat(Nowmoney)+Unit(mod(J+1,4))+M_Cmoney
                Else
                    M_J = int((j+1)/4)-1
                    IF M_J>0
                        IF M_J = 1 or M_J = 3
                            M_C = "万"+m_C
                        Else
                            M_C = "亿"+m_C
                        Endif
                    EndIF
                    IF left(M_Cmoney,2)="万"
                        M_Cmoney=right(M_Cmoney,len(M_Cmoney)-2)
                    EndIF
                    M_Cmoney=CaseFormat(Nowmoney)+M_C+M_Cmoney
                EndIF
            EndCase
    Else
        do case
            Case i = MoneyLen-1
                IF Empty(M_Cmoney)
                    M_Cmoney="元整"
                Else
                    M_Cmoney="元零"+M_Cmoney
                EndIF
            Case i < MoneyLen-3
                IF mod((J+1),4)>0
                    IF substr(M_Cmoney,1,2)#"零" and !substr(M_Cmoney,1,2)$"万亿元"
                        M_Cmoney="零" +M_Cmoney
                    EndIF
                Else
                    M_J = int((j+1)/4)-1
                    IF M_J>0
                        IF M_J = 1 or M_J = 3
                            M_C = "万"+m_C
                        Else
                            M_C = "亿"+m_C
                        Endif
                    EndIF
                    IF substr(M_Cmoney,1,2)="万"
                        M_Cmoney=right(M_Cmoney,len(M_Cmoney)-2)
                    EndIF
                    M_Cmoney=M_C+M_Cmoney
                EndIF
          EndCase
EndIf
j=j+1
EndFor
Return M_Cmoney


3 楼

<二十一>:如何在VFP中访问动态链接库?

     答:   如果需要调用的函数在某 dll 中,可以链接该库,再调用该函数。

          在调用一个 dll 函数之前,必须了解该函数的调用协议,包括函数的名称,参数的数目和类型以及返回值类型。

          在 Visual FoxPro 中,只能使用为三十二位环境编写的 dll。但是,如果需要访问一个十六位的 dll,可以使用 Foxtools.fll 中合适的函数来实现。

          若要调用一个 dll 函数使用 DECLARE - DLL 命令注册 DLL 函数,函数的名称区分大小写。
注意 如果指定 WIN32API 为库名称,Visual FoxPro 将在 Kernel32.dll、Gdi32.dll、User32.dll、Mpr.dll 和 Advapi32.dll 中查找被调用的 32 位 Windows DLL 函数。象调用其它 Visual FoxPro 函数一样调用 DLL 函数。
例如,下面的程序将注册 Windows USER 系统中 DLL 库里的 GetActiveWindow( ) 函数,该函数将显示 Visual FoxPro 主窗口的句柄。GetActiveWindow( ) 无参数,但返回一个一位整数:

        DECLARE INTEGER GetActiveWindow IN win32api
        MESSAGEBOX(STR( GetActiveWindow() ) )

    包含所要注册函数的 DLL 必须存放在默认目录中,如 Windows 或 System 目录,或者在 DOS 路径中。

    如果要调用的函数和 Visual FoxPro 中已存在的函数(本地函数或者前面声明的 dll 函数)重名,您可以为重复的函数名字取一个别名,然后用别名来调用它。

 

        DECLARE INTEGER GetActiveWindow IN win32api AS GetWinHndl
        MESSAGEBOX(STR( GetWinHndl() ) )

    在退出 Visual FoxPro 之前,所链接的 DLL 函数一直保持有效,因此,在每个工作期中只须声明一次。如果不再想调用 DLL 中的函数,可以执行 CLEAR DLLS 命令将其从内存中清除以释放资源。注释 执行 CLEAR dllS 命令时,将从内存中清除所有已声明的 dll 函数。

    向一个 DLL 传递参数,在注册一个 dll 函数时,必须指定参数的数目和类型。默认情况下,数据按值传递。也可以在参数前面添加符号 (@) 来强制参数按引用传递。一般,dll 函数遵循 C 语言中的数据类型规则,这和 Visual FoxPro 中的规定有所不同。例如,dll 函数不支持日期和货币数据类型。如果传递给 DLL 函数的数据不被该函数支持,那么在传递该数据之前,必须将其转换为适当的数据类型。例如,如下命令可以将日期类型转换为数值型的 Julian 格式:

cDate = sys(11, date())
nDate = val( cDate )

     某些 dll 函数需要比较复杂的参数,如结构或数组。如果函数需要一个指向结构的指针,那么必须确定该结构的内部布局,然后在向 DLL 函数传递之前或从 DLL 函数返回之后,转换为 Visual FoxPro 中的字符串。例如,Windows 系统函数 GetSystemTime( ) 需要一个指向结构的指针,该结构包含八个字或 16 位的无符号整数,分别代表年、月、日等,其结构定义如下:

typedef struct _SYSTEMTIME {
   WORD wYear ;
   WORD wMonth ;
   WORD wDayOfWeek ;
   WORD wDay ;
   WORD wHour ;
   WORD wMinute ;
   WORD wSecond ;
   WORD wMilliseconds ;
} SYSTEMTIME

为了在 Visual FoxPro 和 GetSystemTime( ) 函数之间传递数据,首先必须创建一个 40 字节的字符串缓冲区(初始时其内容为空格),然后把这个字符串的地址作为参数传递给函数,让函数向这个字符串填写要返回的数据。返回之后,必须以两个字符为一单元,进行分析,然后提取出结构的各个部分。下面的语句描述了如何提取该结构的三个字段:

DECLARE INTEGER GetSystemTime IN win32api STRING @
cBuff=SPACE(40)
=GetSystemTime(@cBuff)

tYear = ALLTRIM(STR(ASC(SUBSTR(cBuff,2)) *  ;
   256 + ASC(SUBSTR(cBuff,1))))
tMonth = ALLTRIM(STR(ASC(SUBSTR(cBuff,4)) * ;
   256 + ASC(SUBSTR(cBuff,3))))
tDOW = ALLTRIM(STR(ASC(SUBSTR(cBuff,6)) * ;
   256 + ASC(SUBSTR(cBuff,5))))

    如果在 Visual FoxPro 中使用的数据是数组,则在传递给 DLL 函数之前,必须遍历该数组,把它联结到一个用 C 语言样式的数组的单个字符串。如果 Windows 函数需要 16 位或 32 位的值,则在链接为字符串之前,必须将该值转换为等价的十六进制的形式。在传递包含数组数据的字符串时, Visual FoxPro 将该串的地址传送给 DLL,该 DLL 将其作为数组处理。

4 楼


网速太慢,发个帖比生孩子还难!唉。

5 楼

学习中,感谢123,加123分

6 楼

学习了,谢谢123.唉!就是不能给楼主加分,建议版主设置给楼主加分的功能.

7 楼

谢谢

8 楼

谢谢楼主分享!!!!

9 楼

学习了,谢谢楼主分享!敬佩!

我来回复

您尚未登录,请登录后再回复。点此登录或注册