Anda di halaman 1dari 56

JOURNAL FOR PEOPLE INTERESTED IN CLIENT/SERVER AND INTERNET / INTRANET

DEVELOPMENT WITH POWERBUILDER, POWERSITE, POWER++ AND POWERJ


MARCH/APRIL 1999

PRICE: US$ 7.50

Announcing the Sybase


Developer Network

Contents
Editorial: Announcing the Sybase
Developer Network on Page 1
PFC Place: PFC 7.0 Highlights
by Boris Gasin on Page 3

New Features:
PowerBuilder 7, the HTML DataWindow
by Larry Cermak on Page 7

COM Knowledge:
PowerBuilder and COM, part 2
by Alan J. Walsh on Page 14

Advertiser Index on page 18


Distributed Ways: Developing Component
Based Applications
by Sean Flynn on Page 21

Advanced PowerBuilder:
Thread Synchronization
by Roy Kiesler on Page 27

Internet Aware: Internet-Enable your


Application with the PowerSocket Library
by Tim Roberts on Page 32

InfoPower:
Customizing InfoMaker
by Terry Dykstra on Page 41

Little Helpers:
PBL Peeper 1.1.0 by Terry Voth
reviewed by Arthur Hefti on Page 47

PowerTip:
Determine the Original Size of a Window
by Arthur Hefti on Page 49

User Group News

VOL. 6 / ISSUE 2

Instead of an editorial, we pass the word to Sybase


and more precisely to Marlana Patton, editor of the
online magazine "Powerline", to announce a new
resource for developers using Sybase technology.
"To promote greater success among developers using
Sybase application tools and server products, Sybase
has created the Sybase Developer Network (SDN).
SDNs dedicated Web site, available at
www.sybase.com/sdn, will provide developers with
focused, timely information that clearly illustrates the
best ways to use Sybase technologies and solutions.
The company canvassed the experts on Sybase
products, both within and outside the corporation,
collecting the newest information available from
many different sources, including product engineers
and product managers, technical evangelists and
professional service consultants, and technical
support engineers and partners.
The result - a site that developers can return to again
and again for Sybases newest informational
resources, including:
Comprehensive
technical
articles
and
whitepapers
Development tips and techniques
Free, sample, and beta software
Opportunities to interact with product experts
Technical presentations previously reserved for
internal audiences
Advance notice of product issues with
workarounds
Sybase wants to ensure comprehensive access to
developer information from any touch point on any of
its Web sites. To achieve this level of content
delivery, Sybase is building a central, corporate-wide
information repository, which will dynamically

beginning on page 50

(Continued on page 2)

Page 1

Impressum

(Continued from page 1)

provide the content developers need to understand


product strategies and to successfully implement
business solutions.
Currently, SDN includes technical information
about Sybases Mobile and Embedded, and
Application Development technologies and
solutions. During the remainder of 1999 and into
2000, the intent is to include information about all
Sybase products, and to allow more complete
access to partner products and solutions as well.
Sybase welcomes comments, questions, and
suggestions regarding the new SDN at the following
email address: sdn@sybase.com."

We would like to take this opportunity to thank


Peter Horwood, who has been the responsible for
the Power++ / PowerJ column in PowerTimes.
Peter is now publisher of the new magazine
"SilverStream Journal" (www.s3journal.com) and
will concentrate on his new tasks. He confirmed that
he will continue to support PowerTimes and even
provide an article from time to time. Thanks, Peter!
Starting with the next issue Mark Pfeifer, member
of Team Powersoft, will take up the responsibility
of the PowerJ column. Welcome, Mark!
You may know that the editors of PowerTimes and
the responsible of the Little Helpers column, Arthur
Hefti, have organised PowerBuilder as well as PB/
Internet conferences in Switzerland over the past
few years. Well, this year the Swiss PowerBuilder
User Group and the Swiss Sybase User Group join
forces to organise a 2-day technical conference in
Switzerland. Guess who is involved in its
organisation? You can find more information on the
website of the SPBUG at http://www.spbug.ch. It's
a pleasure for PowerTimes to support this
conference and, by the way, Mark Pfeifer and T.
Carson Hager, who both write for PowerTimes, will
be present at this event! And they will give
autographs!
Please note the following change in the
PowerTimes distribution policy: starting with the
next issue (6/3), we will no longer be sending
PowerTimes directly to you. Instead, it will be
available on the PowerTimes website for you to
download at your convenience. You will be
informed when it will be available, together with the
appropriate password for opening.

PowerTimes is an international PowerBuilder Journal pu-

blished 6 times a year mainly destined to PB User Groups


worldwide. If your User Group does not subscribe to
PowerTimes and you would like to receive it, then you can
subscribe to it on an individual basis.

Contact addresses:
Editor:

Rolf Andr Klaedtke


Bchlistrasse 21
CH-8280 Kreuzlingen / Switzerland
Fax: ++41 - (0)71 - 670 01 71
e-mail: rak@powertimes.com

Co-Editor:

Mark A. Lansing
Eichmatt 17
CH-6343 Rotkreuz / Switzerland
Fax: ++41 - (0)41 - 790 74 79
e-mail: mlansing@powertimes.com

On the web:

http://www.powertimes.com

Column responsibles:
Little Helpers:

Arthur Hefti
arthur@catsoft.ch

PFC Place:

Boris Gasin
bgasin@dynamictechgroup.com

EASy Street:

T. Carson Hager
chager@dyn-data.com

Know How:

Bernie Metzger
bmetzger@kevsys.com

Practical UML:

Else-Marie stling
emostling@birkagroup.com

Distributed Ways:

Sean Flynn
sflynn@goinfinity.com

Subscription rates:
Individual:
User Group:

US$ 45 per year


US$ 750 per year

For user group subscriptions, the group has the right to


distribute PowerTimes to their members in an electronic or
printed form at no extra charge ! PowerTimes is produced
as an electronic document in Adobe Acrobat format.
Disclaimer:
Articles or opinions published in PowerTimes do not
necessarily reflect the opinion of the editorial team. They
reflect the opinion of the submitter. We assume that the
copyright of all submitted articles is owned by or granted
to the submitter. We must decline every responsability
regarding technical correctness or truth of submitted
articles and opinions.

Happy reading,
Rolf Andr Klaedtke and Mark Lansing

Page 2

PFC 7.0 HIGHLIGHTS


by Boris Gasin

Introduction
When compared to other new features of the Enterprise Application Studio 3.0 release, it may seem
that PFC did not get much attention. There were no
major new features added to PFC 7.0 release. However, even though the spotlight was elsewhere, PFC
did receive quite a few bug fixes and other changes
needed to bring the class library up to speed with the
PowerBuilder 7.0 release. There were over 100 issues in the PFC bug queue prior to the 7.0 release.
Most of them were resolved in PFC 7.0.
In this months column I will highlight some of the
major changes in PFC 7.0 while making an emphasis
on the migration issues.

Object Level Structures


The first major enhancement is actually a result of
the change in the previous PowerBuilder release. If
you have been working with PFC 6.0 this message
may look familiar Warning
C0190: Instance variables of local
structure type ('<local_structure>') will be implicitly private in the next release.
PowerBuilder allows you to declare a structure class
within another class. This structure is often refered
to as an object level structure. Sometimes these
structures are also called local, while the application level structures are named global. This terminology is OK as long as the difference between the
class definition and a variable scope is clear. Object
level (local) structure is an object defined within
another object. Typically a user object or a window.
Its class definition is saved within the parent objects
class definition. The class definition cannot be
accessed from another object. Application level
(global) structures are defined through the structure
painter, stored as separate objects and can be referenced throughout the application.

blic or protected caused intermittent compile and run


time errors.
The solution chosen by PowerBuilder developers
was to limit the object level structure scope to private. However, in version 6 the limitation was
placed only on the new code. The old objects with
public or protected object structures were allowed to
migrate, while access to the migrated structure from
the descendant objects was not permitted.
PFC itself contains several object level structures.
Typically they were well encapsulated, reducing the
need to access them from the descendant objects.
One exception may be a case of overriding the event
or function to fix a bug and copying the ancestor
code. If the ancestor event references an object level
structure the descendant code would not compile.
In PFC 7.0 all the object level structures were replaced by the autoinstntiated NVOs. Behind the scenes,
structures behave a lot like autoinstantiated NVOs.
Structures are auto-matically created when a reference variable comes into scope and destroyed when
it goes out of scope. There is one major distinction.
NVOs support functions and inheritance. This makes them much better suited data containers. Functions can be used to present the data in a different
format and hide the internal structure. Inheritance
allows us to add new data members in the extension
layer.
On to the migration issues. Changing the structures
to NVOs presented a dilemma. The instance variables referencing the newly added attribute NVOs
were changed to remain consistent with the PFC
naming convention. Typically this would have prevented any project which referenced the variables by
their old names from being migrated to 7.0. In order
to allow the migration to proceed, an extra variable
was added for each attribute NVO referencing it by
the old (structure) name.

In PowerBuilder 4 and 5 it was possible to declare


an instance variable of an object structure class type
and scope the access to private, public or protected.
However, object level structures were not designed
to be used outside of the parent object itself, even in
the descendant. Declaring object structures as pu-

Page 3

(Continued on page 4)

date.

(Continued from page 3)

Old Variable
pfc_n_cst_dssrv_multitable
istr_newupdate[]
pfc_n_cst_dwsrv_dropdownsearch
istr_newupdate[]
pfc_n_cst_dssrv_multitable
istr_newupdate[]
pfc_n_cst_dwsrv_querymode
istr_querymodeinfo[]
pfc_n_cst_dssrv_multitable
istr_newupdate[]
pfc_n_cst_dwsrv_resize
istr_registered[]
pfc_n_cst_winsrv_statusbar
istr_dwobjects[]
pfc_n_cst_ resize
istr_registered[]
pfc_n_cst_winsrv_sheetmanager
istr_sheet[]
pfc_w_toolbar
istr_toolbar[]
pfc_n_cst_dwcache
istr_cachelist[]
pfc_n_cst_trregistration
istr_trans[]
pfc_u_tv
istr_ds[]

New Variable

// Initialise test values.


ldt_invalid = date("50/50/1900")

inv_newupdate[]

inv_newupdate[]

// Check for invalid values.


If
ad_source = ldt_invalid Then
Return False
End If

inv_newupdate[]
inv_querymodeinfo[]

Unfortunately, in the current version any conversion


from an invalid string date will result in the variable being set to 1/1/1900, which is as far as
PowerBuilder is concerned is a valid date. Most of
the datetime fixes revolved around this issue. Changes were made to eliminate the comparisons to an
Invalid date. If you have used a similar technique
you will need to revisit your code.

inv_newupdate[]
inv_registered[]
inv_dwobjects[]
inv_registered[]
inv_ sheet[]

Auto-instantiated NVOs

inv_toolbar[]

The bug related to auto-instantiated NVOs was mentioned in the February '98 issue of PowerTimes;
Object Control: Adding Object Management to the
PFC article by Michael Gora.

inv_cachelist[]
inv_trans[]
inv_ds[]

The source for every object created in PowerBuilder


contains a global variable declaration of the same
name. If you export an object you will see a line
similar to:

Table 1, Object level structure variables


Only the new NVO variables are referenced by the
PFC code. If you have referenced any of the variables in Table 1 in the extension code, be sure to
change them to the new inv_xxx variables. All of
the above objects are autoinstantiated. This means
that declaring an extra variable will cause an additional instance of the object to be created in memory.
The performance hit should be negligible because
PowerBuilder caches object class definition. However, if you are uneasy about having an unused object
in memory after a successful migration go ahead and
remove all the istr_xxx variables from the PFC
objects. This will also validate that all of the previously referenced structure variables have been
switched to the new ones.

global w_frame w_frame

This allows a developer to reference objects without


declaring a variable.
Open(w_frame)

There were quite a few date-related bug fixes in this


release. Most of the bugs were related to the
of_isValid() function in the datetime service. Starting with the version 5.0.03 there was a change in the
date and datetime behaviour. In the current release of
PowerBuilder a date or a datetime variable cannot
contain an invalid date. The date variable can only
contain a Null or a valid date. This was not always
the case. In the earlier releases a date variable could
contain an invalid date.

The same variable was declared for the autoinstantieted NVOs. However, in an auto-instantieted
NVO simply declaring a global variable results in its
creation. Therefore the auto-instantiated NVOs
should not have a global variable declaration in its
source. Doing so will result in creation of an extra
global instance of any auto-instantiated NVO! The
constructor event firing twice is also a by-product of
the same bug. The good news is that the bug was
fixed over a year ago. So why are we still talking
about it? Well, the bad news is that this was a
painter bug. This means that even although any
newly created auto-instantiated (AI) objects will not
have the global variable declaration, any existing
objects, with the variable already in the source code,
will not be fixed until you explicitly open and save
the object. Regenerate does not modify the source
code. This task was done in PFC 7.0, but there is a
migration gotcha. If you keep your existing extension layer, chances are that the AI NVOs in yur

Many PFC functions had specific tests for an invalid

(Continued on page 6)

Date Issues

Page 4

Page 5

(Continued from page 4)

extension layer are still broken. To test your objects


you can perform a simple test. Export any AI NVO.
For example I will use a n_cst_dirattrib. View the
source. If it contains a line below
global n_cst_dirattrib n_cst_dirattrib

you will need to open and save every AI NVO in


your extension layer(s). You should repeat the test
for all the AI NVOs or simply open and save them as
a precautionary measure.

Other Enhancements
The last change worth mentioning is a bug fix to the
statusbar window pfc_w_statusbar. To align the
status window with the MDI microhelp area, the
of_GetSystemSettings function read the registry to
determine the current value of the window border
width:
// NT 4.0 registry.
registryget('hkey_current_user\control
panel\desktop\windowmetrics','borderwidth',
ls_temp)
li_border = abs(integer(ls_temp)) * 15
ii_borderheight = unitstopixels (li_border,
yunitstopixels!)

I have nothing but respect for PFC developers, who


even in their blunders get the code to work, at least
most of the time . The first problem is that unitstopixels conversion should have been pixelstounits
(the registry contains values related to pixels). Second issue is that the multiplication by factor of 15
should be a division. Would you believe two wrongs
cancel each other out making a right! Well, almost
The problem is that the registry to pixel coefficient
of 15 may vary depending upon the selected system
font size or whether the control panel settings were
ever configured. If you have implemented a fix that
reads the registry for the value of window border, it
may still fail in some cases.

can enable or disable the screensaver, change accessibility options, control various keyboard and mouse
settings, and so on A detailed description of the
function arguments and options is available at the
MSDN site:
http://msdn.microsoft.com/library/sdkdoc/sysmgmt/
sysinfo_4p67.htm

Final Words
This article did not cover every modification and
bug fix in the PFC 7.0 release. Instead I attempted to
concentrate on a few issues which had the greatest
potential to affect migration or any changes allowing
new extension possibilities. The general migration
procedure remains the same as it did for the 6.0
release. The readme.txt file distributed with the new
release contains a complete list of every change in
PFC 7.0. A good resource for step by step migration
instructions is the December 97 PowerTimes article,
available for download at the PowerTimes website http://www.powertimes.com/download.htm
There is also a Sybase technical document
PowerBuilder Foundation Class Library: Upgrade
Strategy for Customized Extension Levels available
online http://techinfo.sybase.com/css/techinfo.nsf/DocId/
ID=44505

A better way to solve this problem is to get the value


through windows API, which is exactly what was
done in 7.0. A very versatile API call SystemParametersInfo was added to the win32 platform service,
which is the real reason I mentioned this bug in the
first place.
Function boolean SystemParametersInfoA
(uint wActon, uint wParam, REF int pvParam,
uint fUpdateProfile) Library "USER32.DLL"

Along with providing information about the current


window border width, the SystemParametersInfo
API function can read and set a multitude of other
system-wide parameters. To name just a few, you

?
Boris Gasin is a Chief
Technology Officer at
Dynamic
Technology
Group, a New Jersey based
consulting
company
specialising in Internet and
Client-Server technologies.
Boris is a member of Team
Powersoft - an elite group of
technically
proficient
Sybase product users who volunteer their time to
answer questions in various support forums. He is a
three time winner of the Team Powersoft MVP
award. Boris is a co-author of PowerBuilder
Foundation Class Professional Reference
published by McGraw-Hill, ISBN # 0-07-913267-7
and a contributing author of the PowerBuilder 6.0
Secrets of the Masters by SYS-Con Publishing.
He can be reached via email at
bgasin@dynamictechgroup.com.

