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
Advanced PowerBuilder:
Thread Synchronization
by Roy Kiesler on Page 27
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
VOL. 6 / ISSUE 2
beginning on page 50
(Continued on page 2)
Page 1
Impressum
Contact addresses:
Editor:
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:
Happy reading,
Rolf Andr Klaedtke and Mark Lansing
Page 2
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.
Page 3
(Continued on page 4)
date.
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
inv_newupdate[]
inv_newupdate[]
inv_newupdate[]
inv_querymodeinfo[]
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 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
(Continued on page 6)
Date Issues
Page 4
Page 5
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!)
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
?
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
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.
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)
Web Server
Web Browser
Page Server
HTML
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 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
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
Page 9
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
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");
Page 11
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.
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)
?
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() );
Page 12
Page 13
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.
Performance
Why Automation?
Page 14
Page 15
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
Page 16
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.
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.
?
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.
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
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
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.
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
In order to make the sample application fully functional, code was added to several objects.
Page 22
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.
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
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.
Database
Server
Client
Presentation
Order
Business
Data Access
Business
Component
DLK
BCM
UILink
UILink
Database
BEO
BCM
DLK
BEO
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
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.
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
http://www.cyrano.com
Page 26
THREAD SYNCHRONIZATION
by Roy Kiesler
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
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
2.
Thread A
Thread B
w_main::il_rows
RW
RW
w_main::ib_stop
n_cst_comm::ii_rc
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.
CRITICAL_SECTION cs;
struct CRITICAL_SECTION {
RTL_CRITICAL_SECTION_DEBUG *DebugInfo;
LONG
LockCount;
LONG
RecursionCount;
HANDLE OwningThread;
HANDLE LockSemaphore;
DWORD SpinCount;
}
//
// 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;
}
Page 29
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
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
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:
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.
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
Page 31
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.
Page 32
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.
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
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.
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
Automatic scheduled
backups.
Prevents data loss conflicts.
Increases productivity.
Replicates entire directory
branches or individual files.
Detects file changes at preset
intervals.
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.
IntelliGen builds all your corporate applications with the click of a button.
Regenerate your applications automatically.
Page 35
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
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
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
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
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!
Page 37
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
Page 38
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
//
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)
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
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.
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.
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
Page 41
Tool of
the
month
ImagN
Corner
Stone 6.5
PowerDoc
Power Spy
1.65
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
Page 43
Modifying Imstyle6.pbl
Restrictions
call super::open;
dw_drilldown.SetTransObject( sqlca )
dw_drilldown.Retrieve()
Page 44
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")
parentwindow,
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
Conclusion
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 )
?
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
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 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.
Page 47
CATsoft
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
ColdFusion Reseller
Resale
- SilverStream Reseller
SilverStream VAR
Page 48
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.
2. 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
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.
Page 49
http://www.powertimes.com
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
Greg Dzingeleski
greg.dzingeleski@ssa.gov
Page 50
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
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.
Vince Fabro
vfabro@col.nmedia.com
614 - 220 7900
Vice President:
Jeff Kusner
jkusner@kusner.com
614 - 213 5329
Secretary:
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:
http://www.cmhpbug.com
Page 51
Fax: 01.46.99.08.92
E-mail:
Tel:
Fax:
Internet:
rasmus@aloc.dk
+45 6313-6128
+45 6313-6199
http://www.sybase.dk/pbusgdk
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
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.
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
Page 53
Deanna Townsend
president@ipbug.org
VP:
Scott Cunningham
vicepresident@ipbug.org
Secretary:
Angie Kemerly
secretary@ipbug.org
Treasurer:
Bruce Kohl
treasurer@ipbug.org
Webmaster: webmaster@ipbug.org
Internet:
http://www.ipbug.org
Tel.:
Fax:
E-mail:
Page 54
Jay Hunt
djayhunt@illini.net
Vice President:
Allen Phelps
aphelps@anet-stl.com
Secretary:
Neal Bockwoldt
bockwold@primary.net
Treasurer:
Dave Blankenship
daveblankenship@prodigy.net
Craig Malcolm
craig.malcolm@bsg.co.uk
Treasurer:
Ian Soden
Isoden@linkhand.com
Secretary:
David Speight
dspeight01@aol.com
Postal address:
Suite A8, Kebbell House
Carpenders Park
Watford, Herts. WD1 5BE
Page 55
Eric Larson
(608)252-3385
Vice President:
Treasurer:
Secretary:
Webmaster:
Amy Jo Esser
Craig Cole
Bruce Meister
Jeffrey White
Internet:
http://www.madpug.org
President:
Carole Hargrave
chargrav@magi.com
Treasurer:
Mark Jorgensen
mjorgensen@roslynit.com
Secretary:
Pauline Aubertin
pa@pa-associates.on.ca
President:
Arthur Hefti
arthur@catsoft.ch
Vice-President:
& Webmaster
Treasurer:
Martin Rothenberger
mroots@bigfoot.com
Secretary:
Celia Hegarty
ch@sql.ch
"Worker":
Andreas Theis
andreas.theis@national.ch
Membership:
Individual member
Corporate member
Page 56