Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

CC3/Win7 Exit Code Bug Workaround

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • CC3/Win7 Exit Code Bug Workaround

    #INCLUDE file / instructions for use with all three program files:
    Code:
    ' FILE: CC2CC3RC.INC
    ' #INCLUDE file for program RUNCC3.BAS (a PB/CC 2.0 program) and any PB/CC 3.0
    ' or PBN/Win 7.0 program which needs to use these features.
    ' Author: Michael Mattias Racine WI
    ' Placed in the public domain by the author 10/26/02.
    ' --------------------------
    ' PURPOSE
    ' These procedures were created to allow users of PowerBASIC Console Compiler
    ' version 3.0 and PB/Windows v 7.0 to use those compilers to create programs when
    ' the exit codeis required (e.g., to run in a batch file and interrgate the ERRORLEVEL).
    ' A bug in these compilers does not permit setting the exit code as the FUNCTION
    ' return value of WinMain/PbMain.
    ' ------------------------------------------------
    ' This file contains some procedures which will be compiled into the PB/CC 2.0
    ' program and some which will be compiled into PB/CC 3.0 or PB/Win7.0 Program.
    ' It was written so it is not dependent on the version of the Win32API.INC file
    ' provided by PowerBASIC Inc., as the functions used in this file have been amongst
    ' the most-changed and most-tinkered-with functions in PB's conversion of the
    ' 'standard' Windows header files. Certain equate values have also been affected
    ' by changes in equate casting from LONG to DWORD
    ' --------------------------------------------------------------------------
    ' To use these procedures in a PB/CC v 3.0 or PB/Win 7.0 program,
     'add TWO lines of code to the program
    
    ' #INCLUDE "CC2CC3RC.INC"                    << ADD THIS LINE HERE
    ' FUNCTION PBMAIN () AS LONG
    ' all your 'regular' PB/CC or PB/WIN code goes here
    
    '  SetObjectRC (desired RETURN CODE value)   << ADD THIS LINE HERE
    '  END FUNCTION
    
    ' TO RUN THE CC3/Win7 Program from a batch file:
    ' RunCC3.exe CC3ProgramName.exe
    'IF Errorlevel 5  GOTO whereever
    'IF Errorlevel 4  GOTO whereever
    'IF Errorlevel 3  GOTO whereever
    'etc...
    ' =====================================================================
    '  START OF PROGRAM MATERIAL
    ' =====================================================================
    ' Object name for memory-mapped file
    $MMF_NAME          = "CC2_CC3_RC_OBJECT_NAME"
    ' this program's custom return codes
    %RC_NO_CC3_PROGRAM = &hFF
    %RC_SHELL_FAILED   = &hFE
    
    #IF NOT %DEF(%NULL)
      %NULL  = 0&
    #ENDIF
    #IF NOT %DEF(%MAX_PATH)
      %MAX_PATH = 260&
    #ENDIF
    
    '==========================================================================
    ' TO GET AROUND VARIATIONS BETWEEN THE CC2/CC3/WIN7 AND MULTIPLE-DATED AND
    ' CHANGED AND GENERALLY UNPREDICTABLE VARIATIONS IN THE POWERBASIC-SUPPLIED
    ' WIN32API.INC FILES, WE WILL DO OUR OWN DECLARATIONS AND USE CALL DWORD
    ' TO ACCESS THE FILE MAPPING FUNCTIONS IN KERNEL32.DLL
    ' (This looks like the hard way, but trust me, it is a lot easier
    ' than figuring out and handling all the header file variations ).
    ' =========================================================================
    ' Procedure Names and Declares for functions from KERNEL32.DLL:
    $KERNEL32_LIBNAME         = "KERNEL32.DLL"
    $CREATE_FILE_MAPPING      = "CreateFileMappingA"
    $OPEN_FILE_MAPPING        = "OpenFileMappingA"
    $MAP_VIEW_OF_FILE         = "MapViewOfFile"
    $UNMAP_VIEW_OF_FILE       = "UnmapViewOfFile"
    $CLOSE_HANDLE             = "CloseHandle"
    $WAIT_FOR_SINGLE_OBJECT   = "WaitForSingleObject"
    $GET_PROC_ADDRESS         = "GetProcAddress"
    DECLARE FUNCTION B_CreateFileMapping  _
       (BYVAL hFile AS LONG, BYVAL SecurityAttributes AS LONG, BYVAL flProtect AS LONG, BYVAL dwMaximumSizeHigh AS LONG, _
        BYVAL dwMaximumSizeLow AS LONG, lpName AS ASCIIZ) AS LONG
    DECLARE FUNCTION B_MapViewOfFile _
      (BYVAL hFileMappingObject AS LONG, BYVAL dwDesiredAccess AS LONG, BYVAL dwFileOffsetHigh AS LONG, BYVAL dwFileOffsetLow AS LONG, BYVAL dwNumberOfBytesToMap AS LONG) AS LONG
    DECLARE FUNCTION B_UnmapViewOfFile _
      (BYVAL BaseAddress AS LONG) AS LONG
    DECLARE FUNCTION B_OpenFileMapping _
      (BYVAL dwDesiredAccess AS LONG, BYVAL bInheritHandle AS LONG, lpName AS ASCIIZ) AS LONG
    DECLARE FUNCTION B_CloseHandle (BYVAL hObject AS LONG) AS LONG
    
    ' the DECLAREs we must do "conventionally".
    ' You may get conflicts on the parameters depending on which version of Win32API.INC is in use
    DECLARE FUNCTION GetProcAddress LIB "KERNEL32.DLL" ALIAS "GetProcAddress" (BYVAL hModule AS DWORD, lpProcName AS ASCIIZ) AS LONG
    DECLARE FUNCTION GetModuleHandle LIB "KERNEL32.DLL" ALIAS "GetModuleHandleA" (lpModuleName AS ASCIIZ) AS LONG
    DECLARE FUNCTION LoadLibrary LIB "KERNEL32.DLL" ALIAS "LoadLibraryA" (lpLibFileName AS ASCIIZ) AS LONG
    DECLARE FUNCTION FreeLibrary LIB "KERNEL32.DLL" ALIAS "FreeLibrary" (BYVAL hLibModule AS DWORD) AS LONG
    
    ' file mapping equates
    %B_PAGE_READWRITE         = &H04
    %B_FILE_MAP_WRITE         = &H2
    %B_FILE_MAP_READ          = &H4
    
    #IF (%PB_REVISION AND &h0FF00) - &h0200
      ' this is not the CC2 program, so it must be a CC3 or Win 7 program
      ' ==================================================================
      ' Code which will be included when compiling the CC3/Win 7 program:
      ' ==================================================================
    
     ' Function to set the value of the return code in the memory-mapped file
     FUNCTION SetObjectRC (BYVAL RC AS LONG) AS LONG
       LOCAL szLibName AS ASCIIZ * %MAX_PATH, szProcName AS ASCIIZ * %MAX_PATH
       LOCAL hKernel AS LONG, dwProcAddr AS DWORD
       LOCAL hMMF AS LONG
       LOCAL pRC AS LONG PTR
       LOCAL DesiredAccess AS LONG, Inherit AS LONG
       LOCAL OffsetHigh AS LONG, OffsetLow AS LONG, nBytes AS LONG
       LOCAL szMMFName AS ASCIIZ * %MAX_PATH
    
      ' get handle to the MMF created in the CC2 program
       szLibName      = $KERNEL32_LIBNAME
       hKernel        = GetModuleHandle (szLibName)
       szProcName     = $OPEN_FILE_MAPPING
       dwProcAddr     = GetProcAddress (hKernel, szProcName)
       IF dwProcAddr THEN
         DesiredAccess = %B_FILE_MAP_WRITE
         Inherit       = 0
         szMMFName     = $MMF_NAME
         CALL DWORD  dwProcAddr USING B_OpenFileMapping (DesiredAccess, Inherit, szMMFName) TO hMMF
    
         IF hMMF THEN          ' we have found the object, and can now map it
            szProcName     = $MAP_VIEW_OF_FILE
            dwProcAddr     = GetProcAddress (hKernel, szProcName)
            DesiredAccess  = %B_FILE_MAP_WRITE
            OffsetHigh     = 0&    ' we are accessing offset zero of the MMF
            OffsetLow      = 0&
            nBytes         = SIZEOF(@pRC)
            CALL DWORD dwProcAddr USING B_MapViewOfFile (hMMF, DesiredAccess, OffsetHigh, OffsetLow, nBytes) TO pRC
            @pRC = RC         ' set the value of the return code into the MMF
            ' free our lock on the MMF
            szProcName     = $UNMAP_VIEW_OF_FILE
            dwProcAddr     = GetProcAddress(hKernel, szProcName)
            CALL DWORD dwProcAddr USING B_UnmapViewOfFile (BYVAL pRC)
            ' and close our handle to the MMF
            szProcName     = $CLOSE_HANDLE
            dwProcAddr     = GetprocAddress (hKernel, szProcName)
            CALL DWORD dwProcAddr USING B_CloseHandle (hMMF)
         END IF     ' if we obtained a handle to the MMF
       END IF       ' if we even found the OpenFileMappingProc
    
     END FUNCTION
    
    #ELSE
       ' ========================================================================
       '              CODE INCLUDED ONLY IN THE CC2 PROGRAM
       ' ========================================================================
     DECLARE FUNCTION ExecuteProgram (szProgramName AS ASCIIZ) AS LONG
    
     FUNCTION RunCC3Program (szProgramName AS ASCIIZ, szParameterString AS ASCIIZ) AS LONG
       ' Create a memory-mapped file; run target CC3 program; interrogate the
       ' value of the RC which the CC3/Win7 program stored as a long in the MMF at offset zero
    
          LOCAL szLibName AS ASCIIZ * %MAX_PATH, szProcName AS ASCIIZ * %MAX_PATH
          LOCAL hKernel AS LONG, dwProcAddr AS DWORD
          LOCAL hMMF AS LONG, hFile AS LONG
          LOCAL pRC AS LONG PTR, RC AS LONG, Stat AS LONG
          LOCAL DesiredAccess AS LONG, Inherit AS LONG
          LOCAL OffsetHigh AS LONG, OffsetLow AS LONG, nBytes AS LONG
          LOCAL Security   AS LONG, Protect AS LONG
          LOCAL szMMFName AS ASCIIZ * %MAX_PATH
          LOCAL ShellExecuteResult AS LONG
    
          ' Create a memory mapped file object:
           szLibName      = $KERNEL32_LIBNAME
           hKernel        = GetModuleHandle (szLibName)
           szProcName     = $CREATE_FILE_MAPPING
           dwProcAddr     = GetProcAddress (hKernel, szProcName)
           IF dwProcAddr THEN
              hFile       =  -1&                  ' create memory object, not disk file
              Security    =   0&                  ' you want security? do it yourself!
              Protect     =   %B_PAGE_READWRITE   ' one process reads, one writes
              OffsetHigh  =   0&                  ' when used with CreateFileMapping, these
              OffsetLow   =  SIZEOF(@pRC)         ' are the 'max Size' parameters for the object
              szMMFName   =  $MMF_NAME
              CALL DWORD dwProcAddr USING B_CreateFileMapping (hFile, Security, Protect, OffsetHigh, OffsetLow, szMMFName) TO hMMF
              IF hMMF THEN   ' object created successfully
                 Stat = ExecuteProgram (szProgramName)    ' execute the specified program
                 IF ISTRUE Stat THEN                      ' Program Executed.
                 ' using the handle we got when we created the MMF, map a view of MMF
                 ' and interrogate the RC stored there by the program we ran.
                    szProcName     = $MAP_VIEW_OF_FILE
                    dwProcAddr     = GetProcAddress (hKernel, szProcName)
                    DesiredAccess  = %B_FILE_MAP_READ
                    OffsetHigh     = 0&    ' we are accessing offset zero of the MMF
                    OffsetLow      = 0&
                    nBytes         = SIZEOF(@pRC)  ' we only need access to a long integer
                    CALL DWORD dwProcAddr USING B_MapViewOfFile (hMMF, DesiredAccess, OffsetHigh, OffsetLow, nBytes) TO pRC
                    RC             = @pRC          ' store function return value BEFORE unmapping
                    szProcName     = $UNMAP_VIEW_OF_FILE
                    dwProcAddr     = GetProcAddress(hKernel, szProcName)
                    CALL DWORD dwProcAddr USING B_UnmapViewOfFile (BYVAL pRC)
                ELSE
                    RC = %RC_SHELL_FAILED
                END IF
                ' clean up by closing our handle to the MMF we created
                szProcName     = $CLOSE_HANDLE
                dwProcAddr     = GetprocAddress (hKernel, szProcName)
                CALL DWORD dwProcAddr USING B_CloseHandle (hMMF)
             END IF     ' if we obtained a handle to the MMF
          END IF       ' if we even found the CreateFileMappingProc
    
         FUNCTION = RC  ' set this function to the RC
    
      END FUNCTION
    ' ==================================================================
    ' UDT, Procedure Names and Declares for functions from SHELL32.DLL,
    ' ONLY REQUIRED IN THE CC2 PROGRAM
    ' ==================================================================
    TYPE B_SHELLEXECUTEINFO
      cbSize        AS DWORD
      fMask         AS DWORD
      hwnd          AS DWORD
      lpVerb        AS ASCIIZ PTR
      lpFile        AS ASCIIZ PTR
      lpParameters  AS ASCIIZ PTR
      lpDirectory   AS ASCIIZ PTR
      nShow         AS LONG
      hInstApp      AS DWORD
      ' Optional fields
      lpIDList      AS DWORD
      lpClass       AS ASCIIZ PTR
      hkeyClass     AS DWORD
      dwHotKey      AS DWORD
      item          AS DWORD     ' hIcon or hMonitor in other versions of Win32API.INC
      hProcess      AS DWORD
    END TYPE
    
    DECLARE FUNCTION   B_ShellExecuteEx (X AS B_SHELLEXECUTEINFO) AS LONG
    $SHELL32_LIBNAME  = "SHELL32.DLL"
    $SHELL_EXECUTE_EX = "ShellExecuteExA"
    %B_SEE_MASK_NOCLOSEPROCESS                     = &H40
    %B_SW_SHOW                                     = 5
    %B_INFINITE                                    = -1&
    %B_WAIT_OBJECT_0                               =  0&
    
    $WAIT_FOR_SINGLE_OBJECT_EX  = "WaitForSingleObjectEx"
    DECLARE FUNCTION B_WaitForSingleObjectEx _
      (BYVAL hHandle AS LONG, BYVAL dwMilliseconds AS LONG, BYVAL bAlertable AS LONG) AS LONG
    
     ' This function returns TRUE If Program Executed
     ' NOTE: I did not allow for parameters.  I had to leave something for you to do yourself!
     FUNCTION ExecuteProgram (szProgramName AS ASCIIZ) AS LONG
       LOCAL SEI AS B_ShellExecuteInfo, Stat AS LONG, E AS LONG
       LOCAL hLib AS LONG, szLibName AS ASCIIZ * %MAX_PATH, dwProcAddr AS DWORD, szProcName AS ASCIIZ * 128
    
       LOCAL hWaitProcess AS LONG
       LOCAL lpVerb AS ASCIIZ * 20, lpParameters AS ASCIIZ * 20, lpDirectory AS ASCIIZ * 20, lpFile AS ASCIIZ * %MAX_PATH
       LOCAL TimeOut2 AS LONG      ' "Timeout" is a reservedword
    
       lpverb       = "OPEN"
       lpFile       = szProgramName
       lpParameters = ""
       Timeout2    = %B_INFINITE
    
       SEI.cbSize       = SIZEOF(SEI)
       SEI.fmask        = %B_SEE_MASK_NOCLOSEPROCESS  ' we want a process handle
       SEI.hWnd         = %NULL
       SEI.lpVerb       = VARPTR(lpVerb)
       SEI.LpFile       = VARPTR(lpFile)
       SEI.lpParameters = 0&                '  VARPTR (lpParameters) if you add parms
       SEI.lpDirectory  = %NULL
       SEI.nShow        = %B_SW_SHOW
    
       SEI.hInstApp     = 0             ' updated by function
       SEI.lpIdList     = %NULL         ' here down to hprocess ignored unless appropriate mask included in fmask
       SEI.lpClass      = %NULL
       SEI.hkeyClass    = %NULL
       SEI.dwHotKey     = %NULL
       SEI.item         = %NULL
       SEI.hProcess     = 0             ' will be updated by ShellExecuteEx
    
       ' Get the address for the call to ShellExecuteEx
       szLibName  = $SHELL32_LIBNAME
       hlib       = LoadLibrary (szLibName)
       szProcName = $SHELL_EXECUTE_EX
       dwProcAddr = GetProcAddress (hLib, szProcName)
    
       ' Call Shell ExecuteEx
       CALL DWORD dwProcAddr USING B_ShellExecuteEx (SEI) TO Stat
       ' we are done with SHELL32.DLL, so free the handle
       FreeLibrary hLib
    
       IF ISTRUE Stat THEN   ' function succeeded and returned
            hWaitProcess = SEI.hProcess
            szProcName   = $WAIT_FOR_SINGLE_OBJECT_EX
            szLibName    = $KERNEL32_LIBNAME
            hLib         = GetModuleHandle (szLibName)
            dwProcAddr   = GetProcAddress (hLib, szProcName)
            CALL DWORD dwProcAddr USING B_WaitForSingleObjectEx (hWaitProcess, Timeout2, %NULL) TO Stat
            IF Stat = %B_WAIT_OBJECT_0 THEN
                 ' normal return completion of process
                 FUNCTION = 1
            ELSE
                 ' something went wrong
                FUNCTION = 0
            END IF
            szProcName = $CLOSE_HANDLE
            szLibName  = $KERNEL32_LIBNAME
            hLib       = GetModuleHandle (szLibName)
            dwProcAddr = GetProcAddress(hLib, szProcName)
            CALL DWORD dwProcAddr USING B_CloseHandle (hWaitProcess)
        ELSE             ' shell executeEx failed
            FUNCTION  = 0
        END IF
    
    
    END FUNCTION
    
    
    #ENDIF
    ' end of #INCLUDE file CC2CC3RC.INC
    ------------------
    Michael Mattias
    Tal Systems Inc.
    Racine WI USA
    [email protected]
    www.talsystems.com

    [This message has been edited by Michael Mattias (edited October 26, 2002).]
    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]
    http://www.talsystems.com

  • #2
    RUNCC3.BAS to be compiled with PB/CC 2.0

    Code:
    'FILE RUNCC3.bas FOR PB/CC 2.0
    ' Author: Michael Mattias Racine WI
    ' Placed in the public domain by the author 10/26/02
    '   THIS PROGRAM RUNS A CC3 OR WIN7 PROGRAM AND INTERROGATES THEN
    '   RETURN CODE SET WITH A COOPERATING PROGRAM
    '   Runs a CC3 or Win7 program
    '   USAGE:  RUNCC3.EXE cc3ProgramName
    ' THis version of the program is set up with some displays and 'wait for key'
    ' only so the developer can actually see it works.
    
    #COMPILE EXE
    #REGISTER NONE
    #DEBUG ERROR ON
    #INCLUDE "CC2CC3RC.INC"
    FUNCTION PBMAIN () AS LONG
      LOCAL szCC3ProgName AS ASCIIZ * 260, szParms AS ASCIIZ * 20, RC AS LONG
      szCC3Progname = COMMAND$
      szParms       = "Parameters"
    
      IF szCc3Progname <> "" THEN
         STDOUT "RUNCC3: Running Program " & szCC3Progname
         RC = RunCC3Program (szCC3ProgName, szParms)
         STDOUT "RUNCC3: Setting Exit code to" & STR$(RC)
         FUNCTION = RC
      ELSE
         STDOUT "USAGE: RUNCC3.EXE CC3programnname"
         FUNCTION = %RC_NO_CC3_PROGRAM
      END IF
      STDOUT "RUNCC3:Any Key.."
      WAITKEY$
      END FUNCTION
    ' === END OF PROGRAM RUNCC3.BAS =====
    
    ' ** END OF FILE ***
    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      CC3RCA.BAS Demo Program to be compiled with PB/CC 3.0

      Code:
      'FILE: CC3RCA.BAS for PB/CC 3.0
      ' Demo program showing how to use a PB/CC 3.0 program with program RUNCC3.EXE
      ' Author: Michael Mattias Racine WI
      ' Placed in the public domain by the author 10/26/02
      ' This could be any PB/CC 3.0 program. It is run from RUNCC3.EXE because PB/CC 3.0
      ' has a bug which prevents setting the exit code as desired.
      ' This program sets the value of the return code to the number of seconds in
      ' the current time. NOTE THAT A VALUE IS NOT ASSIGNED PBMAIN (not that it matters,
      ' because that is the bug in PB/CC 3.0)
      
      #COMPILE EXE
      #REGISTER NONE
      #DEBUG ERROR ON
      
      ' include support for the cooperating CC2 program which calls this program
      #INCLUDE "CC2CC3RC.INC"            '  <<< One of the two extra lines of code needed
      
      FUNCTION PBMAIN () AS LONG
      
        LOCAL rc AS LONG, T AS STRING
        ' use the seconds of the time to get a random return code
        T = TIME$
        RC = VAL(RIGHT$(T, 2))
        STDOUT "CC3RCA: Setting Memory-mapped file  RC Value to " & STR$(RC)
        SetObjectRc RC                  ' the second extra line of code needed
        STDOUT "CC3RCA: Back from Setting RC to" & STR$(RC)
        STDOUT "CC3RCA: Any Key...";
        WAITKEY$
      
      END FUNCTION
      Michael Mattias
      Tal Systems (retired)
      Port Washington WI USA
      [email protected]
      http://www.talsystems.com

      Comment


      • #4
        WIN7RCA.BAS Demo Program to be compiled with PB/Win 7.0

        Code:
        '-------------------------------------------------------------------------------
        '   WIN7RCA.BAS  for PB/Win 7.0
        '   Demo program to show use of CC2 program RUNCC3 to interrogate a return
        '   code set by a PB/Win7 program. The PB/Win 7 compiler as a bug which
        '   prevents the 'normal' method of setting a return code - setting the
        '   value of WinMain before exiting.
        '-------------------------------------------------------------------------------
        
        #COMPILE EXE
        #DEBUG ERROR ON
        #REGISTER NONE
        ' Just to insure that the #ICNLUDE file for the MMF method is compatible
        ' with inclusion of the standard WIndows Header files:
        #INCLUDE "WIN32API.INC"
        
        ' INLCUDE SUPPORT FOR MMF Return Code:
        #INCLUDE "CC2CC3RC.INC"                ' << one of the two lines which must be added
        FUNCTION WINMAIN (BYVAL hInstance     AS LONG, _
                          BYVAL hPrevInstance AS LONG, _
                          lpCmdLine           AS ASCIIZ PTR, _
                          BYVAL iCmdShow      AS LONG) AS LONG
        
          LOCAL X AS STRING, RC AS LONG
        
            X = INPUTBOX$ ("Enter Return Code Desired","MMF Return Code Demo")
            RC = VAL(x)
            SetObjectRc RC         ' << THE OTHER LINE WHICH MUST BE ADDED
        
        END FUNCTION
        
        ' ** END OF PROGRAM **
        Michael Mattias
        Tal Systems (retired)
        Port Washington WI USA
        [email protected]
        http://www.talsystems.com

        Comment

        Working...
        X