Can you read or seek from an embedded file?


Sometimes you might want to embed a file inside an exe. For example, if you add a file into a project and don’t mark it as Excluded, the file will be physically inserted inside the target EXE or APP. Visual Class Libraries (VCX files), Forms (SCX files), Reports (FRX) and other files that are not marked as Excluded are also embedded inside. When the user code tries to open a file with the same name as one that’s embedded, the embedded one will be opened. Embedded files cannot be written to, so they need to be opened READONLY.


 


The code below creates a test file TEST.BIN of 256 bytes and uses the FSEEK() function to seek various positions in the file and read its contents. Then the code embeds the test file into an APP and does the same tests.


 


When you seek before the start of the file, an error occurs. When you seek past the end of file, no error occurs, but reading from beyond the end of file returns no bytes read. If the read straddles the end of file, then the bytes up to the eof are read. The same behavior must occur whether the file is embedded or not, and whether the file is opened buffered or not.


 


(The original bug report was from www.FoxClub.ru)


 


The output is like this:


 


       Pos       Mode     FSeek   ErrCode    CurPos AfterRead    Read10   bytes


        50          0        50         0        50        60        10  50  51  52  53  54  55  56  57  58  59


       -15          0         0        25        60        70        10  60  61  62  63  64  65  66  67  68  69


         5          1        75         0        75        85        10  75  76  77  78  79  80  81  82  83  84


        40          1       125         0       125       135        10 125 126 127 128 129 130 131 132 133 134


      -800          1         0        25       135       145        10 135 136 137 138 139 140 141 142 143 144


       -55          1        90         0        90       100        10  90  91  92  93  94  95  96  97  98  99


       -50          2       206         0       206       216        10 206 207 208 209 210 211 212 213 214 215


      -800          2         0        25       216       226        10 216 217 218 219 220 221 222 223 224 225


        50          2       306         0       306       306         0


        -5          2       251         0       251       256         5 251 252 253 254   0


 


 


 


 


SET SAFETY OFF  


_screen.FontName=”Courier new”


LOCAL nHandle 


LOCAL cStr 


cStr=” 


FOR i=0 to 255 


      cStr=cStr+CHR(MOD(i,256)) 


ENDFOR


nHandle=FCREATE(“test.bin”)


FWRITE(nHandle,cStr) 


FCLOSE(nHandle) 


 


TEXT TO cstr  NOSHOW


            PROCEDURE Temp(nOpenmode as Integer) as String


            SET ALTERNATE TO temp.txt


            SET ALTERNATE ON


            ?”Open mode = “,nOpenMode


            nHandle=FOPEN(“test.bin”,nOpenmode)  && unbuffered, must be readonly for embedded file


            IF nHandle=-1


                  ?”Error opening file”,FERROR()


            ELSE


                  ?”       Pos       Mode     FSeek   ErrCode    CurPos AfterRead    Read10   bytes”


                  doit(nHandle,  50,0)    && seek 50 bytes rel from start


                  doit(nHandle, –15,0)    && seek-15 bytes rel from start: err


                  doit(nHandle,   5,1)    && seek 5 bytes rel from curpos


                  doit(nHandle,  40,1)    && seek 40 bytes rel from curpos


                  doit(nHandle,-800,1)    && seek -800 bytes rel from curpos: err


                  doit(nHandle, –55,1)    && seek -55 bytes rel from curpos


                  doit(nHandle, –50,2)    && seek -50 bytes rel from end


                  doit(nHandle,-800,2)    && seek -800 bytes rel from end     : err


                  doit(nHandle,  50,2)    && seek 50 bytes rel from end : no err, none read


                  doit(nHandle,  5,2)    && seek -5 bytes rel from end :partial read


                  FCLOSE(nHandle) 


            ENDIF


            SET ALTERNATE TO


            RETURN FILETOSTR(“temp.txt”)


 


      PROCEDURE doit(nHandle, npos as Integer, nMode as Integer)


            ?npos,nMode


            ??FSEEK(nHandle,npos, nMode)


            ??FERROR()  && 0 = no error


            ??FSEEK(nHandle,0,1)    && Show Cur Pos


            cbuf=FREAD(nHandle,10)  && Try to read 10 bytes: changes curpos


            ??FSEEK(nHandle,0,1)    && Show cur pos after read


            ??LEN(cBuf)                   && Show # bytes read


            FOR i = 1 TO MIN(LEN(cBuf),10)      && Show up to 10 of the bytes read


                  ??ASC(SUBSTR(cbuf,i,1))


            ENDFOR


ENDTEXT


STRTOFILE(cstr,”temp.prg”)


COMPILE temp.prg


ERASE temp.app


cresNorm=temp(0)+temp(10)


BUILD PROJECT temp FROM temp


MODIFY PROJECT temp nowait


_vfp.ActiveProject.Files.Add(“test.bin”)  && embed the generated binary file


_vfp.ActiveProject.Close


 


BUILD APP temp FROM temp


ERASE temp.prg


ERASE temp.fxp


?”Now embedded”


cresEmbed=temp(0)+temp(10)


?”Are they equal?”,cresEmbed==cresNorm


RETURN


 

Comments (2)

  1. IgorK says:

    Unfortunately there are a lot of differences between different LLFIO modes that made all the work with these functions complex – for example in unbuffered read mode in VFP9SP1 you can FSEEK before start of file (but you can’t FREAD there) and even worse – you can’t FSEEK beyond end of file – that is totally different from buffered read mode, in unbuffered mode failed FSEEK return weird numbers in some cases.

    As for embedded files – you can read data outside of such a file! In VFP8SP1 you can only read data after logical file end, attempt to access data before logical beginning of file result in some "current byte pointer" corruption – but in VFP9SP1 you can read "before file start" in unbuffered mode and "after file end" in buffered mode.

    So IMHO these part of VFP have to be rewritten – at least there must be no difference between buffered and unbuffered modes, and external/internal file processing. Suppose it will be done in VFP9 SP2

    BTW it will be nice to have built-in ability to create "shared" files with LLFIO – I mean FILE_SHARE_* flags in CreateFile API – not to "invent the wheel" while wrapping direct API calls.

  2. Calvin_Hsia says:

    Yes, Igor, this will be a lot better in SP2! The test program says they are equal in SP2.