We all know that PowerBuilder is great for developing client server applications. Often there is a need to perform batch processing on schedule, usually at night when nobody is around. These batch programs can easily be developed in PowerBuilder. All you have to do is create a non-visual custom class object and call functions in it from the application open event.

A batch program of mine writes status messages to a log file and can run a long time. The run time can be anywhere from 1 minute to over an hour depending on the input volume. PowerBuilder batch programs do not have the ability to write to the command window so the user monitoring the program has no way to tell how it is progressing without periodically opening the log file.

I searched the web looking for a solution that would allow my program to write to the command window. I came across the following article from the PowerBuilder Developer’s Journal:

Writing Console Applications with PowerBuilder 9

By XUE-SONG WU

http://pbdj.sys-con.com/node/42603

This article is a demo of PBNI, a feature introduced in version 9 that allows for two types of C/C++ integration. The first type allows C/C++ programs to call PowerBuilder code. The second type allows you to create PowerBuilder objects in C/C++. The article uses both types.

First is an executable that uses the PBNI function RunApplication to execute an application starting with the open event. Second is a .PBX file which is similar to a .DLL file. In PowerBuilder you right click a library in the system tree and select ‘Import PB Extension’ from the popup menu. Choose the .PBX file and an object is created that forms an interface to the .PBX that you can use just like it were a regular PowerScript object. The object contains functions that you use to interact with the command window.

I made a couple of improvements:

Upgraded the project from Visual C/C++ 6 to Visual Studio 2012.

Added an argument to specify the PowerBuilder VM filename so that it can be used with any version of PowerBuilder.

Added an optional argument that is passed to the application open event commandline argument.

Added a function to set a program return code.

Added a program icon to the executable and changed its name from ‘pb’ to ‘pbconsole’.

I put a zip file containing the Visual Studio project on my website. It includes PowerBuilder 9 and 10.5 examples. You can download it from here:

Topwiz Software – Tools

To report this post you need to login first.

8 Comments

