Skip to Content
Author's profile photo Former Member

Writing to the DOS Command Window

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

Assigned Tags

      8 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Former Member
      Former Member

      Hi Roland;

        Very "kool" 😎 .. thanks for the update!

      Regards ... Chris

      Author's profile photo Former Member
      Former Member

      I remember that article,  I have it's link stored somewhere in my various online PB resources.

      Author's profile photo Former Member
      Former Member

      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?

      Author's profile photo Former Member
      Former Member

      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

      Author's profile photo Former Member
      Former Member
      Blog Post Author

      Thanks for posting this, it works great!

      Author's profile photo Former Member
      Former Member
      Blog 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.

      Author's profile photo Former Member
      Former Member

      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..

      Author's profile photo Former Member
      Former Member
      Blog 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