Page 6

POWERBUILDER 7, THE HTML DATAWINDOW


by Larry Cermak

Introduction
As a frequent speaker at PowerBuilder user conferences and user group meetings, there are two questions that are asked time and time again. The first is
What is the future of PowerBuilder?. The second
is How can I move my PowerBuilder applications
to the web?. Companies do not want to rewrite their
applications to make them web enabled due to time
and budget constraints. In addition, these companies
want to leverage their existing PowerBuilder code
and expertise to quickly move their applications to
the web. The features in EAStudio can make this a
reality.
In answer to the first question, the DataWindow has
always been one of the features of PowerBuilder that
helped it be a successful product and place it ahead
of its competitors. The new DataWindow functionality introduced in version 7 will help keep it there.
And as for the second question, the features in
EAStudio can make moving PowerBuilder applications to the Web a reality. Of those features, the new
HTML DataWindow in PowerBuilder 7.0 is one of
the most exciting. Sybase has given me permission
to write articles about this new feature before its
release so I could offer proof of their commitment to
making EAStudio one of the premier enterprise development environments.
In this issue I will cover the basics and architecture
of the HTML DataWindow, and how to configure
the components (Jaguar and PowerDynamo) to
make this work. Subsequent issues will discuss
advanced features and what it takes to move your
PowerBuilder applications to the web. Naturally
there is more than just making the DataWindow run
in a browser. Providing access to your business
logic is equally as important. As you can probably
already tell from the comments thus far, EAStudio is
feature rich and is a viable option for enterprise
development.

Builder runtime DLLs or plug-ins on the client machine. In fact, there is no runtime or downloads
necessary to run the DataWindow in a browser. This
works by a page server, such as PowerDynamo or
Microsoft Active Server Pages, working together
with a transaction or component server. For this
issue, PowerDynamo and Jaguar CTS will be used.
An important point to note is that this functionality is
not limited to Jaguar and PowerDynamo. Microsoft
Internet Information Server (IIS), Microsoft Transaction Server (MTS), and Active Server Pages
(ASP) can be used to create an HTML DataWindow
web application.
Jaguar reads the DataWindow object definition from
the PowerBuilder library (.PBL) as well as the data
from the data source and converts it into HTML.
Server side scripts create a web page which includes
the generated HTML. Actions that are taken by the
user (clicking a button) send requests to the server,
which then processes the request and returns the
generated HTML. An example of an action is clicking a next page button which will return the next
page of data. Figure 1 depicts this process.

Hosting a DW Control in a Browser


There are four different ways to host a DataWindow
control in a browser and each serves a different
purpose. These include the DataWindow plug-in,
DataWindow Web ActiveX control, HTML DataWindow control and PowerSite DataWindow DTC.
Each will be described in the following paragraphs
and List 1, at the end, shows the pros and cons of
each implementation.
The DataWindow plug-in is a PSR or Powersoft
Report viewer. It is used to display reports that have
been previously saved to disk. This manner requires
that the plug-in is installed to the browser before
using it. This does not provide for database access, it
simply takes the PSR file and generates the HTML
to display it in the browser.

What is the HTML DataWindow?


The HTML DataWindow is just what the name
implies, the ability to generate a DataWindow in
HTML. This allows a browser to communicate with
a component server and have the server render the
HTML from the DataWindow without any Power-

The DataWindow Web ActiveX is an ActiveX control that is hosted in Internet Explorer. This is a fully
functional DataWindow control that provides client

Page 7

(Continued on page 8)

(Continued from page 7)

Web Server

Web Browser

Page Server

HTML

Figure 1, Processing an HTML request


side scripting in JavaScript or VBScript. It provides
the full event model and safe functions, meaning
no printing or saving to another format.

work and then future issues will discuss specific


implementation issues and more details, such as how
to trap DataWindow events in client side JavaScript.

The HTML DataWindow Control, my personal favorite, utilizes a component server and page server
to read the DataWindow definition from a PBL and
convert it into HTML which is passed to the browser. This implementation provides the most functionality and the GUI that most closely resembles a
DataWindow that runs in a Windows application.
The client side scripting capabilities provide client
side validation without requiring a round trip to the
server. In addition, DataWindow validation is converted into client side JavaScript, which once again
does not require a trip to the server.

The DataWindow in PowerBuilder 7 has many new


attributes for HTML generation. This includes items
for formatting, table generation, and customizing the
client and server side execution of the HTML DataWindow. Figure 2 represents the HTML table attributes which are used to format tables in HTML.
HTML tables are used often to format data. Figure
3 shows the HTML generation attributes. These are
used to customize what processing occurs on the
client and what occurs on the server. These attributes also directly affect the size of the downloaded
HTML and there are performance considerations
when designing the DataWindows. For example, the
Client Events attribute will create a larger HTML
download to provide the DataWindow events being
triggered, in JavaScript, on the client. These are
most of the same events that get triggered in a
standard DataWindow such as clicked, itemchanged,
and updatestart. As a
developer, you can write JaveScript code to be accessed when these events occur. A future issue will
review how to do this and the benefits.

The PowerSite DataWindow DTC reads the DataWindow definition from a PBL, generates server
side JavaScript that retrieves the data and generates
the HTML for the presentation style. Updates are
available if freeform style is used

Making the HTML DW Work


Now that the various implementations have been
described, it is time to see how to make this work.
For this example, the HTML DataWindow Control
will be demonstrated. A sample DataWindow will
be implemented which accesses information from a
database and provides insert and update capabilities.
The rest of this article will review how to make this

The first step, once the DataWindow has been created, is to check the HTML DataWindow box on the
general tab of the DataWindow properties. This

Page 8

(Continued on page 10)

Page 9

Figure 2, HTML table attributes

Figure 3, HTML generation attributes

Page 10

(Continued from page 10)

ndicates that the DataWindow will be used to generate HTML for data entry, database access, and
browser specific features. The default values will be
used for the HTML Table and HTML Generation
attributes. The sample DataWindow is a maintenance screen to enter and update PowerBuilder tips
that might be found on my web site (available at
www.ctpartner.com). If you want to see how the
DataWindow will look in a browser, choose Design/
HTML Preview from the dropdown menus. If you
made any changes, save, close, and reopen the DataWindow before doing the preview or the changes
will not be reflected.
Once the DataWindow has been created, Jaguar CTS
needs to be configured to have the HTML DataWindow package installed. This configuration should
automatically occur as part of the typical installation
process. To verify this, start the Jaguar Manager and
connect to Jaguar. In the treeview, under packages,
make sure that the DataWindow package exists with
the HTMLGenerator component.
The final configuration that is necessary is to install
PowerDynamo and set up a web site. Dynamo
includes a personal web server which makes is easy
to install and test locally. After the personal web
server has been started, open the Sybase Central tool
and create a connection profile to the web site. The
connection profile for this article was named
tips_dynamo and connected to the ODBC data

source named tips. The first time a connection is


established to a web site, PowerDynamo will prompt
if you want to create a web site on this connection.
Answer Yes to have it automatically create the web
site into the ODBC database. If you are curious,
take a look at the database after the web site is
created and you will see that it created tables whose
names begin with WEB.
Once the web site has been created into the database,
you must create a URL mapping to point to the site.
The Sybase Central utility is used to define this
mapping as well. Under the PowerDynamo utilities,
there is a configuration folder which contains a
Mappings option. Double-click on the Add Mapping entry to create the new mapping entry. This
example mapping was named /tips and references
our tips_dynamo web site as shown in figure 4. This
mapping is used to reference the site from the client
browser.

