No announcement yet.

Sub Routine compiled/changed out side main source how to intergrate with main code ?

  • Filter
  • Time
  • Show
Clear All
new posts

    Sub Routine compiled/changed out side main source how to intergrate with main code ?

    Hi !

    I have written a point of sale program using PB. My biggest problem or rather a tedious task for me is when a client wants to change the stationery they using.

    I usually have the stationary printing embedded into the program and call it up using a subroutine called PRINT_NOW:


    PRINT_PORT$="1" : `this number could be sitting in a variable
    Print#20,”The Acme Company”:’could also sit in a variables
    Print#20,”Address One ”
    Print#20,”Address Two”
    Print#20,”……blah blah . . “ . .
    Print#20,tab(40);”Sold At”;sold(1):’variable storing price item was sold

    What I need to know is :-

    1) Instead of having this subroutine embedded in my main program source code can I have it as an external program that can be compiled separately when ever changes are made to it.
    Eg: the tab(40) was changed to tab(20),more lines added to the stationery etc. . However the main compiled program stays intact and need not be compiled again when ever a change occurs in the stationery setting.

    2) How would I call the PRINT_NOW program from the main program.

    I really don’t fully understand the $link and #include functions and my aged mind can’t seem to figure out how a change in the PRINT_NOW code externally effects the main source code and how the exchange of variables occur (sold(1))

    I feel stupid . . .

    [B][email protected]

    ------------------- [email protected]

    Please do not feel stupid about yourself. ALL of us PB programmers
    have areas of programming where we do not have much expertise, and
    need to ask for help sometimes.

    Your best bet would be to compile the routine to a UNIT. Let's
    say you named the source code for it "myprint.bas". Then, at the
    top of the source file, you would have the line:


    followed with whatever compiler directives you want for that UNIT
    file. Note that any variables that you must share between the main
    EXE and the PBU file (the UNIT) must be declared as PUBLIC in the
    EXE's source file and must be declared as EXTERNAL in the UNIT's
    source file.

    Then, after you successfully compile your UNIT, in your main EXE's
    source file, near the top (I do not recall if a $LINK metastatement
    may be used anywhere in the main source file. I always put them
    at the top of my main source files, just to be sure), put the

    $LINK "myprint.pbu"

    Then you are free to call the procedure in your UNIT file from within
    your main EXE's code. Note that the docs say you do not have to
    explicitly DECLARE a procedure that is located in a LINKed UNIT.
    If the UNIT is located within a library (a PBL file), then you do
    have to have explicit DECLARE's for the procedure(s) in the UNIT.

    Note that all code located within a UNIT must be in a FUNCTION or SUB - you
    cannot have mainline code in a UNIT.

    One final note: if you change the source code for the UNIT file, then
    recompile the UNIT, you must also recompile the main EXE. This
    is because the PBDOS compiler embeds the UNIT directly into the
    main EXE at compile time. Therefore, if you do not recompile the EXE
    after changing the code in the UNIT, then the EXE will not contain the
    changes in the UNIT.

    I hope this helped!

    clayclear4 at mchsi dot com


      Second the UNIT recommendation.. and maybe go a step further..

      Compiling to a PBC (chain) module would allow you to change specific routines without recompiling te entire application.. that is, you could ship a only a new *.PBC file and the customer is upgraded.

      Hint: if you are now writing primarily using GOSUB..RETURN converting to either units or chain modules is going to take some work, as in either case you'll have to arrange for procedures to have access to all the data they need. While PUBLIC/EXTERNAL and COMMON can do the job, long-term your best solution for a maintainable system is to use parameterized procedures which use no 'global' datanames.

      FWIW, "change my form" was the most common mod request my firm received when I was GM of a VAR firm. We went to the equivalent of "unit" files (separately compiled, then linked) to make this easier.


      [This message has been edited by Michael Mattias (edited October 31, 2003).]
      Michael Mattias
      Tal Systems (retired)
      Port Washington WI USA
      [email protected]



        Here is an alternative solution.

             PRINT_PORT$="1" : `this number could be sitting in a variable 
             OPEN "Company.dat" FOR INPUT AS #21
             INPUT #21, TabSpot, NoAddrLines
             For Ctr = 1 TO NoAddrLines
                  INPUT #21, AddrData$
                  Print#20, AddrData$
             Next Ctr
             INPUT sold(1)
             Print #20, tab(TabSpot); ”Sold At”; sold(1)
             CLOSE #20
             CLOSE #21


        [This message has been edited by Allen B. Zilbert (edited October 31, 2003).]


          Yet another way:
          %customer_1 = 0   ' set only one of these to 1
          %customer_2 = 1
          %customer_3 = 0
            GOSUB Print_Now
          $IF %customer_1
            $INCLUDE "PNow_1.bas"
          $IF %customer_2
            $INCLUDE "PNow_2.bas"
          $IF %customer_3
            $INCLUDE "PNow_3.bas"
          Keeps each customer's print routine in separate file, does not disturb your main code.


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


            If you want to center the business header lines on the paper,
            and assuming the paper is handling 80 characters per line, you
            can use:
            print #20,tab(40 - int(len(text$) / 2));text$

            There are no atheists in a fox hole or the morning of a math test.
            If my flag offends you, I'll help you pack.


              Ummm interesting, we in South Africa would say . . "Julle het a plaaaaan maar" .. that would translate as "You have a plan .. but . . "

              Hey Clay, thanks for encouraging me to . . but the truth is now I feel even more stupid . .*hehe*

              Allen, has a method which I currently use, (having a company file and then using it), this does not work if the stationery is complex and has many columms etc. .

              Now Michael's code could most definately work for me, however It would still require that I compile the main program each time the .bas file is changed.

              Thanks people, much appreciated. .

              [B][email protected]

              [This message has been edited by Basit Bulbulia (edited October 31, 2003).]

              ------------------- [email protected]


                however It would still require that I compile the main program each time the .bas file is changed.
                So CHAIN or SHELL a separately-compiled printing module...

                <U>CHAIN SOLUTION</U>

                File of COMMON variables shared by main program and chained print module:
                ' FILE: COMMON.BI
                COMMON LastModule$, CustomerNo%, Print_port$, CompanyInfo$(), Sold!()
                PRINT Module, each customer has his own module, or maybe there is a 'default' module
                for customers who use your standard forms:
                ' FILE: PRINTNOW.BAS
                $COMPILE CHAIN
                $INCLUDE "COMMON.BI"
                    DIM I AS INTEGER
                    OPEN "LPT"+PRINT_PORT$ FOR OUTPUT AS #20
                    PRINT #20, CustomerNo%             ' print custmer number in correct place
                    FOR I = 1 to 3
                        Print#20,CompanyInfo$(I) ' 1=name, 2= address, 3 = city, state, zip
                    Print#20, "blah blah blah"
                    Print#20,tab(40);Sold At; sold(1)  :’variable storing price item was sold
                    ' set last module so main program knows what to do when it restarts..
                    LastModule$  = "PRINTNOW"
                    ' return to main program
                    CHAIN "MainProg.exe"
                ' end of file
                Main program, with provision right at the start to redirect flow to the point
                where the print module returns
                ' FILE: Main Program.bas
                $COMPLILE EXE
                $INCLUDE "Common.BAS"
                     IF LastModule$ = "PRINTNOW" THEN   ' is program starting because it's returning from CHAIN?
                       GOTO PrintNowReturn              ' and resume execution at that point
                     END IF
                ' set up the COMMON variables to be passsed to the CHAINed print module..
                      CustomerNo%  = 9876
                      Company$ (1) = "The Acme Company"
                      Company$ (2) = "1234 Main St."
                      Company$ (3) = "Ottumwa IA 56789"
                      Sold     (1) = 1234.56     ' price
                      LastModule$  = "MAINPROG"
                      CALL     SaveSharedVariables         ' save what we will need on return
                      CHAIN   "Printnow.PBC"
                ' we get here when the printnow chained file has returned
                      CALL     RestoreSharedVariables      ' reload current values 
                      PRINT "Invoice printed, do something"
                SUB SaveSharedVariables 
                 DIM hF AS INTEGER
                  hF = FREEFILE
                  OPEN "Save_me.txt" FOR OUTPUT AS hF
                  WRITE #hf, sharedVar1, SharedVar2, SharedVar3, ...
                  CLOSE hf
                END SUB
                SUB RestoreSharedVariables
                 DIM hF AS INTEGER
                   hF     = FREEFILE
                   OPEN  "save_me.txt" FOR INPUT AS hF
                   INPUT #hF, sharedVar1, sharedvar2,sharedvar3,...
                   CLOSE hf
                END SUB
                As I said before, when using CHAIN files it can be tricky to keep track of where you are at and because variables (other than COMMON) lose their values across a CHAIN, you have to save and restore.

                (Maybe recompiling the main program for each customer doesn't look so bad anymore?)

                <U>SHELL SOLUTION</U>

                Write the variables needed to a file (like the save routine above).
                SHELL a separate EXE file to print the invoice
                In the separate EXE file which will do the printing, read the variables from that file (like the reset routine above).

                This way you wouldn't have to worry about 'flow control' in your main program, as SHELL will return to the next line of code in your main program.

                (Kinda fun to do MS-DOS code, haven't done that in a couple of years..)

                [This message has been edited by Michael Mattias (edited November 01, 2003).]
                Michael Mattias
                Tal Systems (retired)
                Port Washington WI USA
                [email protected]


                  Can someone explain me why it is not possible to chain another
                  module by using string variable isntead of filename?
                   A$ = "chainmod.pbc"
                   CHAIN A$
                  I can give you a real example:
                  Let's say we have a main program and list of chain modules:
                  CH1.PBC, CH2.PBC, CH3.PBC ..etc.

                  Users can browse throght the list of chain files and run one of them.
                  In case of upgrade, I could just send an appropriate chain module (ex. CHxx.PBC)

                  I know, that there is another way (send modified MAIN.EXE and chain modules),
                  but the way I described below is more convenient: You provide only chain module.
                  Is it possible or PBDOS restrict using of string variable as a parameter for CHAIN command?

                  Hope, I could explain my situation.

                  Best regards,



                    CHAIN <U>can</U> certainly use a string argument in PB/DOS 3.5. A quick test program confirms this... for example:

                    $COMPILE EXE "NONAME.EXE"
                    COMMON X%
                    INCR X%
                    PRINT "NONAME.EXE LAUNCH COUNT =" X%
                    A$ = "NONAME.EXE"
                    IF X% < 25 THEN CHAIN A$

                    PowerBASIC Support
                    mailto:[email protected][email protected]</A>
                    mailto:[email protected]