Welcome!

PowerBuilder Authors: Chris Pollach, Yeshim Deniz, Jayaram Krishnaswamy, Kevin Benedict, Avi Rosenthal

Related Topics: PowerBuilder

PowerBuilder: Article

Writing Console Applications with PowerBuilder 9

Writing Console Applications with PowerBuilder 9

From the PowerBuilder forums I've discovered that quite a few people are requesting a feature that allows them to write console applications with PowerBuilder. PowerBuilder doesn't support this feature natively right now, but we can already do it with PowerBuilder 9Swith the help of PBNI.

There are two reasons you can't create console applications with the PowerBuilder IDE. One, PowerBuilder doesn't provide functions or classes for writing strings to a console and getting input from the console; two, the executables created by the PowerBuilder IDE are always for Windows GUI applications.

Fortunately, both problems can be solved using PBNI technology. First we write a PBNI extension that exposes a PB native class named console that provides functions for getting input from the console and writing output to the console. Then we write a small console stub in C++ that loads the PBVM (PowerBuilder Virtual Machine) and runs a PB application that utilizes the console native class.

This article shows how to write the console native class and a generic console stub in C++ that run a PB application, and how to use them to create PB console applications.

The article includes a .zip file, console.zip, that contains the source code for the console PBNI extension and the C++ console stub, and a PB application demonstrating how to create a PB console application. This file can be downloaded from www.sys-con.com/pbdj/ sourcec.cfm. The .zip file contains four subdirectories - bin, ext, pb, and test - under the console directory. The bin directory contains the compiled binary files: console.pbx, console.pbd, and pb.exe. The ext directory contains the source file and project file for the console extension. The pb directory contains both the source and project files for the C++ console stub. The test directory contains a PB application for testing the console extension and the C++ console stub.

Figure 1 shows the relationships between the console stub pb.exe, PBVM, console.pbx, and PB application.

The Console PBNI Extension
The console extension provides a native class called console. The description of the extension is as follows:

static const TCHAR desc[] = {
"class console from nonvisualobject "
"function int read() "
"function string readline() "
"subroutine write(string s) "
"subroutine writeline(string s) "
"end class "
};

The read() function gets a character from the input stream; the readLine() function gets a line from the input stream; the write() function puts a string to the output stream; and the writeLine() function puts a string followed by a newline to the output stream.

We create a C++ class, PBConsole, that implements the IPBX_NonVisualObject interface. Listing 1 provides the declaration for the PBConsole class.

The four private functions - read(), readLine(), write(), and writeLine() - correspond to the four functions of the console native class, respectively. We use the standard C++ iostream to implement the four functions. Standard C++ defines two global variables: cin and cout. cin is for controlling extractions from the standard input as a byte stream; cout is for controlling insertions to the standard output as a byte stream. The code is rather simple (see Listing 2).

You may notice that tcin and tcout are used in this listing instead of cin and cout. This makes it possible to compile the PBNI extension in both ANSI and Unicode versions. When _UNICODE is defined, the Visual C++ compiler will compile the extension in the Unicode version, and the global variables wcin and wcout will be used instead of cin and cout. Likewise, the type wstring will be used instead of string.

To make the coding easier, we define a set of macros starting with "t," including tstring, tcin, and tcout. When _UNICODE is defined, they will map to wstring, wcin, and wcout, respectively. The three macros are defined as follows:

#if defined(_UNICODE)
#define PBTEXT(s) L##s
#define tstring std::wstring
#define tcin std::wcin
#define tcout std::wcout
#define
#else
#define PBTEXT(s) s
#define tstring std::string
#define tcin std::cin
#define tcout std::cout
#endif

The macro PBTEXT is for making a constant string to a Unicode string if _UNICODE is defined. PowerBuilder 10 will support Unicode. To make your PBNI extensions able to be compiled in both ANSI and Unicode versions, the above mentioned technique is a common way to achieve this.

The source file of the console extension, console.cpp, is located in the ext directory. Open the Visual C++ project file, console.dsp, with Visual Studio 6 and build the project; you'll get the console.pbx in the bin directory. Run pbx2pbd90.exe to generate the .pbd file, console.pbd, for the extension.

Write the C++ Console Stub
You may wonder if we can use the console extension in normal PB applications to create console applications. The answer is no, because a normal PB application is a Windows GUI application that doesn't open a console window. Calling functions of the console native class in a GUI application will not have any effect.

The C++ console stub is pretty simple. It just loads the PBVM, gets the pointer to the IPB_VM interface, and calls the RunApplication() function on the interface pointer. The only thing we need to consider is how to make the stub generic.

Each PB application has an application-derived class and a list of PBL (or PBD) files (called library list in PB's terminology). The RunApplication() function takes the name of the application-derived class and the library list as parameters. After some initialization work, the function will trigger the open event of the application object, causing the script of the open event to be executed.

If the name of the application class and the library list can be passed to the console stub through the command line, the console stub will be able to load all PB console applications. Java.exe is very likely doing it the same way.

Listing 3 provides the source code for the console stub.

The parseOptions() function gets the application name and the library list from the command line.

The LibList helper class converts the vector of strings to a C array of pointers to zero-terminated strings, which is required by the RunApplication() function.

The source file, pb.cpp, is located in the pb directory. Building the Visual C++ project, pb.dsp, will generate pb.exe located in the bin directory. The usage of pb.exe is:

pb application_name library_list

If there is more than one library in the library list, use a semicolon ";" to separate them. For example, pb testconsole testconsole.pbl;console.pbd.

Creating PB Console Applications
You can do virtually everything in a PB console application except open windows. You can create a nonvisual object and call functions on the object; you can also create a DataStore and retrieve data from a database. If you need to get a user's input or want to print something to the console, you can use the console native class. The following PowerScript code is an example:

console con
con = create console

con.writeline("PB console application")
con.write("Input your name please: ")
string s
s = con.readLine()
con.writeLine("Hello, " + s)

First, create an instance of the console class, then call the writeline() function to print a line of text, "PB console application", to the console, followed by printing "Input your name please:" to the console by calling the write() function. After that, call the readline() function to get the user's input. Finally print the hello message to the console.

To run the PB console application, you need to add the bin subdirectory to the PATH, then open a Command console, go to the directory where the PB application is located, and type the command:

pb testconsole testconsole.pbl;console.pbd

When the application asks to input your name, input PBDJ, and you'll get the result shown in Figure 2.

If you want to convert the PBL files, e.g., the testconsole.pbl file, to PBD files, you can create a normal application project to create an executable file, but without appending the PBD files to the .exe file. That way, you can get the PBD files you want. The .exe file generated is actually a stub for a Windows GUI application, and the pb.exe is for replacing it.

Conclusion
As you can see in this article, PBNI enables you to do a lot of creative things. Although the future versions of the PowerBuilder IDE might support creating console applications directly, you can use the technique described in this article with PowerBuilder 9.

The console native class can be further enhanced; for example, we could add a readXXX function for each PB data type, or we could add a write function for each PB data type. With the enhanced console class in hand, it would be quite easy for you to write PB console applications.

More Stories By Xue-song Wu

Xue-song Wu, a staff software engineer in Sybase Asia Development Center, Singapore, team leader of PowerBuilder Virtual Machine and Compiler team.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.