JavaScript Code
Now that everything has been configured, the next
step is to create an HTML page that contains JavaScript to connect to Jaguar, indicate the PBL and
dataobject to read, generate the HTML, connect to
the database, and finally retrieve the data. The main
JavaScript function calls will be explained to provide an understanding of the processing that is occurring.
The following statement creates a variable called
dwMine by connection to Jaguar and creating an
instance of the DataWindow/HTMLGenerator component. The second parameter to the CreateComponent function is the connection information to Jaguar.
dwMine= java.CreateComponent("DataWindow/
HTMLGenerator", "iiop://localhost:9000","jagadmin","","DataWindow/HTMLGenerator");

Once the component has been successfully created,


the next step is to call the method to set the PBL and
dataobject. As you can see, this uses the tips.pbl
library and the d_tips_header DataWindow.
dwMine.SetDWObject("tips.pbl","d_tips_heade
r")

Once the dataobject has been established, the


connection to the database is established in Jaguar
and then the data retrieved.
dwMine.SetHTMLObjectName ("dwMine");
dwMine.SetTrans("ODBC","ConnectString='DSN=
tips;UID=dba;PWD=sql'", "", "", "", "", "")

(Continued on page 12)

Figure 4, Creating a new URL mapping entry

Page 11

(Continued from page 11)


retVal = dwMine.Retrieve()

Conclusion

Once the data has been retrieved, several more settings need to be made. The first is to provide a URL
reference for the page. Each page is considered an
object and is typically referred to as document.
The following line of code tells the DataWindow to
reference the page with the attribute document.name.

The HTML DataWindow is definitely something to


get excited about with PowerBuilder 7. This provides a manner to truly web enable an existing PowerBuilder application or to create new web applications with PowerBuilder. In the next issue, I will
review the generated HTML, discuss some performance considerations, and demonstrate how to trap
the DataWindow events as they are fired.

dwMine.SetSelfLink (document.name, "");

The SetWeight function is used to tell the server


component which functionality should be enabled on
the client. Omitting any functionality will generate
less HTML that is transferred to the client browser.
There are five parameters to this function.

Updating data
Validating newly entered data
Client-side events
Allowing client side scripts to call methods of
the client control
Applying display formats to entered data

The following line of code tells the server to generate HTML for all the functionality for this example.
dwMine.SetWeight (true,true,true,true,
true)

Once all the server side configuration is complete,


the final statement generates the HTML and returns
it as a string to the browser. This is done by writing
the generated HTML to the URL reference stored in
document . The URL points to the local machine
(localhost), the website reference created above
(tips), and the HTML page w_tips_header.html.

?
Larry Cermak is a
Technology Partner
with
Corporate
Technology Partners,
Inc., an emerging
technology consulting
and training firm
specializing in enterprise solutions. Larry is a
member of Team Powersoft, CPD Professional,
member of the CPD Review Committee, writer for
PowerTimes journal, and frequent speaker at
Powersoft/Sybase conferences and seminars across
the country. Larry can be reached at 001-630-4282650 or lcermak@ctpartners.com. Check out his
tips and techniques at www.ctpartners.com.

document.Write( dwMine.Generate() );

(Continued on page 13)

Page 12

(Continued from page 12)

List 1 Pros and Cons of Different DataWindow Implementations


DataWindow Plug-in
Pros: No client side configuration except installing the plug-in
Can be used in both Internet Explorer and Netscape
Cons: No auto installation of plug-in
SaveAs and print are not secure on the client side
Read only, no updating allowed

DataWindow Web ActiveX


Pros: Fully functional DataWindow including events, functions, and dynamic modifications
Supports database updates
Cons: Requires one time download of a CAB file (about a megabyte)
Only works with Internet Explorer
No server side scripting

HTML DataWindow Control


Pros: Thin client solution
Works one page at a time
Supports database modifications
Database access occurs on the server. No client side database connectivity configuration
Supports some client side DataWindow events
Supports client and server side scripting
Cons: Depending on the configuration, may cause heavy load on the component server
Full DataWindow API not available
All DataWindow events not available
All page navigation, and database access requires a round trip to the server

PowerSite DataWindow DTC


Pros: Same as the HTML DataWindow with no client side scripting or events
No PowerBuilder DLLs required on the server
Cons: HTML generation is limited by JavaScript implementation
No client side scripting or DataWindow events
Updates only allowed with freeform presentation style

Page 13

POWERBUILDER AND COM: PART 2


By Alan J. Walsh

Interfaces Revisited
Last month we began our journey as COM developers by talking about interfaces, the building
blocks of COM. Then we saw how to put those
interfaces into action in PowerBuilder. As I promised, we will now look at ways to improve upon our
interfaces using the capabilities of PowerBuilder 6.5.
In our original example we saw that it was possible
to create an object in PowerBuilder and then expose
that code to Visual Basic by means of a COM
interface. So what else is there to talk about? Well
as it turns out, that code is not as optimized as it
could be. When we made that call from VB, we
were actually using something called an automation
interface.

your interfaces methods at runtime. This is why


some people refer to automation as late binding or
"dynamic invocation."
Why go to all this trouble? Well as it turns out, there
was a time when Visual Basic was only capable of
using something like automation interfaces. There
was simply no way for VB to obtain the necessary
information about an interface at compile time. For
this reason, the VB team created, or at least convin1
ced others to create IDispatch. Nowadays VB is not
limited to automation interfaces, but other clients
still are. Scripting clients are a good example. If,
for example, you want your code to be accessible
from an Active Server Pages script, you will have to
provide a dispatch interface.

Also known as a dispatch interface or


dispinterface, this kind of interface allows your
code to be called from the widest variety of tools. It
does this by exposing an interface called IDispatch,
which all COM-aware tools know how to talk to.
Many applications expose this interface to allow
other programs to have access and control. The
Microsoft Office applications are a good example of
this, and the original push behind COM, or OLE,
was to allow these tools to work together in an
"automated" fashion.

Performance

PowerBuilder itself provides an automation interface


and indeed this is exactly what our VB client was
using in the first example. From our clients perspective it appeared like they were directly accessing
our component, but this was just the result of some
fancy redirection in the registry. In reality, our client
was pointed right at PBVM60.DLL the PowerBuilder virtual machine.

Well, if you never plan on using your code in a


physically distributed environment, then you will
probably not notice a significant drop in performance. But if you are considering putting this code
in a distributed application then you must not use
automation - period. Why? Because each of those
extra functions becomes a round-trip on the network
and that is a very expensive performance hit.

Why Automation?

Best of Both Worlds

IDispatch contains just a handful of methods, and the


most important one as far as we are concerned is
called Invoke. By calling this method and passing in
a unique method identifier, called a DISPID, a client
can call any method in your component without
having to have explicit knowledge about the method
at compile time. This is the key point with automation interfaces: clients get all their information about

The alternative to automation interfaces is a


"normal" COM interface. In this scenario, our client
does have static knowledge about our interface and
2
its functions. Because they have this information at
compile time, not runtime, our client can access our
code without having to go through a dispatch inter-

So if automation guarantees us the widest coverage


among clients, why would we want to consider using
anything else? The answer is performance. There is
a price to pay for the kind of flexibility that automation provides us. The performance hit comes in the
form of some extra function calls that our client has
to make in order to navigate through the IDispatch
interface to get to our components real functions. It
only amounts to a few extra calls per function, which
doesnt sound like much, right?

Page 14

(Continued on page 16)

Page 15

(Continued from page 14)

face. Performance is much better and our client is


happy. But before you give up on automation altogether, let me tell you about a way to have your cake
and eat it too. There is a way to have your component provide an automation interface and a normal
interface at the same time. The trick is something
called a dual interface.
Dual interfaces were introduced in COM to solve
this problem of having to satisfy the needs of a
diverse group of clients. If you have clients that are
incapable of handling a custom interface, then you
will be forced to use automation. On the other hand,
you don't want to sacrifice performance for clients
that can handle a normal interface. A dual interface
solves this dilemma, and, as you might expect, it is
the default in PowerBuilder 6.5. As a matter of fact,
it is the only option when building COM components in PowerBuilder 6.5.

get a list of the available objects in your libraries.


Remember that only user objects are eligible. Check
the objects that you want to include and click OK.
You will notice that your Project Painter window
now lists these objects.
Click on the Properties button to open the window
where we'll do most of our work. You will see
something like Figure 1. The first tab on the Properties window is the General tab and it contains some
very basic information about our component. There
is a dropdown where you can specify the type of
server you are creating (DLL is the only option).
You can choose a name and location for your DLL,
and you can enter some comments about the component. Finally, there is a check box to indicate that
you want PowerBuilder to register everything when
you build.

Component Generator
Up until version 6.5, PowerBuilder support for COM
was very limited. The only option for creating COM
components was the OLE Generation Wizard that
we saw in the first part of this article. As we now
know, that limited us to an automation interface,
which severely limits our options in designing and
deploying our application. But Sybase was committed to component development and they made vast
improvements in the 6.5 release. Most notably they
added new component generators, including one for
COM. We'll use this generator to create the new,
more efficient version of our component.
We'll use the same PowerBuilder code that we had in
the example from the last issue. If you dont have
that code handy, go back to the last article and
recreate it - it was only three lines of code for our
receive() function. The component generators are
part of the Project Painter, so let's start by clicking
on that toolbar item. On the Select Project window
choose New to start a new project. You will notice
that there are now several choices here - assuming
that you have installed all of the options available
with version 6.5. If you do not see an icon for COM,
go back and run the component generator install
program. After you choose COM as your application type, you are presented with a rather plain
looking window that shows you the details of your
component. Not much to see here yet, but we are
about to fill in some of the blanks.

Options, Options

Figure 1: Object Properties

The next tab is labeled Libraries and it contains


some options for the libraries that we are using. This
is probably the most important part in the entire
process because it contains the one "gotcha" that I
think is likely to trip up a newbie. The Library Type
option is straightforward (P Code/Machine Code), as
is the additional libraries list. But the third option,
Directory Search Method, is the one that will cause
you grief, so please pay particular attention.

Let's start by choosing an object to generate our


component from. Go to the Edit, Select Objects to

Page 16

(Continued on page 17)

(Continued from page 16)

The default option here is "Search Directories in the


PATH Environment Variable." The problem with
that choice is that unless your files are in a directory
that is in your PATH statement, your code will not
be found and your component will not work. I
would recommend the other option, "Search the
Directory Specified in the Registry." This will work
nicely if you choose to have PowerBuilder register
everything when you build. The location of your
libraries will be stored in the registry and this is
where PowerBuilder will look when it tries to load
your component.
The next tab is Objects, and this is where we set
some of the COM specific options for our objects.
You'll notice that the first options are defaults, such
as the dropdown where we specify the type of interface. As I mentioned earlier, dual is the only choice.
You can also specify a default prefix for your Program IDs, and I would suggest picking a standard for
your company, or at least for each project. This is
the first part of the name that most of your clients
will use when referencing your component, so make
it a good one.
The lower portion of this window is where we set
object specific options. Highlight an object and then
choose its interface type and full Program ID. There
is a button labeled "Select Advanced Options."
Choosing this will allow you to specify the class
name and interface name that PowerBuilder will put
into your IDL. IDL stands for Interface Definition
Language, and it is the native language of COM and
other component architectures (CORBA). This is
the code that your clients will use to reference your
interface definitions at compile time. We'll talk
more about this after we generate our component, so
for now just take note of this option and accept the
defaults.
The last tab on the Properties window is for the Type
Library that will get generated, and the only options
are for version numbers and a help string. Click OK
to save your settings and return to the Project Painter.

Building the Component


The last step is of course to actually build the
component. Click on the Build toolbar button and
PowerBuilder will go through the steps to create
your component. When it is finished, you will see
the results (good or bad) at the bottom of the Project
Painter window. If it succeeded, here is what you
should have: your COM component - in the form of
a DLL, the registry entries, and an IDL file.

Putting it to Work
Now that we have a new version, let's see how we
make it work. For starters, the VB client code that
we used in Part 1 of this article will work as is. From
the client's perspective, nothing has changed. The
difference is that we are no longer using an automation interface for our component. Our component
has a dual interface, and because more recent versions of VB support the use of custom interfaces, that
is exactly what will happen. How does VB obtain
the necessary knowledge about our interface and its
methods? VB uses a type library for this purpose. I
talked about type libraries very briefly in Part 1, and
we saw that the OLE Generation Wizard created one
for us. Type libraries contain information about
interfaces in a binary format that many tools can read
and utilize.

IDL and C++ Clients


Clients written in C++ can also use our code now
because of the IDL file that PowerBuilder created
for us. IDL is the language that is used to express
COM interfaces, and sooner or later everything gets
boiled down to this. It is a relatively simple language that bears a strong resemblance to C. Let's
take a look at the IDL that PowerBuilder created for
our component when we built our project:
import "unknwn.idl";
import "oaidl.idl";
[
object,
uuid(4F363A34-E063-11D2-94B000A0C9AB6E49),
pointer_default(unique),
dual,
oleautomation
]
interface DIMessage : IDispatch
{
// properties for public instance variables
// methods
[ id(0) ] HRESULT send( [out, retval]
BSTR * retval );
};
// typelibrary and coclass
[
uuid(4F363A35-E063-11D2-94B000A0C9AB6E49),
lcid(0x0), version(1.0),
helpstring("")
]
library pbcomLib
{
[
uuid(4F363A33-E063-11D2-94B000A0C9AB6E49)
]
coclass CoMessage
{
interface DIMessage;
}
}

Page 17

(Continued on page 18)

(Continued from page 17)

Summary
Don't worry! You do not have to become an expert
on IDL to be a successful COM developer in PowerBuilder. Having a good understanding of IDL basics
will definitely help you, but it is not required. Armed with what you already know about COM, you
should be able to pick out some familiar things in
this code snippet. For example, we see some GUIDs
- those long, ugly strings of characters that we know
uniquely identify our COM objects. We also see
some keywords like "dual" and "oleautomation."
C++ clients do not actually use IDL files to access
COM. They need something native to their environment, like a header file. We can give them that by
"compiling" our IDL file. Microsoft provides a
utility called MIDL that takes raw IDL and compiles
it into something useful. The utility is distributed
with the Platform SDK and as part of Visual Studio.
MIDL generates several files when you compile an
IDL source, depending on the options that you specify in your code and on the command line. One of
the things that you get is a header file that contains
C/C++ definitions of your interfaces. You also get
a C source file that contains GUID information for
your component. Armed with this, a C++ developer
can use your PowerBuilder component in an application. I have included a C++ sample program in the
source for this article that illustrates the usage of our
PowerBuilder component.

I hope that this article has given you a good headstart


on building COM components with PowerBuilder.
It is a powerful technology, and armed with the
functionality that the latest versions of PowerBuilder
provide, you can do some wonderful things. I like to
think of COM as a way of extending the reach of
your PowerBuilder applications or code by opening
them up to the diverse range of COM-aware tools
and languages. PowerBuilder 7 will have even
better support for COM development, so now is a
great time to start learning. Good luck!

?
Alan Walsh is a systems analyst/programmer at
Indiana University. His internet address is
alwalsh@indiana.edu.

______________________________
1
David Chappell, Understanding ActiveX and OLE
(Redmond, WA: 1996), p.91.
2
Don Box, Essential COM (Reading, MA: 1998),
p.13.

Advertiser Index
Page

Name

Internet address

5
9
15
19
20
23

ServerLogic, Inc.
Sybase, Inc.
E. Crane Computing
Envisionsoft
Syap AG
Riverton Software Corp
RAK Software, Consulting & Publishing
Cyrano
Ecola Software
Object Force Software
PowerPeople
Integress
CATsoft Development GmbH

www.serverlogic.com
www.sybase.com
www.ecrane.com
www.envisionsoft.com
www.syap.ch
www.riverton.com
www.raksoft.ch
www.cyrano.com
www.ecola.ch
www.objectforce.com
www.powerpeople.de
www.integress.com
www.catsoft.ch

26
30
35
42
46
48

Page 18

Page 19

For more information fax back this reply, or call our sales team.

Phone: 041 760 5072

Fax: 041 760 1707

Name
Company
Function

Department

Phone

Fax

E-Mail
SYAP AG, Dorfstrasse 38, Baar CH-6340.
Phone: 041 760 5072 Fax: 041 760 1707 Web Site: www.syap.ch

Page 20

DEVELOPING COMPONENT BASED APPLICATIONS


by Sean Flynn

Introduction
In the previous issue of PowerTimes I discussed the
layered architecture of partitioned applications and
the various components within the architecture. In
this article we will examine the PowerBuilder objects and code necessary to support a partitioned and
distributed implementation. A sample application
that works in both client/server and distributed modes will be demonstrated.

Sample Application
The sample application used in this article is a
simple order/entry system based upon the PowerBuilder 6.x Demo database. This example has purposely been kept simple for demonstration purposes

and it should not be construed that the tools or


architecture involved cannot support more sophisticated implementations.
As in the last article, I will be using Riverton Software's HOW tool for modelling and generation purposes. As I write this I am using a beta copy of HOW
2.1 which should be released shortly. All of the code
and the HOW project are available to download
from
the
PowerTimes
web-site
(www.powertimes.com). In order to run the sample
application and load the HOW project you will also
need HOW 2.1. The HOW 2.1 Learning Edition
(LE) should be available on the Riverton web-site

Figure 1, Sample application object model.

Page 21

(Continued on page 22)

(Continued from page 21)

(www.riverton.com) shortly after the HOW 2.1 product release. It will also be bundled in the Sybase
EAStudio 3.0 (e.g. PowerBuilder 7.0) release.
Note that although there are numerous references to
the HOW tool within this article, no attempt is made
to provide an overview of the tool. Additional information about HOW can be obtained from the Riverton web-site.

The Object Model

model or task diagram are the query objects and the


business service object. The query objects define
how the data is retrieved from the database. Two
objects are generated for each query: a DataWindow
object that defines the result set definition and a
DataLink service object that defines how the data is
retrieved and updated. The DataLink service object
names are prefixed with n_cst_dlk_ and can be
found in the server.pbl library.
The business service object is used here as the order
business component within the middle tier server.
The business component manages the overall transaction including data retrieval, updating and validation. The business component is used as the interface
when publishing into middle tier servers such as
MTS or Jaguar. The business service object names
are prefixed with n_cst_bso_ and can be found in the
server.pbl library.

Figure 1 depicts the object model used for the sample application. This object model was created by
first reverse engineering the PowerBuilder Demo
database into PowerDesigner and creating physical
and conceptual data models. The data models were
then reverse engineered into HOW. A HOW domain
(e.g. object model) was then created representing the
objects that will participate in the sample application.

Code Elaboration

The Task Diagram

In order to make the sample application fully functional, code was added to several objects.

Figure 2 depicts the HOW task diagram that was


used to generate the sample application. The task
represents the window used for editing order information and the associated order line items.

On the sheet window (w_order) visual controls were


added to allow the user to specify a particular order
and two buttons allow the user to retrieve and update
the order. Several window functions were also added
that contain the functionality to retrieve and update
the order via the business component.
Several functions were added to the business component (n_cst_bso_order) to facilitate retrieval, updating and validating of the order.

Figure 2, Task Diagram

HOW Generated Objects


Based upon the object model and the task diagram, a
number of PowerBuilder objects are automatically
generated.
From the object model the Business Entity Objects
are generated representing the individual business
object classes. The Business Entity Object names are
prefixed with n_cst_beo_ and can be found in the
business.pbl library.
A number of objects are generated from the task
diagram including an application object, application
and task manager service objects, a frame window, a
sheet window corresponding to the window in the
task diagram and two external source DataWindow
objects used on the sheet. These objects can be
found in the order.pbl library.

Code was also added to the application manager


(n_cst_orderappmanager) object to create a connection object to facilitate the connection to a remote
application server.

Business Rule Implementation


Business rule validation can be implemented either
at an attribute level or at a transaction level. Attribute level validation occurs when an attempt is made
to change an attribute value on a particular business
object. From an interface perspective this usually
occurs as the user enters a value into a DataWindow
column (e.g. ItemChanged). Feedback is immediate
allowing the user to correct the value before continuing. In the sample application a simple business
rule to ensure that the quantity entered for a particular item is greater than zero. See the function SetQuantity
on
the
business
object
n_cst_beo_sales_order_items to see exactly how this
business rule has been implemented.

Other generated objects that are not part of object

Page 22

(Continued on page 24)

Modeling
is more than
Pictures

HOW is a component-based modeling tool and deployment framework for building business applications. HOW
makes it straightforward for mainstream developers to
build distributed and Internet applications using popular
development platforms like Visual Basic, Java and
PowerBuilder.

Riverton Software Corporation


One Kendall Square, Building 200,
Cambridge MA 02139
USA

RAK Software, Consulting & Publishing


Rolf Andr Klaedtke
Bchlistrasse 21
CH-8280 Kreuzlingen

Phone
Fax
URL

Phone
Fax
URL

617.588.0500
617.588.0412
htp://www.riverton.com

++41-71-670 01 60
++41-71-670 01 71
http://www.raksoft.ch

1998 Riverton Software. HOW, OpenFrame and Modeling is more than Pictures are trademarks of Riverton Software
Corporation.

Page 23

(Continued from page 22)

Transaction level validation typically occurs when


the transaction is saved. A transaction in this case is
defined as the order business component. From a
users standpoint this occurs when they attempt to
save the order. For the sample application we have
added a business rule to ensure that the quantity for
each item ordered does not exceed the quantity on
hand for that particular product. See the function
ValidateOrder on the business component
n_cst_bso_order to see exactly how this business
rule is implemented. Note that the PowerBuilder
Demo database has a quantity column on the product
table that represents the current quantity on hand
which is updated automatically via a database trigger.

Client/Server Example
Figure 3 shows the logical (dashed lines) and the
physical partitions (solid line) in the client/server
configuration. An existing order can be retrieved by
filling in the order number and pressing the Retrieve
command button. After retrieving the order try entering an invalid quantity (e.g. <= 0). Notice that
attribute level validation is immediate. Next try entering a large value for the quantity (e.g. 999). Notice
there is no immediate feedback. Try saving the order
and you should be presented with an error that the
quantity of the item exceeds that quantity that is in
stock.

Distributed PowerBuilder Example

driver) needs to be changed. The server application


must be run prior to running the client application.
Notice that server activity only occurs during data
retrieval and updating. While the user enters data all
activity occurs locally.

Stateful vs. Stateless


You may have noticed that the client creates a local
version of the BCMs when operating in distributed
mode. This is done so that the business component
on the middle tier server can be destroyed thus
freeing server resources while local client processing
takes place. By keeping the BCMs locally, the client
maintains the state of the transaction locally and no
state information is retained on the server. The business component is considered stateless in that no
information about the transaction is maintained on
the server during local client processing. By using
stateless components the server scalability is maximised allowing it to serve more clients. Although
this is less important with Distributed PowerBuilder
it becomes crucial when using transaction servers
such as Sybase Jaguar and Microsoft Transaction
Server.
A stateful component is one that stays created during
the life of the transaction. Essentially the client
interfaces directly to the BCMs within the business
component on the server thus eliminating the need
for the local copies. Although this mode of operation
was supported by earlier versions of OpenFrame it is
not supported in current version (2.1).

Figure 4 depicts the sample application in distributed


mode of operation. To use the sample application in
distributed mode only a simple INI file setting (e.g.

(Continued on page 25)

Database
Server

Client

Presentation
Order

Business

Data Access

Business
Component
DLK

BCM
UILink

UILink

Database

BEO
BCM
DLK

BEO

Figure 3, Client/Server example.

Page 24

(Continued from page 24)

Distributed
PowerBuilder

Client

Presentation

Business

Database
Server

Data Access

Business
Component

Order

BCM
UILink

BEO
UILink

Database

BCM

BCM
BEO

DLK

BEO
BCM
BEO

DLK

Figure 4, Distributed PowerBuilder Example.

Summary
We have demonstrated here how a partitioned application can easily switch from client/server to a distributed mode of operation. Rivertons HOW tool provides for object modelling and code generation to
PowerBuilder. HOW also provides a framework
infrastructure with OpenFrame to support partitioned
and distributed applications. Distributed PowerBuilder provides a simple yet effective means for distributing application functionality.

Next Time
In the next article we will look at implementing the
same sample application with the business component built as a COM object and installed in
Microsoft Transaction Server (MTS). We will also
look at a sample Active Server Page application
accessing the same business component in MTS.

Sean Flynn is a Managing Partner at Infinity


Systems Group, a Boston area consulting and
training company specialising in distributed systems
design and development. Sean was the principal
developer of the Riverton OpenFrame distributed
business object framework. Sean is a CPD
Professional, member of the CPD Review
Committee and sits on the PowerBuilder Customer
Advisory Board. Sean is available to present an
onsite seminar on distributed application design and
development including the topics discussed in this
article.
Sean
can
be
reached
at
sflynn@goinfinity.com or by voice at 978-2875332.

Page 25

CYRANO ClientPack
Comprehensive Automated Software Quality
solution for PowerBuilder developments
Key Features of CYRANO ClientPack
Delivers a complete suite of integrated tools for managing and

controlling quality in PowerBuilder development


Provides the only tool for defining and maintaining standards in
PowerBuilder applications
Optimizes development, simplifies maintenance and checks compliance to standards in PowerBuilder applications
Offers the industrys leading MS Windows multiplatform test planning, and defect tracking tool set
Integrates all of the key components needed for building and running
automated regression test suites.
Analyzes in detail the API and SQL calls of the transactions in PC based client/server
applications
Identifies where changes are needed in a PowerBuilder regression test suite as your application
is updated, developed and maintained.
Measures how much of a PowerBuilder applications functionality and objects are being tested.

Key Benefits of CYRANO ClientPack


Allows development teams to start working on building quality much
sooner than would be possible with more traditional development methods
Provides an almost instant return on investment without the need for
extensive product or methodology training
Improves communication between team members saving expensive travel

and meeting costs discussing routine project progress


Simplifies the maintenance of PowerBuilder applications and regressions
test suites
Accelerates and secures migration to latest versions of PowerBuilder
Shortens the PowerBuilder development learning curve

CYRANO Worldwide Headquarters


38, rue du Gnral Foy
75008 PARIS
Tl : +33 (0) 1 44 90 85 70
Fax : +33 (0) 1 44 90 85 79

http://www.cyrano.com
Page 26

THREAD SYNCHRONIZATION
by Roy Kiesler

Previously on Multithreading 101


At the end of my previous article, I pointed out that
the terms multithreaded and thread-safe
are not synonymous, and that writing thread-safe
applications requires different design process and
coding techniques than those used for developing
single threaded applications.
In this article, I will discuss some of the design
considerations and coding techniques necessary to
implement thread-safe PowerBuilder applications, as
well as the Win32 tools available to us to tame the
multithreaded beast.

Murphys Law
If anything can go wrong, it will (and at the worst
possible time). These should be words to live by for
every programmer who embarks on a journey to
develop a multithreaded application.
So what can possibly go wrong? Consider the results
of having two or more threads simultaneously access
the same global or shared data. One thread could be
updating the contents of a global structure while
another thread is reading the contents of the same
structure. It is unknown what data the reading thread
will receive: the old data, the newly written data, or
possibly a mixture of both.
This is a classic example that can be found in many
textbooks that discuss multithreading issues in C/
C++. Fortunately, it is not applicable in PowerBuilder due to the unique implementation of the shared
object. As I mentioned in my previous article, PowerBuilder creates a copy of all global variables in
the shared object session, thus preventing threads
from directly accessing one anothers data. Once
again, kudos to the PowerBuilder Kernel Group
members.
Dont get too excited, though. Were not out of the
woods yet

When Threads Byte


<Pun intended> One situation that comes to mind is
when two or more threads need to read and/or write

to/from the same key in the Windows Registry. As in


the previous C++ example, there is no way of telling
what value the reading thread will receive: the old
value, the new one or something in between. Thread
communications is also a vulnerable area. Recall
that an applications primary thread passes a nonvisual object (NVO) by reference to the shared
object. This object implements functions that enable
a two-way communication between the primary and
worker thread. Now, consider the following scenario:
An application spawns two worker threads to retrieve data from two different databases. A progress
window in the primary thread displays the total
number of rows retrieved by both threads. A single
communication object is passed by reference to both
worker threads. The communication object implements a method that increments an instance variable
on the main window, as shown in the following code
snippet:
// of_IncrementCount
w_frame.il_rows += 1

To understand how vulnerable this single line of


code is, we need to examine the underlying assembly
1
code that is executed at run time:
; load the contents of il_rows from
; memory to a register
MOV
EAX,DWORD PTR [EBP-4]
; increment the contents of the register
ADD
EAX,1
; write the contents of the register back
; to memory
MOV
DWORD PTR [EBP-4],EAX

The problem has to do with the way the CPU schedules threads for execution. The worst case scenario
looks like this:
Thread A loads the value of il_rows from
memory.
Thread B loads the value of il_rows from memory.
Thread A increments the value in the register.
Thread A write the value back to memory.
Thread B increments the value in the register.

Page 27

(Continued on page 28)

(Continued from page 27)

Thread B writes the value back to memory.

As you can see, the result is that the value of


il_rows was only incremented by 1, instead of 2.
Depending on what data is being retrieved, this may
or may not be a critical error, but it is nonetheless a
data integrity issue, which should never be dismissed.

What does this matrix tell us? A capital R in position


(x, y) means that variable x is read from when thread
y runs. A capital W means that x is written to when
thread y runs. We use this information to exclude
variables as candidates for access conflicts. The
following rules are used:
1.
2.

Multithreading Design Considerations


Perhaps the hardest part of implementing a multithreaded application is designing it such that concurrent, asynchronous access of multiple threads to
shared data does not affect the desired functionality
of the application. This task has two components:
1.

2.

Safety analysis - determining whether data can


be corrupted by concurrent access from more
than one thread, and if so, designing mechanisms to prevent this from happening.
Deadlock analysis - determining whether a given thread can end up in an infinite suspended
state.

Safety analysis begins with the assumption that every


variable in the system is a potential candidate for
access conflicts. The next step is to eliminate these
cases for which we can prove that no conflict can
occur. Finally, we look at the remaining variables,
one at a time, to determine if they can cause problems, and if necessary, apply synchronization mechanisms.
In PowerBuilder terms, we have to look at all the
functions and events that are called from the shared
object, and determine the vulnerability of any data
referenced in them. The bad thing about this approach is that there is no algorithm that can do all of
this work for us. The good news is that in practice,
the majority of these cases can be pre-excluded by
intuition, i.e., data elements that you know right
away you don't care about possibly being misread or
assigned.
IMHO, the simplest way of capturing this information in using a simple matrix, as show in table 1:
Variable

Thread A

Thread B

w_main::il_rows

RW

RW

w_main::ib_stop

n_cst_comm::ii_rc

Any variable that is only read from and never


written to is safe.
Any variable that is accessed from one thread
only is safe (presumably, this thread knows how
to make good use of its own data).

Using these rules on the matrix, we can quickly


reduce the candidates for access conflicts by striking
out all rows in which all columns are only R (rule 1),
or in which only one column has entries in it (rule 2).
The remaining entries in the matrix are the ones that
require synchronized access.
I will delay the discussion of deadlock analysis to a
later part of this article, as it involves concepts that
will only be defined in the following sections.

Wake Up and Stretch


I realize this sounds like CS531, Advanced Topics
in Taking the Fun Out of Programming, but now
that we know what it is that can go wrong, we can
start discussing coding techniques that will make
sure nothing does go wrong.

Win32 Synchronization Objects


The Microsoft Win32 API provides a variety of
ways that threads can use to synchronize access to a
resource.
There are 5 major types of synchronization objects
in Win32:
1. Critical Sections
2. Mutexes
3. Semaphores
4. Waitable Objects
5. Events
In the following sections, I will only discuss the first
object type, the critical section, as it is the most
commonly used, and best suited for the type of
multithreading that PowerBuilder supports. If you
want to learn more about semaphores, waitable objects, events and other type of synchronization objects, consult the sources listed in the reference
section at the end of this article.

Table 1, Access rights matrix

(Continued on page 29)

Page 28

(Continued from page 28)

Critical Sections
Critical sections are used to synchronize multiple
threads in a single process. A critical section object
can be owned by only one thread at a time. A critical
section is often described as a narrow path in the
road where only a single vehicle can path at any
given time.

n_cst_criticalsection::constructor - calls the InitializeCriticalSection API function, since every


critical section object must first be initialized
before any of the threads in a process can use it.
n_cst_criticalsection::of_claim - calls the EnterCriticalSection API function to obtain ownership of the critical section object.
n_cst_criticalsection::of_release - calls the LeaveCriticalSection API function to release ownership of the critical section object.
n_cst_criticalsection::destructor calls the DeleteCriticalSection API function to free the memory occupied by this object.

// instance variable declaration


n_cst_criticalsection inv_critsec
// of_IncrementCount revised
inv_critsec.of_claim()
w_frame.il_rows += 1
inv_critsec.of_release()

Figure 1, Critical Section


A process is responsible for allocating the memory
used by a critical section. This is normally done by
declaring a variable of the type CRITICAL_SECTION.

When to Use What?


Here are a few simple rules of thumb for the use of
Win32 synchronization objects:

CRITICAL_SECTION cs;

A critical section is a structure, defined as follows:

struct CRITICAL_SECTION {
RTL_CRITICAL_SECTION_DEBUG *DebugInfo;
LONG
LockCount;
LONG
RecursionCount;
HANDLE OwningThread;
HANDLE LockSemaphore;
DWORD SpinCount;
}

Can more than one application use the resource?


If yes, use a mutex. If not use a critical section.
Use your synchronization objects as close as
possible to the vulnerable code segment.
Never use synchronization objects conditionally.

Pros, Cons and Gotchas

Performance - Critical sections are significantly


faster than mutexes and semaphores, or any
other kernel object, for that matter. The reason
for this is that the locking and unlocking operations of a critical section are implemented entirely in user mode and do not require the thread
to switch from user mode to kernel mode and
back again. Consequently, entering a critical
section typically requires only 10 or so CPU
instructions to execute. When using kernel objects, the transition from user mode to kernel
mode and back typically requires 600 CPU
instructions on an x86 processor. Thats a huge
difference!

Abnormal Termination - If a thread enters a


critical section and then terminates abnormally,
the critical section object will not be released.
On the other hand, if a thread terminates without
releasing its ownership of a mutex object, the
mutex is considered to be abandoned, and a
waiting thread can still acquire ownership of it.

//
// nested structures
//
struct RTL_CRITICAL_SECTION_DEBUG {
WORD
Type;
WORD
CreatorBackTraceIndex;
RTL_CRITICAL_SECTION *CriticalSection;
LIST_ENTRY ProcessLocksList;
DWORD EntryCount;
DWORD ContentionCount;
DWORD Spare[ 2 ];
}
struct LIST_ENTRY {
LIST_ENTRY *Flink;
LIST_ENTRY *Blink;
}

As you can see from the C++ type declarations


above, the data structure which represents a critical
section involves 2 levels of nested structures as well
as self-referencing pointers. For this reason, we will
not attempt to translate this convoluted data structure
to its PowerScript equivalent; instead, we can use the
Watcom C++ Class Builder that ships with the
Enterprise version of PowerBuilder, to create a
wrapper NVO around this object. Here is what this
NVO looks like:

Page 29

(Continued on page 31)

Reach the top at ECOLA (Switzerland) as a

PowerBuilder Software Developer


Job description: You work in our young and strong development team. We develop our
up-to-date standard software solution for the administration of schools and courses (ECO
open). First, you will be trained in our software development environment PowerBuilder. Then,
your job will be the design and development of new functions for our product. The new
functions will be general and customer-specific.
Prerequisites: More than 2 years development with PowerBuilder. Good knowledge of
PC-systems. You are able to learn easily and hard working. You are 20 to 35 years old.
Our offer: ECOLA is a software company with 20 employees. ECO open is market leader in
Switzerland. It goes without saying that we offer both on the job training and project-specific
courses as well as a good working climate. We are located in Rotkreuz (Zug) in Switzerland.
Start: as soon as possible. Mr. M. Schlehan is looking forward to your application!
Our motto: The relationship between employer and employee can be summarised by fairness,
loyalty on both sides and by an equal give/take-ratio on both parts.

ECOLA Software AG
Lettenstrasse 7
CH-6343 Rotkreuz
Tel: +41-41/790 20 50
Fax: +41-41/790 20 48
eMail: info@ecola.ch
http://www.ecola.ch

Erreichen Sie Ihre beruflichen Ziele bei ECOLA (Schweiz) als

AnalytikerIn/ProgrammiererIn PowerBuilder
Aufgaben: Sie arbeiten in unserem jungen, dynamischen Projektteam an der Entwicklung
unserer modernen Standard-Software-Lsung fr Schul- und Kursadministration (ECO open).
Nach einer intensiven Einfhrung in unsere Entwicklungsumgebung PowerBuilder gehren
die stndige Weiterentwicklung der Lsung sowie die Realisierung von kundenspezifischen
Anforderungen zu Ihren Aufgaben.
Voraussetzungen: Neben mehrjhriger Software-Entwicklungs-Erfahrung mit PowerBuilder
kennen Sie sich im Umfeld der PC-Systeme aus. Sie sind lernfhig und belastbar und Ihr Alter
betrgt 20 bis 35 Jahre.
Angebot: Wir sind ein Software-Haus mit 20 Mitarbeitern. Unsere Lsung ECO open gehrt
zu den Marktfhrern in der Schweiz. Training on the Job und projektbezogene Weiterbildung
sind bei uns ebenso selbstverstndlich wie ein gutes Arbeitsklima. Arbeitsort: Rotkreuz
(Kanton Zug) in der Schweiz. Eintritt: baldmglichst. Herr M. Schlehan freut sich auf Ihre
Bewerbung!
Leitsatz: Die Zusammenarbeit zwischen Arbeitnehmer und Arbeitgeber zeichnet sich durch
Fairness, gegenseitige Loyalitt und durch ein ausgewogenes Geben/Nehmen-Verhltnis
aus.

Page 30

(Continued from page 29)

References
Deadlocks
A deadlock is a condition in which two or more
threads wait for each other to release a shared resource before resuming their execution. Since all of
the participating threads in a deadlock are suspended
and hence, cannot release the resources they own, no
thread can continue, and the entire application appears to be hung. One of the most common scenarios
that can lead to a deadlock is shown in figure 2
below:

Asche, Ruediger. Detecting deadlocks in Multithreaded Win32 Applications, January 1995,


Microsoft Developer Network CD, Technical Articles.
Asche, Ruediger. Synchronization on the Fly, September 1993, Microsoft Developer Network CD,
Technical Articles.
Cohen Aaron and Mike Woodring.
Win32 Mul-tithreaded Programming,
1998, OReilly and Associates.
Richter, Jeffrey. Advanced Windows, Third
Edition, 1997, Microsoft Press.
Richter, Jeffrey. "Win32 Threads Using Critical Sections, Semaphores, and Mutexes." Microsoft Systems
Journal (August 1993): 27-44. (Microsoft Developer Network CD, Books and Periodicals.)
Vert, John. Writing Scalable Applications for Windows NT, June 1995, Microsoft Developer Network CD, Technical Articles.

Figure 2, Deadlock
Thread A acquires critical section 1 and attempts to
acquire critical section 2, which is already acquired
by thread B. At the same time, thread B is attempting
to acquire critical section 1.

Confused? Good. This is a warning sign that your


design needs some more work. Heres a word of
advice: avoid nested mutual exclusions at all cost.
For more information on deadlock analysis, refer to
Ruediger Asches excellent article, Detecting deadlocks in Multithreaded Win32 Applications.

Roy Kiesler is a Software Engineer at Logica Inc.,


a leading provider of business-engineered, contentrich solutions for the Financial Services, Energy
and Utilities, Telecommunications and Automotive
markets. Roy has been working with PowerBuilder
for the past 5 years, and is also an avid C++/MFC
programmer. Contact Roy via e-mail at
kieslerr@logica.com.

Conclusion
In my previous article, I introduced you to threads
and the benefits of multithreaded programming. In
this article, I exposed the dark side of threads and
some of the possible pitfalls of using them, as well as
the safeguards that need to be implemented against
those pitfalls. In my next article, the final part of this
trilogy, I will walk you through the implementation
of a thread manager object, a broker, if you will, that
will make it easier than ever to use multiple threads
in your PowerBuilder client applications. Until then,
feel free to download and explore the source code
and samples used in this article from my web
site at http://www.geocities.com/
~rkies/win32api/threads.htm.

______________________________
1

Assembly code generated by Microsoft Visual C++ 6.0 for a


similar C++ function.

Page 31

INTERNET-ENABLE YOUR APPLICATION


WITH THE POWERSOCKET LIBRARY
by Tim Roberts

Introduction
Have you ever needed the ability to transfer a file
using FTP? Do you daydream about creating a commercial application that can schedule and download
web pages? Is a client bugging you to get the latest
stock figures, download weather radar images, or
simply send an email across the Internet? Instead of
telling the client wow - wouldnt that be nice, you
can actually fulfill these goals with minor programming using the PowerSocket library.
The PowerSocket library was written by Ted
Coombs and Jason Coombs at Science.Org to allow
PowerBuilder developers to write applications that
can access the Internet or intranet using the Windows Socket library (winsock.dll). The PowerSocket Library can be found at:
http://computers.science.org/PowerBuilder/, where
sample applications are available for download.
By following the process shown below, you will be
able to create a PowerBuilder application utilizing
the PowerSocket library that can send an email or
retrieve a web page.

Brief History
But first, a little background. The Windows socket
library (winsock.dll) was created to allow Windows
applications to take advantage of the socket model
used by both Unix and the Internet. How does it do
this? Its much like a phone conversation - you must
look up whom you want to talk to, dial the number,
talk, listen, and hangup. The keypoint is that a phone
provides the means for two people to talk but does
not the dictate the content. The conversation that
takes place on the phone is determined by the
people who use it. A socket is similar in that you
look up a host server, connect to it, send information
or commands, receive responses and close the
connection. The socket provides the means for two
computers to talk but not the conversation (or protocol). This allows us to use current and future protocols (HTTP, SMTP, FTP, NNTP, etc) all using the
same socket model.

These extensions allow the programmer to take advantage of the event driven nature of Windows by
notifying the application whenever a socket event
happens. This will allow the user of the application
to multi-task while your application communicates
with the sockets.

Stream vs. Datagram Socket


Using a socket to both receive and send data may
sound similar to talking and listening on a phone.
Actually, every packet of information sent on the
Internet can be routed through a different path, duplicated, or arrive out of sequence - if at all. Imagine
the following phone conversation: You say I would
like to order a pizza and the receiver hears like
pizza would I to to. Two types of sockets were
created to correct this less than ideal situation stream and datagram sockets.
A stream socket provides a reliable, sequenced and
unduplicated flow of data to the application. This
type of socket gives the application programmer the
assurance that what was sent is going to be what
arrives. As you can guess, a large majority of applications use this type of socket and it will be the
socket type that this article deals with.
A datagram socket provides almost nothing to help
the situation above. It is up to the application to
figure out the sequencing, eliminate the duplicates
and request missing information. The advantage of
the datagram socket is in its performance. The datagram socket does not have any of the overhead
associated with the stream socket and will easily out
perform a stream socket on a reliable network.

Blocking vs. Non-Blocking


As mentioned before, the Winsock standard has
Windows-specific extensions that take advantage of
the event-driven nature of Windows. This has created two sets of functions: blocking and non-blocking.
Blocking functions are those that will not return until
the call has completed. These functions will not
allow the application to continue processing until the
call has completed. Non-blocking (or asynchronous)

The Windows socket library includes all the socket


functions plus some Windows-specific extensions.

Page 32

(Continued on page 33)

(Continued from page 32)

functions will return immediately and notify the


application when completed. A non-blocking socket
is the default behavior for the PowerSocket library.
The implication is that if a blocking call is used on a
non-blocking socket, a check must be made if a
blocking situation would occur. If it would, then
your application should wait (or allow the user to
work on other tasks by issuing a yield statement)
until it receives notification that the action has been
completed.

PowerSocket Library
The PowerSocket library is made of three base objects and two inherited objects. The Winsock base
object contains both the information to register the
application and functions that do not require a socket
- mainly lookup and conversion functions. The socket base object has communication functions, including connection, reading, writing and closing. The
third base object is the socketraw object which is
used internally to communicate to the winsock.dll.
Two objects inherited from the socket base object
are socket_stream and socket_datagram. As
their names imply, these are objects that have all the
setup required for their respective socket.

Creating the Base Socket Object


Aspiring to be an OO guy, I decided to create a base
object to encapsulate the code necessary for basic
socket operation. The individual protocols can then
be encapsulated in objects inherited from the base
object. The base object needs functions to connect,
read, send, close and wait. Additionally, events are
needed to initialize the socket, receive socket notifications and to close the socket. Instance variables
are needed to capture the different states a socket
may be in (connected, ready to send, data ready to
read, etc.) and to handle the sockets input-output
buffers. At this point, you may want to download the
PowerSocket library to reference the constants and
functions defined.
Now lets create the base socket. One requirement is
that a valid Windows handle is needed to receive
socket event notification. A custom visual object
gives us that handle - call it u_socket. Then define
the instance variables that are needed (see listing 1
below).
// Winsock object,Powersocket Lib
winsock ws
// Stream Socket object, Powersocket Lib
socketstream sstream
// Error code
int ii_socketerrcode
// I-O Buffer size
int ii_buffersize = 1024
// I-O Buffer

blob iblb_buffer
// Server Response BLOB
blob iblb_response
// Socket States
boolean ib_socketerror
=
boolean ib_socketconnected
=
boolean ib_socketreceived
=
boolean ib_socketreadytowrite =
boolean ib_socketclosed
=

FALSE
FALSE
FALSE
FALSE
FALSE

Initializing
To initialize the socket, go to the constructor event
of u_socket and create the PowerSockets winsock
and streamsocket objects (see listing 2 below). These
objects will register the application with winsock
and create a streaming socket. Additionally, we need
to tell winsock to notify our object whenever a
socket event occurs. For what we are accomplishing,
we want to be notified upon successful connect
(FD_CONNECT), data waiting to be read
(FD_READ), readiness to write (FD_WRITE) and
closing of the connection (FD_CLOSE). This is
done by calling the winsock function WSAASYNCSELECT passing the u_socket handle, the event to
notify (pbm_custom01), and the result of adding the
notification constants together.
int li_trapevents
int li_notifyevent=1024
// 1024 = pb_custom01
ws
= create winsock
sstream = create socketstream
li_trapevents
= ws.FD_CONNECT +
ws.FD_READ + ws.FD_WRITE + ws.FD_CLOSE
sstream.WSAASYNCSELECT(handle(this),
li_notifyevent, li_trapevents)

Socket Notifications
To accept the socket notifications from the winsock,
define the event ue_socketevent that is mapped to
pbm_custom01. The purpose of the event is to determine what socket event occurred (see listing 3 below). First, get the socket event number by calling
the winsock function WSAGETSELECTEVENT. Also determine if an error occurred by calling the winsock
function WSAGETSELECTERROR. If an error occurred,
set the socket state to error and save the error code.
Otherwise, determine which socket event happened.
Upon successful connect (FD_CONNECT), set the
socket state to connected. If the socket has data to be
read (FD_READ), call the of_read function to read
the data. If the socket is ready to write
(FD_WRITE), set the socket state to ready to write.
If the server closes the socket (FD_CLOSE), call the
of_close function to close our end. Any other socket event is considered an error and should be handled accordingly.
(Continued on page 34)

Page 33

ELSE
lul_hostaddress = &
lstr_pbhostent.h_addr_list[1]
END IF

(Continued from page 33)


int li_socketevent, li_selecterr

li_connectrc = &
sstream.WSCONNECT(lul_hostaddress, &
ai_port)
IF li_connectrc = 0 THEN
ib_socketconnected = TRUE
ELSE
li_lasterr
= ws.WSAGETLASTERROR()
IF li_lasterr = ws.WSAEWOULDBLOCK THEN
// Wait for 30 seconds to connect
of_wait(ib_socketconnected,30)
ELSE
ib_socketerror
= TRUE
ii_socketerrcode = li_lasterr
END IF
END IF

li_socketevent =
ws.WSAGETSELECTEVENT(message.longparm)
li_selecterr
=
ws.WSAGETSELECTERROR(message.longparm)
IF li_selecterr = 0 THEN
CHOOSE CASE li_socketevent
CASE ws.FD_CONNECT
ib_socketconnected = TRUE
CASE ws.FD_READ
of_read()
CASE ws.FD_WRITE
ib_socketreadytowrite = TRUE

Waiting

CASE ws.FD_CLOSE
of_close()
CASE ELSE
ib_socketerror
= TRUE
ii_socketerrcode = li_socketevent
END CHOOSE
ELSE
ib_socketerror
ii_socketerrcode
END IF

= TRUE
= li_selecterr

Connecting
To handle the connections to a server, create an
object function called of_connect with two parameters: the name of the server and its port. This
function will do two things: look up the IP address of
the server and try to connect to it on the specified
port (a port roughly relates to a specific protocol - 80
for HTTP, 25 for SMTP) - see listing 4 below. To
lookup the server name, call the winsocks GETHOSTBYNAME. That function will return a structure, PBHOSTENT (included in the PowerSocket library), that
will contain either the IP address of the server or null
if an error occurred. If no error occurred, connect to
the server by calling the sockets WSCONNECT function
passing it the IP address of the server and the port it
listens to. If the connection failed, interrogate the
error by calling the winsocks WSGETLASTERROR to
see if the connect statement would have created a
blocking situation. Because the socket is a nonblocking socket by default, wait until the socket
notifies us of a successful connection by using the
of_wait function outlined below.

The wait function allows the application to wait for a


specific socket state and give the application the
ability to timeout if the server is slow. Create an
object function called of_wait which accepts two
parameters: the desired socket state (by reference)
and how long to wait for it. The desired socket state
argument must be by reference. If it were by value,
that value is set when called and would never change
- making the function useless. First, save the current
time and create a DO loop until either the specified
state is reached, the server closes the connection or
an error occurs (see listing 5 below). A yield()
statement is then placed within the loop to give
control back to the user during the wait. A check is
made to see if the elapsed time (the time between
now and the saved time) causes us to timeout.
time
int

ltm_starttime
li_elapsedseconds

ltm_starttime = Now()
DO UNTIL ab_waitstate or &
ib_socketerror or &
ib_socketclosed
Yield()
// Does not work past midnight!
li_elapsedseconds = &
SecondsAfter(ltm_starttime,Now())
IF li_elapsedseconds > &
ai_timeoutseconds THEN
ib_socketerror = TRUE
ii_socketerrcode = 99999
END IF
LOOP

Sending

Function of_wait()
int
li_connectrc, li_lasterr
ulong
lul_hostaddress
pbhostent
lstr_pbhostent
lstr_pbhostent
= &
ws.GETHOSTBYNAME(as_hostname)
IF IsNull(lstr_pbhostent) THEN
ib_socketerror
= TRUE
ii_socketerrcode = & ws.WSAGETLASTERROR()
RETURN

Sending information to the server can be accomplished by creating a function called of_send with a
single parameter: a blob (all information going to or
coming from the socket will be a blob). This function will break up the blob into separate data packets
depending on the buffer size (see listing 6 below).

Page 34

(Continued on page 36)

PowerBuilder Development Tools

Save Your Company Thousands of Dollars!


Application maintenance and migration has never been easier!

Object Explorer displays applications, libraries, and objects by file


directory, hierarchy, or application library. Search an entire application's
library list, hierarchy tree, or file directory with one click.
Inspect and edit/write code inside editor for any object in your application.
Executive version features multi-global search and replace. Perform
consistent migrations among all corporate applications.

Where Will You Be When Your Hard Drive Crashes?


You lost how many files! Whos going to explain that one to the boss?

File SyncForce performs system wide


data synchronization and management.
Launch another program, while File
SyncForce works silently in the
background.
Protect your vital corporate data, while
giving the ease of local development and
minimized network traffic.

Automatic scheduled
backups.
Prevents data loss conflicts.
Increases productivity.
Replicates entire directory
branches or individual files.
Detects file changes at preset
intervals.

Tired of "I/O Error Saving Object" Error Messages?


Power SyncForce Has the Solution!

Power SyncForce tracks which objects in a library have changed, logs the changes, and then copies the
new objects to a destination (replication) library. The replicated directory prevents collision I/O errors
when running an application while checking objects in and out during development.
Network traffic is minimized.
Automatic object replication.
Always run with the latest objects.

Compiling Applications Just Got Easier.


T he IntelliGen alternative to PowerGen.

IntelliGen builds all your corporate applications with the click of a button.
Regenerate your applications automatically.

Coming Soon: InstallForce!

Free Trial Offer!

Try a full-featured version of any ObjectForce


software before you buy it! For more information,
visit us at www.objectforce.com.

Page 35

(Continued from page 34)

filled by the protocol.

First, store the total bytes to be sent. Then create a


DO loop until all the bytes have been sent or an error
occurs. The buffer is then filled by using the blobmid
function to extract a packet of data. During the first
iteration, li_nextsendbyte is set to 1 to send the first
block of data. Send it by passing the buffer and its
length to the sockets SEND function. If a socket
error occurred, check if the send would have blocked. If so, wait until the socket notifies us that it is
ready to write. Otherwise, add the number of bytes
sent to li_nextsendbyte to point to the next block of
bytes to be sent. The bytes sent may differ from the
buffer size depending on the servers buffer and
other factors. This process then repeats until all bytes
have been sent.

To read the data from the socket, create an object


function called of_read with no parameters. The
of_read function is called whenever the winsock
notifies us data is ready to be read. A typical event
sequence is:
1). The winsock receives 2000 bytes of data and notifies the application (via FD_READ).
2). The application reads 1024 bytes (the size of our
buffer)
3). The winsock notifies the application that data is
still ready to be read (another FD_READ).
4). The application reads the last 976 bytes and adds
it to the 1024 bytes that was received earlier.
5). Etc.

