No announcement yet.

Scope of OPEN statement to DLL Subroutines

  • Filter
  • Time
  • Show
Clear All
new posts

    Scope of OPEN statement to DLL Subroutines

    I am running PB/win 8.04. If I OPEN a file in the main program for input or output, and later perform LINE INPUT# or PRINT# to that file in a subroutine located in the same source file, then the scope of the OPEN statement extends into the subroutine (I can read and write to the file opened in the main program). However, if I compile the subroutine as a separate dll, then the scope of the OPEN statement does not extend into the subroutine (I can not read or write to the file opened in the main program). To turn my subroutine into a dll, I am embedding my subroutine into the dll templet provided as a sample with PB/win 8.04. I am also declaring the subroutine at the top of the main program.
    Last edited by John Harvill; 26 Oct 2007, 04:56 PM.

    Correct, the PB file handle is only valid within the code module in which it is obtained.

    I just (last couple of days) posted an example of how to pass that open handle using OPEN HANDLE, let me see if I can find that thread...

    Yup, here is is...

    Post # 38 on page 3

    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]


      Will the OPEN with a file HANDLE in the subroutine cause my program to lose its position in the sequential file, when calling and returning and calling the routine again? Normally, each input or output command should advance to the next line of the sequential file. UPDATE -- Experimentation shows that each call to the subroutine causes the INPUT # or PRINT # to restart at the beginning of the file, when the INPUT and PRINT are embedded between the open-with-handle and close statements in the subroutine. It appears the file-handle will point to the file but not the position in the file.
      Last edited by John Harvill; 26 Oct 2007, 06:20 PM.


        In main program use SEEK function to get position in file.
        When you call sub in DLL pass the position.
        In DLL sub use SEEK statement to set position.



          Great! That works. Therefore, PB/win 8.04 requires managing two variables when opening in the main (or calling routine) and using INPUT# and PRINT# in the called routine in another source file, especially in a dll. We must manage both the system-file-handle and the position in the file. Therefore, in a simplified format, the templet for sequential files is as follows (OUTPUT is similar):

          In the main or calling routine:
          1. OPEN "filename" FOR INPUT AS filenum
          2. sysFileHandle = FILEATTR( filenum, 2 )
          3. FilePosition = SEEK( filenum )
          4. CALL routine ( ..., sysFileHandle, FilePosition )
          5. CLOSE filenum

          In the called routine:
          1. OPEN HANDLE sysFileHandle FOR INPUT AS filenum
          2. SEEK filenum, FilePosition ..................'Sets the position in the file
          3. INPUT#
          4. FilePosition = seek( filenum ) .............'Gets the new position for later use
          5. CLOSE filenum

          Thank you. I appreciate your guidance in this matter.


            You really shouldn't need to set the filepointer.

            Or maybe you do.

            The doc on using the HANDLE option is a tad thin..e.g., must be opened in consistent access and sharing modes at a minimum. You'd think the help file would say 'something' about the filepointer if it were important.

            I would think when the compiler does an 'open HANDLE' it would pick up the filepointer if it needs to track it itself in the RTL.

            Darned, now I have to test it. Oh well, it's the kind of thing I enjoy doing on Saturdays anyway.

            Whoa, wait a minute....I've done the example I posted, and it works just fine. Then again, that's about the only way I've done it (OPEN for sequential output). So I guess I do have to test some other modes.
            Michael Mattias
            Tal Systems (retired)
            Port Washington WI USA
            [email protected]


              Ok, I found TWO errors in my originally-posted test suite, one logic and one signficant typing error.

              It appears you DO have to set the filepointer, except when opening for APPEND.

              Which is - all and all - quite understandable.

              I'll send a note in to the PB Documentation department to ask them to upgrade the "OPEN HANDLE" portion of the help file.

              DLL SOURCE CODE
              ' FILE: FPHLIB.BAS
              ' Test the SEEK value returned by PB when a file is opened in a DLL using OPEN HANDLE.
              ' 10.26.07
              ' Compiler both DLL and EXE ==> PB/Win 8.03
              #COMPILE   DLL
              #REGISTER  NONE
              #DEBUG     ERROR ON
              #TOOLS     OFF
              %NOGDI = 1     ' no GDI (Graphics Device Interface) functions
              %NOMMIDS = 1   ' no Multimedia ID definitions
              #INCLUDE "Win32API.INC"
               SELECT CASE Reason
                 CASE  %DLL_PROCESS_ATTACH
                  '  Indicates that the DLL is being loaded by another process (a DLL OR EXE is loading the DLL).
                  '  DLLs can use this opportunity to initialize any instance or GLOBAL data, such as arrays.
                  '  Must NOT call any functions not in KERNEL32.DLL under this notification
                 CASE  %DLL_PROCESS_DETACH
                 '  Indicates that the DLL is being unloaded OR detached FROM the calling application.
                 '  DLLs can take this opportunity to clean up all resources for all threads attached and known to the DLL.
                 '  This is functionally equivalent to the WEP function in 16-BIT DLLs.
                  CASE %DLL_THREAD_ATTACH
               '    Indicates that the DLL is being loaded by a new thread in the calling application.
               '    DLLs can use this opportunity to initialize any thread local storage (TLS).
                  CASE %DLL_THREAD_DETACH
                ' Indicates that the thread is exiting cleanly.
                ' IF the DLL has allocated any thread local storage (TLS), it should be released.
              ' Reserved specifies further aspects of the DLL initialization and cleanup.
              ' If Reason is %DLL_PROCESS_ATTACH, Reserved is NULL (zero) for dynamic loads and non-NULL for static loads.
              ' If Reason is %DLL_PROCESS_DETACH, Reserved is NULL IF LibMain has been called by using the
              ' FreeLibrary API call and non-NULL If LibMain has been called during process termination.
               END SELECT
               FUNCTION = 1     ' return 1 if succesfully initialized.
                                 ' return zero if failure.
              END FUNCTION
              FUNCTION FpfReturnSeek ALIAS "FphReturnSeekPos"_
                    (BYVAL hSys AS LONG, openmode AS STRING, OPTIONAL SeekPos AS LONG) EXPORT AS LONG
                LOCAL h AS LONG
                h = FREEFILE
                SELECT CASE LCASE$(openmode)
                    CASE "input"
                        OPEN HANDLE hSys FOR INPUT AS h
                    CASE "binary"
                        OPEN HANDLE hSys FOR BINARY AS h
                    CASE "output"
                        OPEN HANDLE hSys FOR OUTPUT AS h
                    CASE "append"
                        OPEN HANDLE hSys FOR APPEND AS h
                    CASE ELSE
                        MSGBOX USING$ ("Unsupported open mode '&'", openmode)
                        FUNCTION = -1&
                        EXIT FUNCTION
                END SELECT
                ' ------------------------------------------
                ' if a seek position was specified, do that
                ' ------------------------------------------
                IF VARPTR(SeekPos) THEN
                     SEEK h, Seekpos
                END IF
                IF ISFALSE ERR THEN
                    FUNCTION = SEEK(h)
                    CLOSE       h
                   MSGBOX USING$ ("Error on open or seek # &", ERR, ERROR$(ERR))
                   FUNCTION = -1&
                END IF
              END FUNCTION
              ' /// END OF FILE ////
              ' fphcall.bas
              ' Check Seek Position here vs what is reported in library function which
              ' opens the file using OPEN HANDLE
              ' for various open modes.
              ' MCM 10/26/07 (public domain, not that it's worth anything anyway).
              ' Compiler both DLL and EXE ==> PB/Win 8.03
              #COMPILE      EXE
              #DIM          ALL
              #REGISTER     NONE
              #DEBUG ERROR  ON
              #TOOLS        OFF
              $TESTFILE_NAME  = "~fph.dat"
              DECLARE FUNCTION FphReturnSeekPos  LIB "fphlib.dll" ALIAS "FphReturnSeekPos" _
                    (BYVAL hSys AS LONG, openmode AS STRING, OPTIONAL SeekPos AS LONG) AS LONG
              FUNCTION PBMAIN () AS LONG
                LOCAL openMode () AS STRING, msg AS STRING
                LOCAL h AS LONG, Z AS LONG
                REDIM OpenMode (3)
                Openmode (0) = "INPUT"
                Openmode (1) = "BINARY"
                Openmode (2) = "APPEND"
                Openmode (3) = "OUTPUT"
                ' Create a test file we can do stuff with
                ' filesize = 100*(78+2) = 800
                CALL CreateTestFile ()
                ' now do the test using $TESTFILE_NAME
                Msg = ""   ' initialize
                CALL  DoTheTest (openMode(), msg)
                MSGBOX MSG,, "results"
              END FUNCTION
              FUNCTION DoTheTest (OpenMode() AS STRING, msg AS STRING) AS LONG
                  LOCAL h AS LONG, hSys AS LONG, mBUff AS STRING
                  LOCAL exepos AS LONG, dllPos AS LONG
                  LOCAL i AS LONG, sData AS STRING, bOpen AS LONG
                  LOCAL iTestNo AS LONG   ' 0 = no directed seek in call, 1 = directed seek in call, 2 = no SEEK in ExE at all
                  ' ---------------------------------------------
                  MSG = ""
                  FOR iTestNo = 0 TO 2
                        MSG = MSG & CHOOSE$( ItestNO+1,_
                              "Seek in main, no directed seek in call", _
                              "Seek in main, directed seek in call", _
                              "No Seek in main (get default by openmode)") _
                            & $CRLF
                       FOR i = LBOUND (openmode,1) TO UBOUND (openMode,1)
                       ' recreate the test file so results are always clean
                          CALL CreateTestFile
                           h  = FREEFILE
                           SELECT CASE LCASE$(Openmode (I))
                              CASE "input"
                                OPEN $TESTFILE_NAME FOR INPUT AS  h
                              CASE "output"
                                OPEN $TESTFILE_NAME FOR OUTPUT AS h
                              CASE "binary"
                                OPEN $TESTFILE_NAME FOR BINARY AS h
                              CASE "append"
                                OPEN $TESTFILE_NAME FOR APPEND AS h
                           END SELECT
                           IF ISFALSE ERR THEN
                                 bOpen = 1
                                 SELECT CASE iTestNo
                                     CASE 0,1
                                         SEEK h, 401    '   seek to start of 51st record
                                 END SELECT
                                 IF ISFALSE ERR THEN
                                      exePos  = SEEK (h)
                                      hSys    = FILEATTR (h, 2)  ' get system handle
                                      ' either do or don't pass directed SEEK to library.
                                      SELECT CASE ITestNO
                                          CASE 0,2     ' no directed seek
                                            dllPos = FphReturnSeekPos (hSys, OpenMode(I))
                                          CASE 1       ' directed seek
                                            dllPos = FphReturnSeekPos (hSys, OpenMode(I), exepos)
                                      END SELECT
                                      mBUff   = USING$ ( "mode &  ExePos  ###  DllPos ###", openMode(i),exePos, dllPos)
                                 ELSE         ' SEEK failed
                                      mBuff =   USING$ ("Mode &  returns Error # on SEEK", openMode(i), ERR)
                                 END IF
                           ELSE             'open failed
                               bOpen = 0
                               mBUff = USING$ ("Mode & returns Error # on OPEN", openmode (I), ERR)
                           END IF
                           Msg = MSG & mbuff & $CRLF
                           IF bOpen THEN
                               CLOSE h
                           END IF
                      NEXT i ' test next openmode in this test
                 NEXT iTestNO   ' do the other tests
                 MSG = MSG & "End of Test"
              END FUNCTION ' do the test
              FUNCTION CreateTestFile () AS LONG
               LOCAL h AS LONG, Z AS LONG, msg AS STRING
                h = FREEFILE
                msg   = STRING$(78, "x")
                FOR Z = 1 TO 100
                    MSG =  RSET$ (FORMAT$(Z),6) & STRING$(72,"x")
                    PRINT #h, msg                  ' appends CRLF for total record size of 6 + 72 + 2 ==> 80
                CLOSE h
              END FUNCTION
              ' /// END OF FILE
              Last edited by Michael Mattias; 27 Oct 2007, 10:56 AM. Reason: Had several errors, resulting in bad conclusions. I think it's OK now.
              Michael Mattias
              Tal Systems (retired)
              Port Washington WI USA
              [email protected]


                I agree. More details in the help file to clarify the responsiblities of the programmer when working with sequential files would be useful for those new to PowerBASIC, especially for someonw like me from the FORTRAN world who hasn't been thinking about Handles and File Pointers. Thanks for your effort with this issue.
                Last edited by John Harvill; 27 Oct 2007, 06:25 PM.


                  Creating that test program was kind of fun for me.

                  The last couple of months I have not had too many opportunities to just sit down and create applications in BASIC - which is something I truly enjoy doing.

                  The whole 'OPEN HANDLE' thing I had not done for a number of years and at least three versions of the compiler, so my recall perforce was 'fuzzy' at best as to how it works.

                  The fringe benefit is, it shows it's really not that difficult to "TRY IT! - a technique which all too often seems in short supply around here. That whole thing took me maybe an hour, hour and a half - and that is a very long time for a dinky little test program like this.

                  Even if I only use OPEN HANDLE once in the next year, what I learned by actually DOING it rates to repay my time investment on that very first use.

                  On the subject of applications, I was pretty happy to be able to create a new demo this past week. I have a commercial application - the EDI PAl(tm) ANSI ASC X12 Viewer-Editor-Printer - where the primary user action is to "open" a file to "do something" with it.

                  I am also a (heavy) user of this application. While I have standard "file/open", "recent files (MRU),"send to" (from file Explorer) and "drag and drop" ways to open a file, I found myself constanly 'browsing' to the same files because I open enough different files that the MRU list overflows and rolls over .. and the browse is often to a file which is 'deeply nested' in the directory tree.. eg C:\Program Files\Trading Partner\Edata\COmmerce\K-Mart\ANSI\004010\850.. quite time-consuming using the standard "OpenFileName" common dialog.

                  One of my users was watching me do this and said,"You know, you should have a "Favorites" menu to select those commonly-opened files."

                  Duh! Another new feature suggestion out of left field - one I can't help but think *I* should have conceived a long time ago.

                  So, since I didn't want to develop this by hackin' and whackin' inside a 50 Kloc program, I set out to get the code wokring correctly in a 'standalone' environment. And since it was 'standalone' I thought it might be a nice demo for people here - pretty easy to 'cut and paste and tweak' into any application where a primary user activity is "file/open" or "file/select."

                  The result is this demo: Add a 'Favorite Files' menu to your application 10-25-07

                  (I have since tried it with PB/WIN 8.03 and the Windows' header files supplied with that release and it works just fine).

                  (While I did't try it, I think it will work with PB/CC.. except you'll have to change the MSGBOX calls to something else but that takes what, 12-13 seconds for a global "search and replace?").

                  <'I'd better quit now because I am starting to ramble on' icon here>


                  PS: The suggestion I sent to PB for a doc upgrade was this:
                  I think I would suggest under OPEN, where the discussion of the HANDLE
                  clause reads....

                  "HANDLE The HANDLE option allows you to access files that have already been
                  opened by another process, DLL, or API function. The filehandle specified
                  here must be a valid Win32 operating system file handle.
                  [INSERT POINT]
                  When PowerBASIC closes ... [rest not relevant to this discussion]...."

                  .. I would add at the [INSERT POINT] something like this...

                  "The use of the HANDLE option causes the file pointer to be reset to the
                  default position for the open mode specified in the OPEN HANDLE statement.
                  That is, files opened FOR INPUT, RANDOM or BINARY reset the pointer to the
                  value specified in the BASE= clause (or the default of "1" if BASE= not
                  used), and files opened FOR APPEND have the filepointer set to the end of
                  the current file.

                  It is totally silly to OPEN HANDLE "FOR OUTPUT" since that erases any file
                  and you did not need to worry about passing a handle in the first place. [I
                  think your doc writers can clean this up a bit]"
                  Just parenthetically (and I did NOT include this in my suggestion.. which was after all a "something like...")

                  The file pointer which is reset is that associated with the PB handle used in the OPEN HANDLE statement. I do not know if the file pointer is reset on the PB handle from which the system handle was originally obtained.

                  That of course could be tested by performing an additional test: reading, writing or SEEKing in the function in the DLL and then testing the SEEK position of 'h' in the EXE on return.
                  Michael Mattias
                  Tal Systems (retired)
                  Port Washington WI USA
                  [email protected]