You must be Logged on to comment or reply to a post.

  1. Sébastien Kirche

    Hi Roland,

    nice to see some someone that keeps publishing tools for PB 😘

    We had developped a plain vanilla console object (nonvisual object + internal struct definitions + api calls) that let create a console when your application is visual, write some (colored) text, but cannot read from the console, though (we had no need for this).

    I could post the code, if you wish, maybe that you could integrate the fuctionnalities?

    (0) 
  2. Joseph Vendra

    There was an original posting by Bard Mettee posted on May 7, 2012 7:21 AM:

    http://nntp-archive.sybase.com/nntp-archive/action/article/%3C4fa67f28.5a43.1681692777%40sybase.com%3E

    I improved upon his code slightly to resolve the hanging prompt which did not push enter for the user and re-post the solution here:

    /*

    GF_Console_Output(String AS_STR)

    // External Declarations worked on Windows 7 Enterprise 64 Bit + PowerBuilder 12.50 for Writing CommandLine arguments to command prompt window.

    FUNCTION Int AttachConsole(long ProcID) library “Kernel32.dll”

    FUNCTION Long GetStdHandle(long nStdHandle) library “Kernel32.dll”

    FUNCTION Int FreeConsole() library “Kernel32.dll”

    FUNCTION Ulong WriteConsole(long Handle, String OutPut, long NumCharsToWrite, ref long NumCharsWritten, long reserved) library “Kernel32.dll” Alias For “WriteConsoleW”

    SUBROUTINE keybd_event( int bVk, int bScan, int dwFlags, int dwExtraInfo) LIBRARY “user32.dll”

    SUBROUTINE ExitProcess(ulong uExitCode) LIBRARY “kernel32.dll”

    */

    // PowerScript Code

    // GF_Console_Output(string as_Str)

    long console, hwnd

    AttachConsole(-1)

    console = GetStdHandle(-12)

    hwnd = GetStdHandle(-12)

    string s

    long result

    s = as_Str

    s = “~r~n” + s + “~r~n”

    WriteConsole(hwnd, s, len(s), result, 0)

    WriteConsole(hwnd, CharA(13), len(CharA(13)), result,0)

    // Send VK_RETURN 0x0D  immediately to the console after last writeconsole is completed.

    keybd_event( 13, 1, 0, 0 )  // this solves the hanging prompt issue.

    FreeConsole() // de-attach from console

    ExitProcess(1)

    RETURN

    // Full example:

    // In your application open object you would have the following:

    string ls_cmd

    ls_cmd = Trim(CommandParm())

    // Then you would analyze it and exit if the commandline was not set or set correctly etc

    if isNull(ls_cmd) or ls_cmd = “” then

        // cannot process no args.

        GI_ERRORLEVEL = -1

        gf_console_output(“YourCoolProgram.exe v1.00 – Parameter not set”)

        HALT CLOSE

    end if

    You can get all kinds of ideas here-after how to use this.

    No more hanging prompt

    (0) 
      1. Roland Smith Post author

        I went through all the functions to make sure the datatypes and argument names were correct.

        Function boolean AttachConsole(ulong dwProcessId) Library “kernel32.dll”

        Function boolean FreeConsole() Library “kernel32.dll”

        Function long GetStdHandle(ulong nStdHandle) Library “kernel32.dll”

        Function boolean WriteConsole(long hConsoleOutput, string lpBuffer, ulong nNumberOfCharsToWrite, ref ulong lpNumberOfCharsWritten, ulong lpReserved) Library “kernel32.dll” Alias For “WriteConsoleW”

        Subroutine keybd_event(integer bVk, integer bScan, ulong dwFlags, ulong dwExtraInfo) Library “user32.dll”

        Also, I used -11 (STD_OUTPUT_HANDLE) for the call to GetStdHandle.

        (0) 
        1. Joseph Vendra

          So based on your recommended changes to the external declaration’s I updated the sample GF_Console_Output() example code as follows.

          fixed result variable changing from long result to ulong result

          This example also now uses an array so you build your string array with a bunch of messages for the console then call the function with the array and it pops it all out. 

          // GF_Console_Output(string as_Str[], AB_FreeConsole) – PowerScript Code

          long console, hwnd

          AttachConsole(-1)

          console = GetStdHandle(-11) // instead of -12

          hwnd = GetStdHandle(-11)    // instead of -12

          /*

          https://msdn.microsoft.com/en-us/library/windows/desktop/ms683231%28v=vs.85%29.aspx

          STD_OUTPUT_HANDLE        (DWORD)-11    The standard output device. Initially, this is the active console screen buffer, CONOUT$.

          STD_ERROR_HANDLE           (DWORD)-12      The standard error device. Initially, this is the active console screen buffer, CONOUT$.

          */

          string s

          ulong result        // changed from long result   to ulong result based on latest External Declaration format.

          integer i

          if upperbound(as_str[]) <= 0 then return -1

          // process all args passed in

          for i = 1 to upperbound(as_str[])

              s = as_Str[i]

              s = “~r~n” + s + “~r~n”

              WriteConsole(hwnd, s, len(s), result, 0)

              WriteConsole(hwnd, CharA(13), len(CharA(13)), result,0)

          next

          // Subroutine keybd_event(Byte bVk,Byte bScan,ulong dwFlags,ulong dwExtraInfo) LIBRARY “user32.dll”

          // Send VK_RETURN 0x0D  immediately to the console after last writeconsole is completed.

          keybd_event( 13, 1, 0, 0 )

          // FreeConsole() // de-attach from console

          // ExitProcess(1)

          IF AB_FreeConsole THEN

              // Only de-attach from console if you dont plan on writing subsequent content in between other activities.

              FreeConsole() // de-attach from console

              ExitProcess(1)

          END IF

          RETURN 0

          Everything works, except providing you dont do any processing after the initial console dump.

          For example after the first console dump if you run some additional functions or code, calling the console dump again doesn’t seem to work, probably handle changes?

          After playing around realized needed to extend the function again a bit.
          Added argument AB_FreeConsole to support writing multiple passes into console in between other function calls writes or other work.

          IF AB_FreeConsole THEN

              // Only de-attach from console if you dont plan on writing subsequent content in between other activities.

              FreeConsole() // de-attach from console

              ExitProcess(1)

          END IF

          So now I can run the program WITH the proper args, it outputs a “header’ output , does some work, I can call the gf_Console_Output(AS_Msgs[], FALSE) and fire some console output in between stuff too now.

          The final write I make to console i call gf_Console_Output(AS_Msgs[], TRUE) and thats it..

          (0) 
          1. Roland Smith Post author

            Attached is an NVO I created. It has 4 functions that you call:

            of_attachconsole

            of_freeconsole

            of_writeconsole

            of_writetoconsole

            Call of_attachconsole once at program startup and of_freeconsole once at program end. You can call of_writeconsole as many times as you want. The function of_writetoconsole doesn’t write a CRLF so can be used to append text to the current line.

            $PBExportHeader$n_console.sru

            forward

            global type n_console from nonvisualobject

            end type

            end forward

            global type n_console from nonvisualobject autoinstantiate

            end type

            type prototypes

            Function boolean AttachConsole ( &

              ulong dwProcessId &

              ) Library “kernel32.dll”

            Function boolean FreeConsole ( &

              ) Library “kernel32.dll”

            Function long GetStdHandle ( &

              ulong nStdHandle &

              ) Library “kernel32.dll”

            Function boolean WriteConsole ( &

              long hConsoleOutput, &

              string lpBuffer, &

              ulong nNumberOfCharsToWrite, &

              Ref ulong lpNumberOfCharsWritten, &

              ulong lpReserved &

              ) Library “kernel32.dll” Alias For “WriteConsoleW”

            Subroutine keybd_event ( &

              integer bVk, &

              integer bScan, &

              ulong dwFlags, &

              ulong dwExtraInfo &

              ) Library “user32.dll”

            end prototypes

            type variables

            Constant String CRLF = “~r~n”

            Boolean IsAttached = False

            Long hWnd = 0

            end variables

            forward prototypes

            public function boolean of_attachconsole ()

            public subroutine of_freeconsole ()

            public subroutine of_writeconsole (string as_msgtext)

            public subroutine of_writetoconsole (string as_msgtext)

            end prototypes

            public function boolean of_attachconsole ();// attach the current process to the calling console window

            Constant long INVALID_HANDLE_VALUE = -1

            Constant ulong ATTACH_PARENT_PROCESS = -1

            Constant ulong STD_OUTPUT_HANDLE = -11

            ULong lul_Written

            IsAttached = False

            // attach to the console

            If AttachConsole(ATTACH_PARENT_PROCESS) Then

              // get a handle to standard output

              hWnd = GetStdHandle(STD_OUTPUT_HANDLE)

              If hWnd = INVALID_HANDLE_VALUE Then

              FreeConsole()

              Return False

              End If

              IsAttached = True

            End If

            Return True

            end function

            public subroutine of_freeconsole ();// free the attached console

            If IsAttached Then

              // send VK_RETURN 0x0D keystroke to give back keyboard control

              keybd_event(13, 1, 0, 0)

              // free the console

              FreeConsole()

              IsAttached = False

            End If

            end subroutine

            public subroutine of_writeconsole (string as_msgtext);// write message to the attached console with

            // CRLF so that it appears on the next line

            ULong lul_Written

            If IsAttached Then

              // write CRLF to the console

              WriteConsole(hWnd, CRLF, 2, lul_Written, 0)

              // write message to the console

              WriteConsole(hWnd, as_msgtext, Len(as_msgtext), lul_Written, 0)

            End If

            end subroutine

            public subroutine of_writetoconsole (string as_msgtext);// write message to the attached console without

            // CRLF so that it appears on the same line

            ULong lul_Written

            If IsAttached Then

              // write message to the console

              WriteConsole(hWnd, as_msgtext, Len(as_msgtext), lul_Written, 0)

            End If

            end subroutine

            on n_console.create

            call super::create

            TriggerEvent( this, “constructor” )

            end on

            on n_console.destroy

            TriggerEvent( this, “destructor” )

            call super::destroy

            end on

            (0) 

Leave a Reply