int li_totalbytes, li_lasterr, &


li_nextsendbyte = 1, li_bytessent

From the applications perspective, each call to


of_read should read a buffer of data and append it to
the overall response blob (see listing 7 below). The
buffer is pre-allocated with spaces and then filled
with data by using the sockets RECV function, passing the buffer and its length. The RECV function will
return either an error, a 0 (meaning the server closed
the connection), or the number of bytes received. If
an error occurred, get the error by calling the winsocks WSAGETLASTERROR function. Had the RECV
function returned 0, the server closed the connection
and a call is made to of_close. If we receive any
other value, then we received some data. Add the
number of bytes received into the response blob
using the Blobmid function and call the
ue_receivecomplete event to allow the protocol to
determine if we are at EOF.

li_totalbytes = Len(ablb_send)
DO UNTIL li_nextsendbyte > li_totalbytes
OR ib_socketerror
iblb_buffer
= &
blobmid(ablb_send, &
li_nextsendbyte, &
ii_buffersize)
li_bytessent = &
sstream.SEND(iblb_buffer, &
Len(iblb_buffer),0)
IF li_bytessent = ws.socket_error THEN
li_lasterr = ws.WSAGETLASTERROR()
IF li_lasterr = ws.WSAEWOULDBLOCK
THEN
ib_socketreadytowrite = FALSE
of_wait(ib_socketreadytowrite, &
10)
ELSE
ib_socketerror = TRUE
ii_socketerrcode = li_lasterr
END IF
ELSE
li_nextsendbyte += li_bytessent
END IF
LOOP

int li_lasterr, li_selecterr, &


li_bytesreceived
iblb_buffer = Blob(Space(ii_buffersize))
li_bytesreceived = &
sstream.RECV(iblb_buffer, &
Len(iblb_buffer),0)

Reading
Now for the reverse - reading from the socket.
Unfortunately, the trick to reading is in determining
when to stop reading. Nothing is going to tell us this
is all the server sent or give us an end-of-file (EOF)
indicator. Each protocol implements EOF differently
- HTTP servers will close the socket after sending
information, SMTP will have a ~r~n at the end,
etc. An event needs to be created that the will allow
the protocol to tell the socket when to quit by
returning a boolean indicating whether the read operation has completed. The socket can call this event
to determine when to stop reading. Create a user
event called ue_receivecomplete that is not mapped to any event. Press the args button to change
the return type to a boolean. Then leave this
placeholder event empty - it is expected to be

CHOOSE CASE li_bytesreceived


CASE ws.socket_error
ib_socketerror
= TRUE
ii_socketerrcode = &
ws.WSAGETLASTERROR()
CASE 0
of_close()
CASE ELSE
iblb_response += &
BlobMid(iblb_buffer,1, &
li_bytesreceived)
ib_socketreceived = &
Trigger Event ue_receivecomplete()
END CHOOSE

Hopefully you noticed that the server, not the application, initiated the read operation. A function needs
to be defined that allows the application to get (or

Page 36

(Continued on page 37)

(Continued from page 36)

wait for) the response from the server. Create a


function called of_getresponse with no parameters
that will return a blob (see listing 8 below). This
function will first check to see if a response has
already been received. If there is no response yet,
wait until one is received calling of_wait. Then,
store the response in a local blob to be returned.
Finally, reset the receiving state and blob in preparation for the next response.
blob lblb_return, lblb_null
IF NOT ib_socketreceived THEN
of_wait(ib_socketreceived,10)
END IF

After working with a few protocols, many require a


response after sending a command. To provide this
functionality in the base object, define one last function called of_sendcommand (see listing 11 below)
with a single string parameter that returns a blob.
This function will simply send the string parameter
via of_send and wait for a response using
of_getresponse. If an error occurs, the error message is returned instead.
Functoin of_sendcommand()
blob lblb_response
of_send(blob(as_command))
IF ib_socketerror THEN
Return Blob("Error sending" + &
as_command+": " +
String(ii_socketerrcode))
END IF

lblb_return
= iblb_response
iblb_response
= lblb_null
ib_socketreceived = FALSE
return lblb_return

Closing
We can now connect, write and read from the socket.
All that is left to do is close it. Create a function
called of_close with no parameters (see listing 9
below). We want to use the socket function CLOSESOCKET to perform the close. However, if there is
outstanding data that the winsock has yet to send, the
close function would create a blocking situation. A
loop is needed to be made to keep issuing the close
until it is successful (or a fatal error occurs).
int li_closerc, li_lasterr
DO WHILE NOT ib_socketclosed and &
NOT ib_socketerror
li_closerc = sstream.CLOSESOCKET()
IF li_closerc = ws.SOCKET_ERROR THEN
li_lasterr = ws.WSAGETLASTERROR()
IF li_lasterr = ws.WSAEWOULDBLOCK
THEN
CONTINUE
ELSE
ib_socketerror = TRUE
ii_socketerrcode = li_lasterr
END IF
ELSE
ib_socketclosed = TRUE
END IF
LOOP

lblb_response = of_getresponse()
IF ib_socketerror THEN
Return Blob("Error receiving response
from '"+as_command+"': " + &
String(ii_socketerrcode))
END IF
Return lblb_response

The Real FunSMTP


Now for the real fun: we can now create protocols
that utilize the socket. All protocol specifications are
found in documents called Request For Comments
(RFCs). An index of RFCs can be found at
www.internic.net/ds/rfc-index.html.
Lets create an object that employs the Simple Mail
Transport Protocol (SMTP) to send an email message. Create a custom visual user object called
u_smtp inherited from u_socket. Then, create a function called of_sendmail with three parameters: the
recipient, the subject and the note text.

Destruction
Finally, the destruction of the socket will be handled
by the destructor event (see listing 10 below). This
event calls of_close to close the socket and destroys
the winsock and streamsocket instances. The PowerSocket library will automatically unregister and
clean up the socket connection.
of_close()
destroy winsock
destroy sstream

Bonus Function!

The protocol for SMTP is found in RFC811 and the


header content for the message (subject, CC, etc.) is
specified by RFC812. The process for sending a
mail message according to RFC811 is:
1. connect to the mail server on port 25,
2. say hello,
3. identify ourselves,
4. identify the receiver,
5. send the note text (including header content),
6. quit.
The mail server will send the mail upon receiving the
quit.
First, we need to connect to the mail server using the
of_connect function passing the server name and
(Continued on page 38)

Page 37

END IF

(Continued from page 37)

port number (25 corresponds to SMTP) - see listing


12 below. According to RFC811, the server will
respond to the connect with a 220 success message.
The response is checked by calling
of_getresponse and looking for 220 somewhere
within the response. The next phase is to say hello
to the server via HELO mailservername~r~n.
Since this is a command for which a response is
expected, we can use of_sendcommand that will return the response. The mail server responds to
HELO with a 250. To identify ourselves, use
of_sendcommand to send MAIL FROM:<mailfrom
address>~r~n. A response of 250 indicates success. Now identify who will be receiving the mail by
sending RCPT TO:<mailto address>~r~n. A
250 response also indicates success. Multiple recipients can be specified by repeating the RCPT command multiple times. The next command tells the
mail server that note text is coming. Use
of_sendcommand to send DATA~r~n to indicate
this (a 354 indicates success). Everything from this
point forward will be considered note text and the
server will not respond until the end of note text
indicator is sent (a ~r~n.~r~n).
At this point, RFC812 describes the header content
for the note such as the subject line. RFC812 allows
for a wide range of options including CC, BCC,
Reply to, etc. In its basic form (and the one we use),
simply the date, from address, to address and subject
line is required. The header is delimited from the
body text by a single blank line (~r~n). Because the
server will not respond until the end of note text
indicator, all of the header information and the note
text will be sent using the of_send function.
After sending the note text, we send the end of note
indicator, ~r~n.~r~n, using the of_sendcommand.
The server should respond with a 250. To tell the
server to send the mail, issue a QUIT via
of_send. The server will respond by closing the
socket and sending the message.
string ls_response
string ls_fromid = "troberts@bigfoot.com"
string ls_mailserver = "mymail.server.com"
of_connect(ls_mailserver,25)
IF ib_socketerror THEN
Return "Socket error connecting: " +
String(ii_socketerrcode)
END IF

ls_response = string(of_sendcommand("MAIL
FROM:<" + ls_fromid+">~r~n"))
IF Pos(ls_response,"250",1) = 0 THEN
Return ls_response
END IF
ls_response = string(of_sendcommand("RCPT
TO:<"+as_mailto+">~r~n"))
IF Pos(ls_response,"250",1) = 0 THEN
Return ls_response
END IF
ls_response =
string(of_sendcommand("DATA~r~n"))
IF Pos(ls_response,"354",1) = 0 THEN
Return ls_response
END IF
of_send(Blob("Date:
"+string(DateTime(Today(),Now()),"DD MMM
YYYY HH:MM:SS")+" GMT~r~n"))
IF ib_socketerror THEN
Return "Socket Error sending DATE: " +
String(ii_socketerrcode)
END IF
of_send(Blob("From: "+ls_fromid+"~r~n"))
IF ib_socketerror THEN
Return "Socket Error sending FROM: " +
String(ii_socketerrcode)
END IF
of_send(Blob("To: "+as_mailto+"~r~n"))
IF ib_socketerror THEN
Return "Socket Error sending TO: " +
String(ii_socketerrcode)
END IF
of_send(Blob("Subject:
"+as_subject+"~r~n"))
IF ib_socketerror THEN
Return "Socket Error sending TO: " +
String(ii_socketerrcode)
END IF
of_send(Blob("~r~n"))
IF ib_socketerror THEN
Return "Socket Error sending HEADER
Delimiter: " +
String(ii_socketerrcode)
END IF
of_send(Blob(as_note))
IF ib_socketerror THEN
Return "Socket Error sending NOTE
text: " + String(ii_socketerrcode)
END IF
ls_response =
string(of_sendcommand("~r~n.~r~n"))
IF Pos(ls_response,"250",1) = 0 THEN
Return ls_response
END IF
of_send(Blob("QUIT~r~n"))
Return ""

ls_response = string(of_getresponse())
IF Pos(ls_response,"220",1) = 0 THEN
Return ls_response
END IF
ls_response = string(of_sendcommand("HELO "
+ ls_mailserver+"~r~n"))
IF Pos(ls_response,"250",1) = 0 THEN
Return ls_response

You may remember that we talked about an event


called ue_receivecomplete that will allow the protocol to determine when we have completed receiving a response. For the SMTP protocol, each response from the server will be delimited by a ~r~n

Page 38

(Continued on page 39)

END IF

(Continued from page 38)

(see listing 13 below). To code for this, simply look


for the ~r~n somewhere in the receiving blob. If
found, return TRUE. If not, return FALSE to force
the socket to wait for additional data.
IF Pos(String(iblb_response),"~r~n",1)>0
THEN
Return TRUE
ELSE
Return FALSE
END IF

of_connect(ls_servername,li_portno)
IF ib_socketerror THEN
Return "Socket error connecting: " + &
String(ii_socketerrcode)
END IF

HTTP
To retrieve a web page using the Hyper-Text Transport Protocol (HTTP), create a custom visual user
object called u_http inherited from u_socket. The
HTTP protocol is outlined in RFC1945 and defines
the process for requesting a web page as:
1. connect to the server
2. request the page
The connection will be closed by the server after
each request.
Define a function called of_getpage() with a string
parameter (the request) which returns a string (see
listing 14 below). First, parse the request to get the
server name. The server name will be delimited by
the first single slash encountered after the http://.
Text to the left of the slash will be the server name.
Text to the right of and including the single slash is
the web page request. If no slash was found, extract
the server name and default the page request to a
single slash - meaning retrieve the root page.
Now connect to the server using of_connect passing
it the servername and port number (80 corresponds
to HTTP). If no error occurred with the connect,
format the GET page command as designated by
RFC1945 - GET webpage HTTP/1.0~r~n~r~n.
Then send this command using of_sendcommand
which will return the request web page. Nothing is
coded into the ue_receivecomplete because the
server will close the socket after fulfilling the request. The socket object simply keeps reading until
the socket is closed.
string ls_pagerequest, ls_getrequest,
ls_returnhtml, ls_servername
int li_portno, li_portstart, li_serverend
li_serverend = Pos(as_page,"/",8)
Assumes request starts with http://
IF li_serverend > 0 THEN
ls_servername = Mid(as_page,8, &
li_serverend - 8)
ls_pagerequest = &
Mid(as_page,li_serverend, &
Len(as_page))
ELSE
ls_servername = Mid(as_page,8, &
Len(as_page))
ls_pagerequest = "/"

li_portstart = Pos(ls_servername,":",1)
IF li_portstart = 0 THEN
li_portno = 80
ELSE
li_portno = &
Integer(Right(ls_servername, &
Len(ls_servername) - li_portstart))
ls_servername = Left(ls_servername, &
li_portstart - 1)
END IF

//

ls_getrequest="GET " + ls_pagerequest + &


" HTTP/1.0~r~n~r~n"
ls_returnhtml = &
string(of_sendcommand(ls_getrequest))
IF ib_socketerror THEN
Return "Socket error sending request:"
+ String(ii_socketerrcode)
END IF
Return ls_returnhtml

Application
How do you use these objects in the application? To
retrieve a web page, define an instance variable
iuo_http from class u_http (see listing 15 below).
Then use OpenUserObject(iuo_http) to create the object.
Call
iuo_http.of_getpage(http://www.sybase.com)
to retrieve Sybases root web page and close the user
object by using CloseUserObject(iuo_http).
u_http iuo_http
string ls_result
OpenUserObject(iuo_http)
ls_result = &
iuo_http.of_getpage(http://
www.powersoft.com)
CloseUserObject(iuo_http)

Similarly, to send mail from the application define


an instance variable iuo_smtp from class u_smtp.
Use Openuserobject(iuo_smtp) to create the object.
To
send
mail,
call
iuo_smtp.of_sendmessage(troberts@bigfoot.com,
It works,This actually worked). And finally,
close the object using Closeuserobject(iuo_smtp). Its
that simple!

Gotchas
Like all fun things, there is always a downside. You
should NEVER use messageboxes or the Powerbuilder debugger whenever you expect a socket notification to occur (such as stepping through reading from
a socket). Both messageboxes and the Powerbuilder
debugger seem to block out socket notifications. The
(Continued on page 40)

Page 39

(Continued from page 39)

best advice I can give is to modify the objects to


write to a log file. Then review the log file to
determine if and what problems occurred.
If you are serious about creating an application that
utilizes the Powersocket, you will need a winsock
debugger. A winsock debugger is a middleman between your application and the winsock dll. It will
trace all socket calls your application makes and
allows you to view the parameters or data associated
with those calls - very handy. You can also use it to
trace Netscape, Explorer or any other winsock application to see what they do! An excellent winsock
debugger is TracePlus found at http://
www.sstinc.com/sstinc.

Tim Roberts is an independent consultant and


principle of Armageddon Software in the
Chicagoland region with 5+ years of experience in
Powerbuilder, 10+ years of experience on the
mainframe and 10+ years of experience in database
design (http://www.armageddonsoftware.com).
He specializes (but is not limited to!) in managing
the conversion of or interfacing to mainframe
legacy systems and databases using Powerbuilder.
He can be reached by sending an email to
troberts@bigfoot.com (or
troberts@armageddonsoftware.com).

Finally
Hopefully this article has given you an idea of what
can be done and how you can accomplish it.
Whether its retrieving a web page or sending mail,
using the Powersocket library will enable your application to do it. Now you can proudly say no pr
oblem to those annoying clients.

We gladly publish the following request received from Sharon Buntz,


creater of the the website http://www.pfccheatsheet.com

Hello PFC Gurus!


Do you know of any Y2K issues with the PFC ?
If so, EARS ARE NOW OPEN! So please either...

Post a note to the Sybase PFC Newsgroup at


news://forums.powersoft.com/powersoft.public.powerbuilder.pfc
(with a subject preferably beginning with: Y2K)

or File an Electronic Case Management (ECM) at


http://support.sybase.com/cm_choice.html

or Call Technical Support at


1-800-8SYBASE (in the USA or Canada)

or Email a note to a PFC Mail List such as


mailto:pfcsig@rssi.com or
mailto:PBPFC@DelWare.com
(again, with a subject preferably beginning with: Y2K)

or Feel free to write me at


mailto:sbuntz@texas.net

Your help is greatly appreciated.

Page 40

CUSTOMIZING INFOMAKER
by Terry Dykstra

Introduction
Most PowerBuilder developers are familiar with
Infomaker. They know it is a reporting tool and that
it allows the user to create simple data entry screens.
What few people seem to know, however, is that
Infomaker can be highly customized using Powerbuilder. This article will look at two methods of
customization:
Custom form styles
Modifying imstyle6.pbl
Custom form styles have been available since Powermaker 3.0. Powerbuilder Developers use the
imstyleX.pbl as a template to create custom forms
styles.
With Infomaker 6.0 (IM), imstyle6.pbl has become a
part of the development environment. It's basically a
class library. That means changing this library affects the way IM works, and especially what an IM
user can create.

Custom Form Styles


For a detailed explanation on building forms, see the
Applications Techniques manual, chapter 33:
Building InfoMaker Styles and Actions.
A form is nothing else than a window with associated menu. The window must be saved with a comment that begins with the word style:. IM specifically looks for this in order to recognize it as a
custom form style. The window has strict naming
conventions for the DataWindow controls placed on
it. Table 1 shows the required names for the dwcontrol.
By using predefined names for the dw-control, IM
will automatically invoke the table select painter to
define your SQL select statement. The DataWindow is automatically built by IM. SQLCA is
automatically handled behind the scenes.

DataWindow control
names
Dw_freeform only
Dw_grid and
dw_freeform

Dw_master_12many
and dw_detail_12many
Dw_master_many21
and dw_detail_many21

Type of form style


Freeform
Grid (dw_grid is the central dw control;
dw_freeform shares the result set and serves as the
background, allowing
users to place computed
fields anywhere in the
form)
Master Detail /
One-To-Many
Master Detail /
Many-To-One

Table 1, DW control naming conventions


DataWindow objects with the same name as the
window plus an extension of @1 and possibly @2,
depending on whether there are 1 or 2 dw-controls
on the window. For example, when you create a
freeform form called w_facility, the associated dwobject is called w_facility@1. Unlike PowerBuilder,
you do not associate a dw-object with the dwcontrol. As a result, you cannot control, for instance, the tab-order on the dw-grid, or create groups
or computed fields. Or can you?

Undocumented Form Style Feature


The solution is actually easy. Build a window with a
dw-control on it and give it any name except the
required names, e.g. dw_drilldown. You dont even
have to code SQLCA! IM will take care of that.
Save the window with the Style: comment. Magic
happens! When the IM user builds a form using this
form style, he will not be prompted to create a Select
statement. On his form he can right-click on the
dw-control and he is presented with the report dialog
as shown in figure 1 . Notice that the user can now
browse and select any of his reports.

One of the problems with the built-in forms is the


limited amount of control the user has over the
layout and functionality of the DataWindow. The
DataWindow object that IM generates is hidden to
the IM user. When you browse the pbl with PB, you
will see that IM has generated one or two

Page 41

(Continued on page 43)

PowerGen provides an easy-to-manage facility for


specifying how you want to create your PowerBuilder
executables...the make facility you have been waiting
for!
-PowerBuilder Developers Journal

Tool of
the
month

build your PB application with PowerGen three


times faster than before
Support for PowerBuilder Version 4/5/6/6.5

ImagN

Corner
Stone 6.5

PowerDoc

Show what youve Still the best


got.
choice for develoThe family of ImagN tool- ping with PB 5
kits takes the headache out
of integrating graphics ima- and PB 6.x.
ges into your Windows applications!
Display BMP, PCX, DCX,
GIF, JPEG, Progressiv
JPEG, PIC, Targa, and TIFF
images using ImagNs powerful 16 & 32-bit engines.
Super FAST viewing and
print engine available as an
ActiveX or DLL/VCL

Get a headstart right out of


the box. Powerful extension
to the PFC and a collection
of predefined templates
which boosts up your projects. The only complete
object-oriented class library
for the PFC. Written from
the ground up for use with
the PowerBuilder development suite.

Power Spy
1.65

Get your self some Get an inner sight


extra sparetime.
of whats going on
PowerDoc automates the in your PB 6.5 Apentire process of generating
high quality, technical docu- plication.
mentation. With simple,
easy-to-understand instructions, you select the PowerBuilder application to document, and PowerDoc does
the rest, including the preparation of Table of Contents
and Index.

Am Borsigturm 48
D-13507 Berlin
Germany
Phone: +49 (0)30 430 323 50
Fax: +49 (0)30 430 323 55
Email: info@powerpeople.de

Page 42

More than a debugger. The


service-based TreeView and
ListView controls are now
PB Spy objects with Message Pump and other features. Just-in-time debugging has been built into the
Message Service and the
Action Menu so that you can
break into the source code.

If drilldown_level >= 1 and drilldown_level


<= 3 Then
If drilldown_level - ii_level <> 1 Then
Messagebox("Stop", "Cannot go to level "
+ string(drilldown_level) + " from " +
string(ii_level))
Return
Else
ii_level = drilldown_level
This.Title = "Level: " +
string(ii_level)
End If
Else
Messagebox("Stop", "Drilldown Level
should be between 1 and 3 !")
Return
End If

(Continued from page 41)

// Load report argument names


ls_colname[1] = retrieval _arg1
ls_colname[2] = retrieval _arg2
ls_colname[3] = retrieval _arg3

Figure 1, report dialog


This opens up some exciting possibilities. You can
now start building forms that have a lot more flexibility.

Drill-down Form Example


Lets look at an example, a drill-down form, using
this new method. Well limit the form to 3 levels
deep, with a maximum of 3 retrieval arguments.
On window w_drilldown, place a dw-control
dw_drilldown. Save the window with comment Style
"Drilldown" and add the following code as indicated:
Instance variables:
datastore
ids_level[]
long
il_row
int
ii_level
Dw_drilldown:
clicked event
If Not IsNull(row) Then
This.SelectRow(0, False)
This.SetRow(row)
This.SelectRow(row, True)
il_row = row
End If
w_drilldown:
public function drilldown_level1 (string
as_report, integer drilldown_level, string
retrieval_arg1, string retrieval_arg2,
string retrieval_arg3)
// Note: A public function becomes an action
in Infomaker
int
i
string ls_coltype,ls_describe,
ls_colname[3]
any
la_repval[3]
// Do we have a valid row?
If IsNull(il_row)or il_row < 1 Then Return
End If
// Verify that we have a valid drilldown
level

// Move current data (primary and filter


buffer) to backup datastore
ids_level[ii_level] = Create
datastore
ids_level[ii_level].dataobject =
dw_drilldown.dataobject
dw_drilldown.RowsCopy(1,
dw_drilldown.RowCount(),Primary!,
ids_level[ii_level],1, Primary!)
dw_drilldown.RowsCopy(1,
dw_drilldown.FilteredCount(), Filter!,
ids_level[ii_level], 1, Filter!)
// Get the actual value for column being
passed
For i = 1 To 3
ls_describe = ls_colname[i] + ".Coltype"
ls_coltype =
dw_drilldown.Describe(ls_describe)
If ls_coltype = "!" Then
Continue
/* Unknown colum */
End If
Choose Case mid(ls_coltype,1,5)
Case "numbe", "long"
la_repval[i] =
dw_drilldown.GetItemNumber(il_row,
ls_colname[i])
Case "decim"
la_repval[i] =
dw_drilldown.GetItemDecimal(il_row,
ls_colname[i])
Case "date"
la_repval[i] =
dw_drilldown.GetItemDate(il_row,
ls_colname[i])
Case "datet"
la_repval[i] =
dw_drilldown.GetItemDateTime(il_row,
ls_colname[i])
Case "time", "times"
la_repval[i] =
dw_drilldown.GetItemTime(il_row,
ls_colname[i])
Case "char(", "char"
la_repval[i] =
dw_drilldown.GetItemString(il_row,
ls_colname[i])
End Choose
End For
dw_drilldown.dataobject = as_report
dw_drilldown.SetTransobject(SQLCA)
// Potentially Overloaded retrieve()
dw_drilldown.Retrieve( la_repval[1],

Page 43

(Continued on page 44)

(Continued from page 43)


la_repval[2], la_repval[3] )
w_drilldown:
public function previous_level
If ii_level >= 1 Then
dw_drilldown.dataobject =
ids_level[ii_level].dataobject
ids_level[ii_level].RowsMove(1,
ids_level[ii_level].RowCount(), Primary!, dw_drilldown, 1, Primary!)
ids_level[ii_level].RowsMove(1,
ids_level[ii_level].FilteredCount(),
Filter!,dw_drilldown,1,Filter!)
Destroy ids_level[ii_level]
dw_drilldown.SetTransObject(SQLCA)
dw_drilldown.SetRedraw(True)
// Reset level up 1
ii_level = ii_level - 1
This.Title = "Level: " + string(
ii_level)
Else
Return
End If

Figure 3, Action Drilldown_level1

Modifying Imstyle6.pbl

Notice that we did not code a SetTransObject() and


retrieve() in our window open event. IM generates
this code when the user builds his form. In the open
event of the users form you will see:

Modifying imstyle6.pbl is where the fun starts. This


is the way to add functionality to Infomaker that is
missing. Examples might be:
A simple find window
SaveAsAscii() function
Adding more functions to the built-in forms
Mail: it exists in the development environment,
but not in the executables

Restrictions

call super::open;
dw_drilldown.SetTransObject( sqlca )
dw_drilldown.Retrieve()

In order to be able to select this new form style, the


Infomaker user will need to add the pbl containing
this to his Library Search Path in the environment
painter (you might need to customize the Environment toolbar to add the 'Options' button). All he
needs to do is define his reports and then build a
form using the custom style Drilldown built in PB, as
shown in figure 2.

Before you start modifying imstyle6.pbl, you have to


know some of the limitations:
You cannot modify the application object. Infomaker only recognizes the Default application
object. It is rebuilt with every compile.
You cannot add PBLs to the application library
search path. But you can use PBDs using the
SetLibraryList()
function
in
the
w_pbstyle_ancestor_frame.
Officially you
should use the application object, but since this
object is off-limits, use the frame. It works
fine.
No dynamic assignments. The following will
fail if d_graph is not in the users PBL:
Create Datastore Ids_Graph
Ids_Graph.Dataobject = "d_graph

Figure 2, Infomaker New Form Screen


The action Drilldown_level1 (see figure 3) is used to
specify which report he will drill down to. Note that
you must supply a value for all arguments.
Action Previous_level is used to go back to the
previous levels.

No control over the compile. It defaults to


compiled code, no PBRs allowed.
Most changes affect only executables built with
IM, not the development environment itself.
The exception being Forms, where your changes are reflected in the evelopment environment
as well.
Upgrading Infomaker might require you to redo
your changes. This is where a share-ware tool
like PBDelta (www.pbdr.com) is invaluable. It
allows you to compare PBLs quickly. For instance, I compared imstyle6.pbl with imstyle7.pbl and found that they are virtually identical.

(Continued on page 45)

Page 44

(Continued from page 44)

To keep my customization as manageable as possible, I use the following guidelines:


Use my own objects where possible and limit
the changes to IM's base objects.
Any of the base objects that I do modify, I
identify by prefixing the existing comments with
the name of our company: CFOL.

Example: Connect to a Different DB


One of the limitations that IM executables have is
that they are hardcoded for the database connection.
I like my end-users to be able to run the same reports
against different databases, e.g. Production and Test.
These databases are Oracle 7 based. Figure 4 shows
the custom login window. The list of databases can
be found in Oracle's \network\admin\tnsnames.ora
file.

If ls_space <> 0 Then


ls_pos = min(ls_space, ls_equal)
End If
If ls_period <> 0 Then
ls_pos = min(ls_period, ls_pos)
End If
ls_instance= "@" + left(ls_instance,
ls_pos - 1)
ddlb_database.additem (ls_instance)
End If
End If
Loop Until li_length < 0
FileClose(li_file)
cb_ok clicked event:
// add your own code to close all sheets
DISCONNECT;
sqlca.userid = sle_userid.text
sqlca.logid
= sle_userid.text
sqlca.dbpass = sle_password.text
sqlca.logpass = sle_password.text
sqlca.servername = ddlb_database.text
Connect Using SQLCA;
If sqlca.sqlcode <> 0 Then
Messagebox("Database Connect
Error",SQLCA.sqlerrtext)
Return
End If
// show user which database used
parent.parentwindow().Title =
sqlca.servername
close( parent )

Customize m_pbstyle_frame_ancestor:
Add a new menu with menu-items m_cust and
m_cust.m_connect. Code the following:
m_cust.m_connect:
window
lw_to_open
OpenWithParm(lw_to_open,
"w_cfol_logon")

Figure 4, Custom Login Window


Create a pbl im_shared_objects.pbl that contains
window w_cfol_logon. Add the following code as
indicated and then compile this pbl as a pbd.

parentwindow,

In imstyle6.pbl add custom object: u_cfol_library


(autoinstantiated)
User function of_set_library (string
pbd_name, window parentwindow_name)
int
li_rc
string
ls_dbms, ls_dbparm

w_cfol_logon:
open event:
string
ls_ora_network,ls_tnsnames,
ls_instance
int
li_file, li_step, li_length,
ls_space, ls_equal,ls_period, ls_pos
sle_userid.text
= sqlca.userid
sle_password.text = sqlca.dbpass
// Get location of tnsnames.ora
RegistryGet("Hkey_Local_Machine\Software\Or
acle", "Net20", RegString!, ls_ora_network
)
ls_tnsnames = ls_ora_network +
"\\ADMIN\\TNSNAMES.ORA"
// Populate the list of Oracle servers from
tnsnames.ora
li_file = FileOpen ( ls_tnsnames, LineMode!, Read!, LockWrite! )
li_length = 0
Do
li_length = FileRead(li_file,
ls_instance)
If li_length > 0 Then
If left(ls_instance,1) <> " " and
left(ls_instance,1) <> "#" Then
ls_space = pos(ls_instance, " ",1)
ls_equal = pos(ls_instance, "=",1)
ls_period = pos(ls_instance, ".",1)
ls_pos = ls_equal

m_pbstyle_frame_ancestor m_current_menu
m_current_menu = parentwindow_name.MenuId
ls_dbms = left(SQLCA.DBMS,2)
ls_dbparm = SQLCA.Database
If FileExists( pbd_name ) And (ls_dbms =
'OR' OR ls_dbms = 'O7') Then
li_rc=default.SetLibraryList(pbd_name)
If li_rc <> 1 Then
m_current_menu.m_cust.visible = false
m_current_menu.m_cust.m_connect.enabled =
false
MessageBox("Warning: " + string(li_rc)
,"Could not set library list " +
pbd_name)
Else
parentwindow_name.Title =
SQLCA.Servername
m_current_menu. m_cust.visible = true
m_current_menu. m_cust.m_connect.enabled
= true
End If
Else
m_current_menu. m_cust.visible = false
m_current_menu. m_cust.m_connect.enabled
= false
End If

Page 45

(Continued on page 46)

Conclusion

(Continued from page 45)

By customizing Infomaker, you can provide your


users with a lot more options than they have available right now. Experiment with it. And in case
you are wondering, it works just fine in release 7 too.

Customize w_pbstyle_ancestor_frame:
At the end of the open event add:
u_cfol_library
lu_cfol_library
lu_cfol_library.of_set_librarylist(
"im_shared_objects.pbd", This )

When the user creates an executable, he needs to


place the im_shared_objects.pbd in the same directory. When he connects with the native Oracle
driver, he will be able to switch to a different database. When the file doesn't exist, or he uses a
different driver, he will not even have the custom
connect option.

?
Terry Dykstra is a Database Administrator at
Canadian Forest Oil Ltd., Calgary AB, Canada.
He is a member of Team Powersoft. You can
write him at tdykstra @cfol.ab.ca. He loves to
hear about your customization projects.

Page 46

PBL PEEPER 1.1.0


by Terry Voth
Reviewed by Arthur Hefti

Introduction
PBL Peeper is a tool to view, analyze and compare
PowerBuilder objects. You can perform a variety of
different, helpful tasks within PBL Peeper. These are
described in the rest of the article.
The product is installed using InstallShield. The
application and the manual (in PDF format) are
copied to a single directory. Menu entries in PowerBuilder are created for each of them.

Features
The application has a main window with a large tab
control on it. Each function has its own tab page.
After starting, the application the PBL page is selected. It shows you a list of applications extracted from
the PB.INI. From here you can select the PBLs you
will work with.
The second page is the Browse. Here you can drill
down and browse your objects. For DataWindows,
you see the general definitions, the SQL-part, each
band with the corresponding columns and text fields.
For windows, you have the control properties and all
events and functions declared on it with source code.
On the List page you have different types of lists that
you can filter, sort and print. The following lists are
available: Objects, Scripts, Controls, DataWindow
Objects and References. Building these lists can take
a lot of time, so watch out.
The purpose of the Find page is to scan the selected
PBLs for the occurrence of a certain. string. The
search is customizable: you can specify how to
search (case sensitive or not, match() expression,
whole words only) and where to search (scripts,
scripts and variables, all, include comments, include
text in quotes). The results are presented in a rich
text control that can be printed.

In the Trace page you can load a PowerBuilder


debug file created with /PBDEBUG. The file is
displayed in a tree view on the left side and if you
have selected the PBLs you can see the source code
on the right side of the window.
The Exports page is a very convenient way to export
and import more than one object at a time.
The Process page is a very powerful way to change
objects in your application and should never be done
without a backup. You can replace, remove or format code, or use one of the three predefined options.
On the Compare Page the comparison between two
versions can be done at object or application level.

In My Opinion
PBL Peeper contains a set of very useful tools all
under one cover. It should be part the toolbox of
every PowerBuilder programmer who needs more
than the basic features PB provides.
The manual also contains a section that explains how
you can track problems in your application with PBL
Peeper.

Pricing and Availability


PBL Peeper 1.1.0 is free and is provided "as is".
The current version (most probably Version 1.2 by
the time you read this) can be downloaded from
http://www.visual.com/gallery/. Follow the Sequel
Sandbox Link.
Author:
Terry Voth: tvoth@home.com
Techno-Kitten Production

On the Report page there are four reports available:


List SQL in all DataWindows
Create PBR content for an EXE
Report the comments
Object Inheritance Report (optional Controls)

Page 47

CATsoft

a Client/Server and Internet development and consulting company

Tools
Client /Server

Development
- Custom made Software in any Business
Area

Internet

Enterprise I*Net

Development
- Custom made Web Sites
+ Easy to maintain
+ Fast Development
+ Public Hosting available

Development
- Custom made Web Sites for Intranet and Internet usage

Training
- For Sybase AG and Resale
- ColdFusion
SQL AG
- HomeSite
- ScriptBuilder
Consulting
- On various PowerBuilder Topics

Sybase CAP Partner

ColdFusion Reseller

Resale
- SilverStream Reseller

SilverStream VAR

Catsoft Development GmbH, Amdenerstr. 28a, CH-8872 Weesen


Tel: ++41 55 616 51 11, Fax ++41 616 51 14
Web: www.catsoft.ch, E-Mail: info@catsoft.ch, Compuserve: CATsoftCH

Page 48

DETERMINE THE ORIGINAL SIZE OF A WINDOW


by Arthur Hefti

Introduction
After reading the article in the last issue of PowerTimes concerning the PFC-Resize service, I started
thinking of how it would be possible to find the
original size of a window and the actual workspace
height and width. Not that I cared too much during
development, I normally hard-coded the original
size. I did, however, find three different approaches
which I will describe.

1. LibraryExport
The first idea I had was to export the window
definition from a library and read the width and
height from the source. This would be easy as long
as there was no inheritance, otherwise I would have
to scan through the inheritance tree to find an ancestor where the width and height are saved. Remember the descendant only contains values for properties that are different than the ones in the ancestor.

Height: li_Height - PixelsToUnits( &


YPixelsToUnits!, 2 * li_FrameY + &
li_TitleHeight )

3. The Class Definition Object


The class definition object was introduced in PowerBuilder 6 and provides information about the class
definition of a PowerBuilder Object.
Classdefinition
lcd_WD
Variabledefinition
lvd_WD
Integer
li_Cnt, li_Max,
li_Width, li_Height
lcd_WD = This.ClassDefinition
li_Max = UpperBound(lcd_WD.VariableList)

2. Create Using

FOR li_Cnt = 1 TO li_Max


lvd_WD = lcd_WD.VariableList[li_Cnt]
IF lvd_WD.name = 'width' then
li_Width = lvd_WD.initialvalue
END IF
IF lvd_WD.name = 'height' THEN
li_Height = lvd_WD.initialvalue
END IF
NEXT

The second approach is suitable for PowerBuilder 5


and 6. It creates the window as a NVO by Create
Using.

After finding these values the values for the workspace width and height can be calculated.

integer
li_Width, li_Height, li_FrameX,
li_FrameY, li_TitleHeight
window lw_Temp
lw_Temp = CREATE USING This.ClassName()
li_Width = lw_Temp.Width
li_Height = lw_Temp.Height
DESTROY lw_Temp

This gives me the width and height including the


borders and the title bar. Running the application on
Windows, the size of the title bar and borders can be
determined by a call to the API function GetSystemMetrics().

Conclusion
Comparing all these approaches, it looks like the
second (Create Using) is the easiest to use. In addition, it can be used with PB 5 and 6. However,
Create Using creates an invisible instance of the
window in the memory. As soon as the window isnt
very simple, this will take some time to do. The third
approach (using the Class Definition Object) is much
faster, but can only be used with PB 6.

function int GetSystemMetrics( &


int nIndex ) Library "USER32"
//Values
li_FrameX = GetSystemMetrics( 32 )
li_FrameY = GetSystemMetrics( 33 )
li_TitleHeight = GetSystemMetrics( 4 )

After collecting this information the workspace


height and width can be determined by:

Arthur Hefti (CPI, CPD-Professional) is owner of


CATsoft Development GmbH a leading Client/
Server and I*net Development and Consulting
Company in Switzerland. You may write Arthur at
arthur@catsoft.ch.

Width: li_Width PixelsToUnits( &


XPixelsToUnits!, 2 * li_FrameX )

Page 49

PowerTimes provides this space to the subscribing


user groups to publish their news, announcements
and other communications to their members. Please
send your material to the editor or the co-editor of
PowerTimes (addresses under Impressum).

If your user group doesnt subscribe to PowerTimes


yet, please contact us to get the very reasonable rates
or check out our web site at:

http://www.powertimes.com

Baltimore PowerBuilder User Group


The Baltimore Area PowerBuilder Users Group
(BAPBUG) meets the first
Wednesday of the even numbered months at the Social Security Administration OAG
building in Woodlawn, Maryland.

The members of the Baltimore PowerBuilder User


Group receive PowerTimes thanks to a generous
sponsorship from

http://www.geocities.com/SiliconValley/Bay/8680

http://www.ctpartners.com

President:

Debbie Arczynski
debbie@charm.net

Vice President:

Bill Bitman
bitman@jhuapl.edu

Secretary:

Gordon Giffen
gordon-g@vips.com

Treasurer:

Fred Grau
fgrau@chesint.net

Specializing in the Sybase, SilverStream and


Microsoft families of products, Corporate Technology Partners offers a full line of client/server and
web application development and training services.
Point your browser to http://www.ctpartners.com/
asp/tips.asp for frequently updated, cutting edge
whitepapers, and a variety of helpful, technical tips
from industry pros. Visit today to see how we can
help your organization realize the most effective
emerging technology solution.

Web Administrator: Anne Sola


anne-s@vips.com
Newsletter Editor:

Greg Dzingeleski
greg.dzingeleski@ssa.gov

Calgary PowerBuilder User Group


The President of the Calgary PowerBuilder User
Group, Todd Costella, can be reached at the
following e-mail address:
Todd.Costella@OmniVisionSystems.com
The members of the Calgary PowerBuilder User
Group receive PowerTimes thanks to a generous
sponsorship from aRenDeeco, Inc.

Page 50

Greater Boston PowerBuilder Users Group


The officers of the User Group are:
President:

Dan Murphy
dmurphy@wtgi.com

Vice President:

Olga I. Demidova
olgad@middlecg.com

Treasurer:

Karen Ness
karen_ness@analog.com

Secretary:

Susan Thompson
idfm@idfm.com

KEVSystems is a PowerSoft Consulting Partner and


provides system design and integration assistance
for financial, personnel, and medical systems.
Our staff, made up exclusively of Certified PowerBuilder Developers, has over 20 years experience in
securities processing systems and database design.
We have worked on systems for trust accounting,
shop floor management, personnel, and three-tiered
integration.

The members of the Greater Boston PowerBuilder


User Group receive PowerTimes thanks to a generous sponsorship from KEVSystems Inc., located in
Newton, MA.

KEV Systems, Inc.


65 Stony Brae Rd.
Newton, MA. 02161-1728
Phone:
Fax:

(617) 964 1426 or (800) 376 5755


(617) 527 0090

ChicagoLand PowerBuilder User Group


The CPBUG website can be found at:

The officers of the CPBUG are:


President
Vice-President
Secretary
Treasurer

Michael Baraz
Peter Vassilatos
Jeff Barnes
Nicky Petrila

(630) 235-4529
(312)856-6720
(847) 549-7765
(630) 792-5073

http://www.cpbug.org
The Secretary of the ChicagoLand PowerBuilder
User Group, Jeff Barnes, can be reached at the
following e-mail address: jcbarnes@ibm.net.

Central Ohio PowerBuilder User Group


President:

Vince Fabro
vfabro@col.nmedia.com
614 - 220 7900

Vice President:

Jeff Kusner
jkusner@kusner.com
614 - 213 5329

Refreshments: 6:00 - 6:15pm, Presentation: 6:15

Secretary:

Our mailing address is:


Central Ohio PowerBuilder User Group
P.O. Box 363
Worthington, OH 43085-0363

Jim Nelson
jlnelson@aep.com
614 - 418 1748

Treasurer:

Todd Dake
todd@airnet.com
614 - 418 1748

The Central Ohio PowerBuilder Users Group generally meets during the second week of odd numbered
months. Meetings alternate between member demos
and product presentations.
Location:

Internet:

Bank One Offices


1111 Schrock Road, Columbus, OH

http://www.cmhpbug.com

Page 51

Club Fibonacci - Club des Utilisateurs Sybase France


Le club francais des utilisateurs PowerBuilder n'existe plus en France. Il a t remplac par le Club
Fibonacci: le club des utilisateurs Sybase, qui est
gr directement par Sybase France.
Les membres du Club Fibonacci reoivent PowerTimes grace au sponsoring gnreux de la socit
NOVALYS.
(The PowerBuilder User Group France doesn't existe anymore. It has been replaced by the "Club
Fibonacci" - the Sybase Users Group in France
The members of the Club Fibonacci (France) receive PowerTimes thanks to a generous sponsorship
from Novalys.).

NOVALYS met en place des


outils, mthodes et normes de
dveloppement pour la russite de
vos applications. Les consultants
de Novalys accompagnent et certifient vos projets
pour garantir leur russite technique.
NOVALYS creates, distributes and supports
complementary tools for PowerBuilder and is the
distributor of HOW from Riverton Software in
France.
Visit our website at: http://www.novalys.fr.
Novalys SA
140 rue Gallini
92100 Boulogne-Billancourt / France
Tel : 01.46.99.66.20

Fax: 01.46.99.08.92

PowerBuilder User Group Denmark


The members of the PowerBuilder User Group
Denmark receive PowerTimes thanks to a
generous sponsorship from Sybase Denmark.

E-mail:
Tel:
Fax:
Internet:

rasmus@aloc.dk
+45 6313-6128
+45 6313-6199
http://www.sybase.dk/pbusgdk

New Jersey PowerBuilder User Group


The President of the New Jersey PowerBuilder User
Group, Boris Gasin, can be reached at the following
e-mail address: bgasin@dynamictechgroup.com.
Dynamic Technology Group, Inc.
1055 Parsippany Blvd., Suite 501-26
Parsippany, NJ 07054

The NJPBUG website can be found at:

http://www.njpbug.org
The members of the New Jersey PowerBuilder User
Group receive PowerTimes thanks to a generous
sponsorship from Dynamic Technology Group.

Page 52

Phone:
Fax:

973.402.5600
973.402.5620

http://www.dynamictechgroup.com

Houston PowerBuilder User Group


The members of the Houston PowerBuilder User
Group receive PowerTimes thanks to a generous
sponsorship from Dynamic Data Solutions, Inc.

President:

Vice President:

Rick Pal
prez@hpbug.org

Ken Frohne
viceprez@hpbug.org

Conference Coordinator:

Ron Francis
coord@hpbug.org

Treasurer:

Nonie Cody
treas@hpbug.com

Secretary:

Ram Raman
sect@hpbug.org

Newsletter Editor:

Randeep Takkar
editor@hpbug.org

http://www.dyn-data.com
Dynamic Data Solutions provides expert application development consulting in the areas of traditional and web client/server focusing on integrated
solutions from the Sybase/Powersoft and Microsoft
families of products.
Come check our Tech Zone at http://www.dyndata.com/ims/techzone/techzone.stm for a searchable database of development tips as well as real
world examples of our expertise at work.

Northwest PowerBuilder User Group


Meetings are generally scheduled once every other
month.

The members of the NorthWest PowerBuilder User


Group receive PowerTimes thanks to a generous
sponsorship from ServerLogic Corporation.

The Northwest PB User Group URL is:

http://www.cascadia-sw.com/nwpbug/
The NWPBUG officers are:
President:

Anirban Choudhury
anirban@iname.com

Vice President:

Paul Gibson
pgibson@co.pierce.wa.us

Secretary:

Anil Batra
batra1968@aol.com

Treasurer:

Connie Butler
connie@cadien.com

http: //www.serverlogic.com

Feel free to get in touch with us through our web


site for more meeting info... we look forward to
hearing from you ! PBUG's can be fun (well at least
informative, trust us) !!

ServerLogic Corporation is a premier provider of


corporate software development and information
technology consulting. ServerLogic has worked with
hundreds of organizations throughout the US, Canada, and the world seeking effective consulting, tool,
and training solutions. Successful application development or migration of legacy applications to more
productive client/server or internet environments requires expertise in design, development, testing, and
implementation. ServerLogic has the experience to
help ensure your success in these areas.

Page 53

Indianapolis PowerBuilder User Group


The User Group meets on the 3rd Thursday of every
even month.
President:

Deanna Townsend
president@ipbug.org

VP:

Scott Cunningham
vicepresident@ipbug.org

Secretary:

Angie Kemerly
secretary@ipbug.org

Treasurer:

Bruce Kohl
treasurer@ipbug.org

The members of the Indianapolis PowerBuilder


User Group receive PowerTimes thanks to a generous sponsorship from

Since 1993, Fortune 1000 companies have looked


to Client Server Associates (CSA) for solutions to
their business computing needs. Offering software
products for purchase, consulting services and comprehensive and customized education programs,
CSA is a single source provider and partner throughout the Midwest with offices in Cincinnati, Columbus and Indianapolis.

Webmaster: webmaster@ipbug.org
Internet:
http://www.ipbug.org

PowerBuilder User Group Italia


L' abbonamento a PowerTimes per gli iscritti del
Power Builder User Group Italia e' stato
gentilmente offerto da Sybase Italia.
The members of the PowerBuilder User Group Italy
receive PowerTimes thanks to a generous
sponsorship from Sybase Italia.

Ricordiamo i nostri indirizzi Internet dove contattarci


email: pbuginfo@telemacus.it
e http://www.telemacus.it/pbug

PowerBuilder User Group Germany e.V.


Events:
26.4.99 - 27.4.99 Meeting der PBUGG in Bremen.
Anregungen zu Themen und Vortrgen werden
erbeten an vorstand@pbugg.de oder:
PBUGG e.V.
c/o Power People
Ludwin Feiten
Am Borsigturm 48
D-13507 Berlin

Besuchen Sie die Homepage


der PowerBuilder User Group
Germany:
http://www.pbugg.de

Tel.:
Fax:
E-mail:

Page 54

+49 (0)30 4303 2350


+49 (0)30 4303 2355
vorstand@pbugg.de

St. Louis PowerBuilder User Group


We meet on the 3rd Tuesday of every other month,
beginning in January at Monsanto Corporate Headquarters (directions on the web page). The meetings
begin at 5:30 pm.
The current website is http://home.i1.net/~stlpbug.
We are about to register a domain name, when this
is complete we will provide an update.
President:

Jay Hunt
djayhunt@illini.net

Vice President:

Allen Phelps
aphelps@anet-stl.com

Secretary:

Neal Bockwoldt
bockwold@primary.net

Treasurer:

Solutech is a company specializing in client/server,


web application development, technical training, vendor certified consulting and software solutions.
Solutech maintains strategic partnerships with leading
software vendors such as Microsoft, Oracle, Rational,
Powersoft and Sybase. These partnerships give you a
direct link to new product information and on-going
technology developments. Solutech is a local extension of our vendors, bringing you peace of mind in a
constantly changing environment.

Dave Blankenship
daveblankenship@prodigy.net

The members of the Indianapolis PowerBuilder


User Group receive PowerTimes thanks to a generous sponsorship from Solutech, Inc. in St. Louis.

Solutech has nine offices and training centers. For a


complete list of our free seminars and vendor certified training classes; or to view the latest company
information on Solutech, our locations, and our services, please visit us on the web at
www.solutechinc.com.

UK PowerBuilder User Group


An independent group formed
in 1992, PBUG currently has a
membership approaching 150
companies. Events are held
three times each year with
attendances in the region of
200 at each event. The group is
self-funding and receives
excellent technical support from Sybase.
PBUG Council:
Chairman:

Craig Malcolm
craig.malcolm@bsg.co.uk

Treasurer:

Ian Soden
Isoden@linkhand.com

Secretary:

David Speight
dspeight01@aol.com

The member council runs PBUG, but the day-to-day


work is managed by Pb Associates.
Anne Bocock is responsible for most aspects of the
group including organising the conferences and
putting together PBulletin. Judy Barnett handles all
accounting matters. You can reach Anne and Judy at
the PBUG office.
Telephone:
Fax:
Email:
Internet:

0181 421 3533


0181 420 1420
anne@pbug.co.uk
http://www.pbug.co.uk

Postal address:
Suite A8, Kebbell House
Carpenders Park
Watford, Herts. WD1 5BE

Page 55

Madison PowerBuilder User Group


President:

Eric Larson
(608)252-3385

Vice President:
Treasurer:
Secretary:
Webmaster:

Amy Jo Esser
Craig Cole
Bruce Meister
Jeffrey White

Internet:

http://www.madpug.org

Ottawa PowerBuilder User Group


The Ottawa PowerBuilder User's Group is
composed of Ottawa-area Information Systems professionals who are interested in
staying on the leading edge of PowerBuilder and client/server development.

President:

Carole Hargrave
chargrav@magi.com

The group meets on the 2nd Wednesday of each


month, from September through May. At each
meeting, a new and interesting topic is presented and
discussion is encouraged. These meetings are a great
place to meet fellow users, discuss issues and ideas,
and network for future opportunities.

VP Administration: Bill Stevens


bills@pentelar.com

VP Public Relations: Mike Woods


mikew@pentelar.com

Treasurer:

Mark Jorgensen
mjorgensen@roslynit.com

Secretary:

Pauline Aubertin
pa@pa-associates.on.ca

The Ottawa PowerBuilder User Group website can


be found at http://www.opbug.com

Swiss PowerBuilder User Group

We invite all members and interested non-members


to attend the international two day conference that
we're organising together with the Swiss Sybase
User Group at the Frigen Convention Center in
Central Switzerland.
Members pay only CHF 250.- for two days of
training, we have excellent speakers from the USA
and Europe. Please check our website at http://
www.spbug.ch for more detailed information.
There are PowerBuilder, Java and Database tracks
and speakers include PowerTimes authors T. Carson
Hager, Mike Gora and Mark Pfeifer as well as the
well-known SQL "Guru" Joe Celko, and others.

President:

Arthur Hefti
arthur@catsoft.ch

Vice-President:
& Webmaster

Rolf Andr Klaedtke


rak@powertimes.com

Treasurer:

Martin Rothenberger
mroots@bigfoot.com

Secretary:

Celia Hegarty
ch@sql.ch

"Worker":

Andreas Theis
andreas.theis@national.ch

Membership:
Individual member
Corporate member

CHF 100.- / year


CHF 300.- / year

Members receive PowerTimes and discounts or


special rebates on software.

Page 56

Anda mungkin juga menyukai