Some of these tips answer very frequently asked questions. Others are just plain interesting. Most are from
public postings on PB newsgroups & lists -- some by our staff, many by others. In some cases we lost
track of the name of the author. If we have included in this list something that you wrote, and if you would like
attribution, please drop us a line. If you would like to make some other kind of correction or your own contribution,
we'd be delighted to hear from you.
Alter Table - Adding not-null columns
Array - Changing from bounded to unbounded
Autologoff Bitwise and - Alternative
Connecting to multiple databases
Directory file list without a window
Dot notation - limitations in 5.0.0x
DW - changing the presentation style
DW - currentrow() and getrow()
DW column - highlighting of current row
DW column - setting value of computed column
DW - referencing computed fields
DW - finding column names at runtime
DW - finding controls at runtime
DW - preventing focus loss on tabbing from last row or column
DW
horizontal lines every
DW retrieval argument using aggregate function
DW update error, row changed between retrieve and update
Enter key - make it emulate the tab key
Login - Getting netware login name
PFC DDDW - multiple rows from a lookup
PFC Numeric service - hex extensions
PFC Transactions - optimistic locking, one opinion
PFC Windows - placing controls
Set default printer from within PB
Stored Procedure with Output Parameter
1. Export syntax of the table to the log.
2.
Save the log as a SQL file
3.
Drop the table.
4.
Go into the DBA Admin painter, open the SQL file that contains the syntax of
the table.
5.
Make changes.
6.
Execute the SQL. This will create the table again with the field options
changed.
ALTER TABLE - ADDING NOT-NULL
ATTRIBUTES OR COLUMNS
A
table doesn’t have to be dropped or empty to add a NOT NULL constraint. You have only to …
- make sure all rows are indeed not null on
the target field
- write and run the alter statement in the
DBA painter
You
have to leave and re-enter the painter to see the changes.
If new not null columns are to be added you either have to start with an empty table or do it in 3 steps:
first add the column, then fill in some values and finally set it to not null.
ARRAY – CHANGING FROM BOUNDED TO
UNBOUNDED
string
lsNull [ ], lsBounded [ 5 ], lsUnbounded [ ]
//
assume lsUnbounded has been populated elsewhere.
lsUnbounded
= ls_Null
lsUnbounded
= ls_Bounded
Now
unbounded array will contain the values of the bounded array, dimensioned at 5.
To autologoff after an hour of
inactivity, place code in events as follows:
1. application idle event: gnv_app.event pfc_idle()
2. appmanager contructor event:
Idle(3600)
3. appmanager pfc_idle event:
halt close
If you are familiar with the workings of the Idle event, then you could have it start a timer on your frame window in PB5,
and there is a timer NVO in PFC 6.0. Both of these will trigger a timer event after the specified time length has expired.
Method 1:
Quick & dirty: log out any open transactions, and call a HALT CLOSE. This will force the application to close
immediately without any further processing. If you want open windows to be able to perform specific processing
before
the
app closes, then try something like Method 2.
Method 2:
Since you are using PFC, you could use the sheetmanager application service to pull a list of open sheet windows.
Inherit all of them from an application sheet ancestor and add a custom user event that will be triggered on all open
windows. Something like ue_IdleTimeout(). It will need a return code so the calling script will know when all the
processing is complete.
Instead of using an ancestor application sheet, you could also code this in w_master if you are using a copy of the
PFE layer for your application. Using w_master would allow you to include any open response windows, etc.,
though you have to figure out how get a reference to the non-sheet windows. Some tweaking to the sheetmanager
service might work.
In
the window you declare the event, add the following script:
ib_disableclosequery = True
This is a PFC instance variable that will disable all closequery and pfc_Save processing. Add any other globally
required logic. Then, in the rest of your windows down the inheritance chain, add any other specific logic that is
necessary. Once this event has been triggered on all open windows, close all transactions, and perform a HALT
CLOSE just to make sure something didn't get
missed
Alternative
to pfc's of_BiwiseAnd (). faster than
pfc's. Here is the comparison (50
calls):
this: . 1948 secs
pfc version .7168
sec
//
Function: of_BitWiseAnd (UnsignedLong
aul_value1,UnsignedLong aul_value2)
//
Description: ANDs each bit of the
values. this is an iterative function.
// al_Value1
The first value to be used in the operation.
// al_Value2
The second value to be used in the operation.
//
Returns: UnsignedLong The result of
the AND operation.
//
Author: Lijun Yang Date: 4/8/97
UnsignedLong lul_return = 0
Unsignedlong lul_multiple = 1
// Check for nulls
If IsNull(aul_Value1) Or IsNull(aul_Value2)
Then
SetNull(ll_Result)
Return ll_Result
End If
// Perform the AND
Do
lul_return += mod (aul_value1, 2) * mod (aul_value2, 2) * lul_multiple
aul_value1 = aul_value1 / 2
aul_value2 = aul_value2 / 2
lul_multiple *= 2
Loop Until aul_value1 = 0 AND aul_value2 = 0
RETURN lul_return
Colors in Windows are stored as 4 byte longs, with one byte each representing the Red, Green, and Blue
components and fourth byte usually indicating (alpha) transparency. The structure is: 0xAABBGGRR 0x is
hexadecimal zero, where AA is the alpha (usually 0), BB is the blue component, GG is Green, and RR is red.
0x00 represents no colour,
0xFF saturation You
can easily calculate the colors by the following two methods:
1) Use the Windows Calculator. Set the mode to Scientific (View->Scientific). Select Decimal (Dec) mode.
Enter the number you have, then change to Hex mode.
2)
Use math. R = mod (Colorvalue, 256); G = mod (Colorvalue /256, 256); B = mod
(Colorvalue/65536, 256)
The
16 basic colours are listed in Help/RGB. Other colors of note are:
Buttonface 79741120
WindowText 41943040
WindowBackground 1088479456
Transparent 536870912
CONSTANT long BLACK = 0
CONSTANT long WHITE = 16777215
CONSTANT long LIGHTGREY = 12632256
CONSTANT long DARKGREY = 8421504
CONSTANT long RED = 255
CONSTANT long DARKRED = 128
CONSTANT long GREEN = 65280
CONSTANT long DARKGREEN = 32768
CONSTANT long BLUE = 16711680
CONSTANT long DARKBLUE = 8388608
CONSTANT long MAGENTA = 16711935
CONSTANT long DARKMAGENTA = 8388736
CONSTANT long BROWN = 8388736
CONSTANT long CYAN = 16776960
CONSTANT long YELLOW = 65535
CONSTANT long PALEYELLOW = 1226487
CONSTANT long WINDOWBACKGROUND =
1090519039
CONSTANT long BUTTONFACE = 79741120
CONSTANT long WINDOWTEXT = 33554432
CONSTANT long APPWORKSPACE = 276856960
Values
for the upper color bits
2^25 - Window Text
2^26 - buttonface
2^27 - scrollbar background
2^28 - app. wrokspace
2^29
- transparent
2^30 - Window Background
2^31 - unused
2^32 - unused
string
ls_cmd, ls_arg[]
integer
i, li_argcnt
//
Get arguments and strip blanks from start and end of string
ls_cmd = Trim( commandParm () )
li_argcnt
= 1
DO
WHILE Len(ls_cmd ) > 0
// Find first blank
i =Pos( ls_cmd, " " )
// If no blanks (only one argument),
set i to point to the hypothetical character after end of string
if i =0 then i =Len(ls_cmd) + 1
// Assign the arg to the
arg array.No. of chars copied is one less than position of the space found with
Pos
ls_arg[li_argcnt] =Left( ls_cmd, i - 1
)
// Increment the argument count for
the next loop
li_argcnt = li_argcnt + 1
// Remove the argument from the string so the next argument
becomes first
ls_cmd =Replace( ls_cmd, 1, i,
"" )
LOOP
CONNECTING TO MULTIPLE DATABASES
//
enable transaction registration (the registration service is need when using
more than one database)
gnv_app.of_SetTrRegistration(TRUE)
//
register SQLCA (the connection to the first database which was connect to the
usual way)
gnv_app.inv_trregistration.of_Register(SQLCA)
//
connect to and register ABRN_MRS database (the second database)
//
create 2nd transaction object; n_tr gtr2 must be declared global
gtr2
= CREATE n_tr
//
copy connection info from the first database called SQLCA to the second
database
//
called gtr2 - it copies the user id, password, database name, server name, etc.
from
//
the definition of the first database connection to the definition of the second
database connection
il_return
= SQLCA.of_CopyTo(gtr2)
//
re-assign the database name to the second definition
gtr2.Database
= "your_2nd_database_name"
//
re-assign the server name to the second definition - if it's different
gtr2.ServerName
= "your_2nd_server_name"
//
connect to the second database
ll_return
= gtr2.of_Connect()
If
ll_return <> 0 then MessageBox("Error", "Unable to
connect.")
//
register the second database with the registration service
gnv_app.inv_trregistration.of_Register(gtr2)
Now
whenever you want to retrieve data from the 2nd database, you would write in
the window's open event
li_return = dw_1.of_SetTransObject(gtr2)
dw_1.retrieve()
There is an undocumented way to get to a lot of DDDW / DDLB events. Whenever an event occurs in the
datawindowchild it sends a notification code to the parent DW. This code can be intercepted in the user
event mapped to pbm_command datawindow event. If you want to experiment to determine which events
you can
intercept, try adding the code below to ue_command mapped to pbm_command:
mle_status.text
+= "hwndchild = " + String(hwndchild) + ", " +
"childid = " + String(childid) + ", " + &
"notificationcode
= " + String(notificationcode) + "~r~n"
The
code needed to intercept the dropdown arrow in the ddlb is:
int
li_rc
li_rc
= this.GetChild("ddlbtest", ldwc_ddlb)
IF
childid = Handle(ldwc_ddlb) THEN
CHOOSE CASE notificationcode
CASE 1
Post Event ue_DDLBRowFocusChanged()
END CHOOSE
END
IF
Other
events:
Event ActionID
from WordParm
Clicked 1281
RowFocusChanged 2048,
2049
RightMouseButtonDown 2314
Left Button Up 2313
Retrieve End 769
MouseMove 2311
To use the Drop Down Calendar or the Drop Down Calculator with the editmask style column you will need to use
the
"NONE" style service option.
1.
Create a computed field with a bitmap() expression to the right of the
datawindow column:
BitMap("calendar.bmp")
where "calendar.bmp" may be a small calendar or a drop down arrow. Give the computed field a name;
say "ship_date_arrow".
2.
Enable the DDCalendar service with the "NONE" option:
This.of_SetDropDownCalendar(TRUE)
This.iuo_calendar.of_SetInitialValue(FALSE)
This.iuo_calendar.of_SetAlwaysRedraw(FALSE)
This.iuo_calendar.of_SetCloseOnClick(TRUE)
This.iuo_calendar.of_SetCloseOnDClick(TRUE)
This.iuo_calendar.of_Register("ship_date",iuo_calendar.NONE)
3.
Add code to the DW clicked event to drop the Calendar:
IF dwo.name = "ship_date_arrow"
THEN
IF IsValid(iuo_calendar) THEN
this.SetColumn("ship_date")
this.event pfc_ddcalendar()
END IF
END IF
5.X
It is mandatory to have the deployment kit on your client machine. It doesn't matter if you are building machine code or
p-code executable. In either case you need deployment kit. But you may reduce the number of DLLs to be deployed
depending on what features you used in your application. Following gives the list of DLLs which are optional. For
more information
see Powersoft
Document 4427.
All
Platforms
pbbgr050.dll Business graph engine
pbdwe050.dll DW engine
pbrte050.dll Runtime engine
pbrtf050.dll Runtime functions
pbshr050.dll Stg mgr, dec, print, ...
pbtyp050.dll System object/function definitions
pbitxt50.dll DW
Import text (optional)
Win16,
Win32
pbroi050.dll OLE 2 support
pbtra050.dll Database interface - trace of any database
pbdbl050.dll Database interface - msg handler for pbsyb,
pbmdi, pbnet (optional)
pbdbt050.dll Database interface - msg handler for pbsyc
(optional)
pbdpb050.dll Dist PB - local driver (optional)
pbdse050.dll Distributed PB (optional)
pbidbf50.dll DW Import Dbase (optional)
pbin5050.dll Database interface - Informix 5.0
(optional)
pbmdi050.dll Database interface - MDI (optional)
pbmss050.dll
Database interface - MS SQL Server 6.0 (optional)
pbo71050.dll Database interface - Oracle 7.1 (optional)
pbodb050.dll Database interface - ODBC (optional)
pbor7050.dll Database interface - Oracle 7.0 (optional)
pborc050.dll ORCA
(optional)
pbosc050.dll Dist PB - open server client (optional)
pbrtc050.dll Rich text support (optional)
pbsmi050.dll Dist. PB (optional)
pbsyb050.dll Database interface - Sybase dblib interface
(Microsoft lib linked) (optional)
pbsyc050.dll
Database interface - Sybase ctlib
interface (optional)
pbwsc050.dll Dist PB - winsock client (optional)
Win32
pbwss050.dll Dist PB - winsock server (optional)
pbnpc050.dll Dist PB - named pipe client (optional)
pbnps050.dll Dist PB - named pipe server (optional)
pbo72050.dll Database interface - Oracle 7.2 (optional)
pboss050.dll Dist PB - open server server (optional)
pbsyt050.dll Database interface - Sybase dblib
interface (Sybase lib linked) (optional)
PFCCOM32.DLL
– required for PFC apps
Win16
pboui050.dll OLE 2 user interface
pbvbx050.dll VBX
pbibm050.dll Database interface - IBM (optional)
pbnet050.dll Database interface - net gateway (optional)
And!
-- PFCCOMM.DLL, PFCFLSRV.DLL
6.5
pbdwe60.dll datawindow engine
pbmss60.dll Microsoft direct driver -
replace with
"pbsyt60.dll"
pbrtc60.dll
run time library
pbtra60.dll
database trace library (useful
for live user
debugging!)
pbvm60.dll
main virtual machine
PFCCOM32.DLL
pfc printing library
PFCCOMM.DLL ditto
PFCFLSRV.DLL pfc file services library
PBODB60.DLL
ODBC connection
PBODB60.INI
ditto
Use the FileExists() function, and look for the file named '.' in the desired directory. If the directory exists, it will
contain '.' and '..' as files.
DIRECTORY FILE LIST WITHOUT A
WINDOW
string ls_files[]
window lw_1
listbox llb_1
int li_items, li_i
Open( lw_1 )
lw_1.openUserObject( llb_1 )
llb_1.DirList( sFileSpec,
uFileType )
li_items = llb_1.TotalItems()
For li_i = 1 to li_items
ls_files[ li_i ] = llb_1.Text( li_i )
Next
lw_1.closeUserObject( llb_1 )
Close( lw_1 )
Alt-F4 is disabled if you remove the close option from the system menu for the window. To do this, use this
code in the
open
event of the window where you want to disable Alt-F4:
UInt lUI_menuHandle
lUI_menuHandle = GetSystemMenu( Handle(
this ), FALSE )
DeleteMenu( lUI_menuHandle, 61536, 0 )
You will have to declare the Windows API
calls as Local External Functions of
the window:
FUNCTION uint GetSystemMenu( uint hWnd,
boolean fRevert ) LIBRARY "USER"
FUNCTION uint DeleteMenu( uint hMenu, uint
idItem, uint fuFlags ) LIBRARY "USER"
DOT NOTATION - LIMITATIONS IN
5.0.0x
In 5.0, dot notation is limited to accessing the first 32K rows in a datawindow or datastore. GetItem( ) and SetItem( )
work fine regardless of the number of rows. PowerSoft is aware of this limitation, but no fix is expected until 6.0 due
to the nature of the
underlying bug. It will never be fixed in 5.0.
DW - CHANGING THE PRESENTATION
STYLE
In the exported .srd file search for the word "PROCESSING" - this is the key word that defines the appearance of the
DataWindow. These are processing
property values:
0 (Default) Form, group, query, or tabular
1 Grid
2 Label
3 Graph
4 Crosstab
5 Composite
7 RichText
DW – COLUMN HIGHLIGHTING OF THE
CURRENT ROW
In the datawindow painter, double-click on the column in question and click on the tab mark "Expressions".
Add
this code to the
background.color property
if(currentRow() = getrow(),
rgb(0,255,0), rgb(255,255,255))
or
…
In
the rowfocuschanged event for the
datawindow control use this example:
//
currentrow is a event argument
Modify ("<colname>.background.color='0~tif("
+ String (currentrow) + " = GetRow (),rgb(0,255,0),rgb(255,255,255))
'")
//
OR dot notation:
dw_sheet.object.<colname>.background.color="0~tif
(CurrentRow() = GetRow(),rgb(0,255,0),rgb(255,255,255))"
DW COLUMN – SETTING VALUE OF
COMPUTED COLUMN
You can’t. You can only set the expression. Using this expression, PB calculates and displays the value for all rows.
If you need a blank string column to set a value for each row, you need to use a dummy column in your select
statement like
Select EmployeeID, FirstName,
LastName, ..., space(10) "DummyString" From Employee
This last column "DummyString" becomes a normal column in your DataWindow like other columns EmployeeID etc.
Remember to set it as Non-updatable column in Update properties. Now you can use this column to set different
value for
each row.
DW - REFERENCING COMPUTED FIELDS
When you refer to an
object in a band other than the detail band (usually a computed field) you must
still specify a row number.
Referenced object Row reference
Header, footer, or summary 1
Group header or trailer, Group no., eg :
dw_1.Object.group_one[ 1 ]
If you specify
nothing after the computed field name, you refer to the computed field
dwObject, not the data.
For a computed field
which occurs more than once, you can get all values by specifying buffer or
datasource instead of row number, just as you can for columns.
When the expression
returns an array (because there is no row number), you must assign the result
to an array, even if you know there is only one row in the result.
DW - FINDING COLUMN NAMES AT
RUNTIME
string
sColNames[]
int i
FOR i = 1 TO Integer( dw_1.dwDescribe(
"DataWindow.Column.Count)" )
sColNames[ I ] =
<dwReference>.describe( "#" + string( i ) +
".name" )
NEXT
DW - FINDING CONTROLS AT RUNTIME
Integer li_obj, li_max_objects
string ls_classname
li_max_objects = UpperBound(
windowname.Control )
FOR li_obj = 1 TO li_max_objects
CHOOSE CASE windowname.Control[ li_obj
].TypeOf()
CASE Datawindow!
datawindow l_dw
l_dw = windowname.Control[ li_obj
]
ls_classname = l_dw.ClassName( )
CASE ELSE // other window object types
END CHOOSE
NEXT
DW – PREVENTING FOCUS LOSS
ON TABBING FROM LAST ROW OR COLUMN
(Sandy Barletta[TeamPS] In the loseFocus event for the DW, check to see if focus is on the last column of the last
row. If it is, then insert a new row, do a setcolumn onto the first column, do a setrow and scrolltorow, and POST a
setFocus back to the DW. That should do it. Example:
// DW LoseFocus event
LONG ll_NewRow
IF This.GetRow() =
This.RowCount() AND This.GetColumnName() = 'mylastone' THEN
ll_NewRow = This.InsertRow(0)
This.SetColumn('myfirstone')
This.SetRow(ll_NewRow)
This.ScrollToRow(ll_NewRow)
This.POST SetFocus()
END IF
DW – CURRENTROW() AND GETROW()
What
the datawindow or Infomaker function GetRow()
returns depends on where it is called
Called From Returns
Header First row on page
Group header First row
in group
Detail Row in which
expression occurs
Group trailer Last
row in group
Summary Last row in report or
DataWindow
Footer Last
row on the page
In
contrast, CurrentRow() returns the
no. of the row which currently has focus.
In
PowerScript however, GetRow() returns
the number of the current row. Just to increase the confusion, we also have:
SelectRow( lRow, lBool ) Highlight or
unhighlight lRow in dw
GetSelectedRow( lRow ) Reports
no. of next selected row after lRow
SetRow( lRow ) Move
cursor to lRow in dw but does not
scroll
Replacement for the regular GetRow Function This version fixes the problem where the current row returned by
GetRow can be greater than RowCount This is a bug in PB that is usually encountered when the last row is deleted
and the remaining rows are protected
long ll_row, ll_rowCount, ll_rowTarget
int li_rc
ll_row = this.GetRow()
ll_rowCount = this.RowCount()
if ll_rowCount <= 0 then
return ll_rowCount
else
if ll_row > ll_rowCount then
ll_rowTarget = ll_rowCount
else
if ll_row = 0 then
ll_rowTarget = 1
li_rc = this.ScrollNextRow( )
else
return ll_row
end if
end if
end if
li_rc = this.SetRow(
ll_RowTarget )
// PB BUG
if this.GetRow() <>
ll_rowTarget then this.fu_scrollToRow( ll_rowTarget )
this.fu_scrollToRow(
ll_rowTarget )
return ll_rowTarget
With GRID datawindows, after you have created your initial datawindow, you can't switch columns easily as you can with
other datawindow types. However, you can do this at runtime by clicking on a columnheader, and dragging it to another
position. If you put the datawindow in preview mode, you are also able to do this. When going back to design mode, you'll
see that the changes are reflected to the design as well. Neat and very handy if you know this. Everything you do in preview
mode will be reflected in design mode. You can use this feature for resizing columns, moving columns, even moving
columns underneath eachother.
Create
a group for the DW that groups by the expression Long((GetRow()-1)/N) where N
is the number you want to define your group by. Put the sum in the group footer
as usual.
DW HORIZONTAL LINES EVERY
<N> ROWS
Add
a line to the detail band, as low as possible; stretch it to the maximum width
of your datawindow. Righ click on it and enter the following expression for the
visible attribute:
if ( mod ( getrow(), N ) = 0, 1,
0 )
where
N is the number of rows between the lines.
DW RETRIEVAL ARGUMENT USING
AGGREGATE FUNCTION
In the dw SQL syntax painter, you can specify a subselect in the 'value' portion of the where clause. Right mouse click
on the value portion and choose the Select option. This will open up another SQL Syntax painter where you can put
together another select statement. Choose the employee table, but don't select any columns. Just enter "min(id)"
(without quotes) in the computed columns tab. When you return to the original dw SQL syntax, you'll be set up with a
sql statement that will retrieve
info about the employee with the smallest id.
DW UPDATE ERROR, ROW CHANGED BETWEEN RETRIEVE AND UPDATE
The
'Row Changed between Upd & Ret' error happens because of one the following:
1.
Someone else changed the database row.
2.
You changed the database row.
3.
You changed the update status flags on the record.
4.
You changed a value in the where clause.
If you are running single-user, (1) is out. If you are not calling setItemStatus(), then (3) is not the cause.
Check to see if you might be
deleting the row.
On (4): if your where criteria is to use key and modified values, or key and updateable values, if you change any of those
original values through any other means, when you update the datawindow, it will not find the row. Use the PFC SQL
Spy service (or check the sql statement argument of the sqlpreview event of the datawindow when you are updating it) to
see the SQL statement being sent to the database. This will undoubtably show you what value is causing the row not to
be found and will thus lead you to the place in code
where you are changing the value.
ENTER KEY – MAKE IT EMULATE THE
TAB KEY
To
have the enter key act like a tab key, create a user event to correspond with
the pbm_dwnprocessenter event on a
datawindow.
In that user event, add the following 2 lines:
Send( Handle( this ), 256, 9, Long( 0, 0 ) )
<dwRef>.SetActionCode( 1 )
For
getting the value of environment variables, like PATH:
function long GetEnvironmentVariableA(string
lpszName, ref string lpszValue, long
dwValue) library "kernel32.dll"
You
must pre-allocate the string <lpszValue>. (eg 2k)
//
If you need to set an enviroment variable:
function int SetEnvironmentVariableA(string
lpszName, ref string lpszValue) library "kernel32.dll"
$PBExportHeader$nvo_kernell32.sru
$PBExportComments$Kernell32
API functions
forward
global
type nvo_kernell32 from nonvisualobject
end
type
end
forward
//
THIS MUST BE DONE IN CODE -- STRUCTURE PAINTER DOES NOT SUPPORT IT
type
str_filetime from structure
ulong
ul_LowDateTime
ulong
ul_HighDateTime
end
type
type
str_win32_find_data from structure
unsignedlong fileattributes
str_filetime creationfile
str_filetime lastaccesstime
str_filetime lastwritetime
unsignedlong filesizehigh
unsignedlong filesizelow
unsignedlong reserved0
unsignedlong reserved1
character filename[260]
character altfilename[14]
end
type
global
type nvo_kernell32 from nonvisualobject autoinstantiate
end
type
type
prototypes
FUNCTION
ulong FindFirstFile(ref string lpszSearchFile, ref
STR_WIN32_FIND_DATA
lpffd) LIBRARY "kernel32.dll"
ALIAS FOR "FindFirstFileA"
FUNCTION boolean FindNextFile(ulong hfindfile, ref STR_WIN32_FIND_DATA lpffd) LIBRARY "kernel32.dll"
ALIAS FOR
"FindNextFileA"
end
prototypes
type
variables
//
MAX_PATH constant for file operations
Private
constant long MAX_PATH = 260
end
variables
forward
prototypes
public
function integer of_dirtoarray (string path, ref string lista[])
end
prototypes
public
function integer of_dirtoarray (string path, ref string lista[]);long
lul_handle
str_win32_find_data
str_find
boolean
lb_fin
integer
n
str_find.filename=space(MAX_PATH)
str_find.altfilename=space(14)
n
= 0
lul_handle
= FindFirstFile(path, str_find)
do
n += 1
lista[n] = str_find.filename
lb_fin = FindNextFile(lul_handle,
str_find)
loop
while lb_fin
return
n
end
function
on
nvo_kernell32.create
TriggerEvent(
this, "constructor" )
end
on
on
nvo_kernell32.destroy
TriggerEvent(
this, "destructor" )
end
on
Example:
string
ls_lista[]
integer
i,n, li_fn
nvo_kernell32
inv_kernell
li_fn
= FileOpen("c:\temp\dll.txt", LineMode!, Write!, LockWrite!,
Replace!)
ls_lista[1]
= ''
n
= inv_kernell.of_dirtoarray("c:\windows\system\*.dll", ls_lista)
for
i=1 to n
FileWrite(li_fn, ls_lista[i])
next
FileClose(li_fn)
If you want the amount of used/free memory then use GlobalMemoryStatus(). If you want the amount of used/free disk
space then use GetDiskFreeSpaceA().
GlobalMemoryStatus:
type str_memorystatus from structure
ULong ul_Length; // size of str_memorystatus
ULong ul_MemoryLoad; // percent of memory in use
ULong ul_TotalPhys; // bytes of physical memory
ULong ul_AvailPhys; // free physical memory bytes
ULong ul_TotalPageFile; // bytes of
paging file
ULong ul_AvailPageFile; // free bytes
of paging file
ULong ul_TotalVirtual; // user bytes of address space
ULong ul_AvailVirtual; // free user bytes
end type
Subroutine GlobalMemoryStatus(Ref
str_memorystatus astr_memorystatus) &
Library "KERNEL32.DLL";
GetDiskFreeSpaceA:
Function Boolean GetFreeDiskSpace( &
Ref String as_RootPathName, & // address of root path
Ref ULong
aul_SectorsPerCluster, &
// number of sectors per cluster
Ref ULong aul_BytesPerSector,
& // number of bytes per sector
Ref ULong aul_NumberOfFreeClusters,
& // number of free clusters
Ref ULong aul_TotalNumberOfClusters) & // total number of clusters
Library "KERNEL32.DLL" &
Alias For GetFreeDiskSpaceA;
The calculations are:
Free Disk Space = aul_NumberOfFreeClusters *
aul_SectorsPerCluster * aul_BytesPerSector
Total Disk Space =
aul_TotalNumberOfClusters * aul_SectorsPerCluster * aul_BytesPerSector
Used Disk Space = Total Disk Space - Free Disk Space
GetDiskFreeSpaceA()
works correctly only with hard drives less than 2M. For large disks you should
use
GetDiskFreeSpaceExA ( LPCTSTR
lpDirectoryName,
PULARGE_INTEGER
lpFreeBytesAvailableToCaller,
PULARGE_INTEGER lpTotalNumberOf
Bytes,
PULARGE_INTEGERlpTotalNumberOfFreeBytes )
which
is available only in Windows NT and Windows 95 (OSR 2). Documented in Windows
SDK
//
string f_dec_to_hex ( integer aiNumber), 0 <= n <= 15
//
Converts integer from 0 - 15 into character hex representation
string
sHexChar
Choose
Case aiNumber
Case 10
sHexChar = 'A'
Case 11
sHexChar = 'B'
Case 12
sHexChar = 'C'
Case 13
sHexChar = 'D'
Case 14
sHexChar = 'E'
Case 15
sHexChar = 'F'
Case Else
sHexChar = String(aiNumber)
End
Choose
Return
sHexChar
//
string f_convert_to_hex( long alNumber ), recursive:
//
Recursive function to translate number into hex representation
If
alNumber > 15 Then
Return f_convert_to_hex( alNumber / 16
) + f_dec_to_hex( Mod( alNumber, 16 ) )
Else
Return f_dec_to_hex( alNumber )
End
If
//
string f_convert_char_to_hex( string asString )
//
Converts an input string into a hex character string
string
sResult, sHexChar
integer
i
For
i = 1 to Len( asString )
sHexChar = f_convert_to_hex( Asc( Mid(
asString, i, 1 ) ) )
If Len( sHexChar ) = 1 Then sHexChar =
"0" + sHexChar
sResult = sResult + sHexChar
Next
Return
sResult
LOGIN - GETTING NETWARE LOGIN
NAME
In one of the example pbl's that comes with 5.0 (PBEXAMFE.PBL), there is a user object called u_external_function_win32.
This nvo has a uf_get_logon_name()
function that invokes a call to GetUserNameA, which resides within
ADVAPI32.DLL.
string ls_LocalName
string ls_RemoteName
int i_iX
uint ui_cb
FOR i_iX = 1 to 26
ls_LocalName = char ( 64 + i_iX ) +
":"
ls_RemoteName = space(128)
ui_cb = 127
IF WNetGetConnectionA( ls_LocalName,
ls_RemoteName, ui_cb ) = 0 THEN
This.AddItem ( ls_LocalName +
"~t- " + ls_RemoteName)
ELSE
IF ii_bShowNotConnected THEN
This.AddItem (
ls_LocalName + "~t-not connected" )
END IF
END IF
NEXT
FUNCTION
UINT WNetGetConnectionA( ref string ln, ref string rn, ref uInt cb ) library
"mpr.dll"
//
ue_sendmail
//
mailSession nonvisual object signs on and establishes a MAPI session.
Int
li_Return
String
ls_email_address, ls_attach_name
mailSession
mSes
/*
mailReturnCode. Returns one of the following
values:
mailReturnSuccess!,
mailReturnLoginFailure!
mailReturnInsufficientMemory!
mailReturnTooManySessions!
mailReturnUserAbort!
If any argument's value is NULL,
mailLogon returns NULL.
*/
mailReturnCode
mRet
//
mailMessage obj is a system struct with info about a specific mail message. A
mailMessage object has no events.
mailMessage
mMsg
//
mailFileDescription obj is a system struct with info about attachment file to a
mail message. no events.
mailFileDescription
mAttach
//
The attached file to be saved as a *.psr Format
ls_attach_name
= gnvo_constants.is_Temp_Dir + 'DataWndw.psr'
//
Create a mail session
mSes
= create mailSession
//
Get user to enter email loginid and password
Open(w_email_login)
//
Log on to the session
//mRet
= mSes.mailLogon ("userid","password")
mRet
= mSes.mailLogon ( is_email_loginID , is_email_password)
IF
mRet <> mailReturnSuccess! THEN
//MessageBox ("Mail",
'Logon failed. Make sure the email's
MAPI server is started.')
MessageBox ("Mail",
'Logon failed.')
RETURN
END
IF
//The
datawindow to be saved as ls_attach_name, which is the *.psr file.
li_Return
= tab_1.tabpage_main.dw_10.saveas(ls_attach_name,PSReport!,True)
//Specifies
the content of the message body. (Runtime only.)
mMsg.NoteText
= 'Universal Result Reporting Email, check the attachment:'
//Specifies
the recipient of the current message.
//mMsg.Recipient[1].name
= 'Lastname, Firstname'
Open(
w_email_address )
ls_email_address
= Message.StringParm
mMsg.Recipient[1].name
= ls_email_address
//mMsg.Recipient[1].name
= 'Fred@acme.com'
//Specifies
the type of the attachment file. Values are:mailAttach! (data file)
mAttach.FileType
= mailAttach!
//Specifies
the full path of the attachment file.
mAttach.PathName
= ls_attach_name
//Specifies
the full path of the attachment file.
mAttach.FileName
= ls_attach_name
//
Specifies the position of the attachment file within the message body. (Read
only.)
//
Note: In MS Mail v3.0b, Position=-1 puts attachment at the beginning of the
message.
//
This will place the attachment at the End of the text of the message
mAttach.Position
= len(mMsg.notetext) - 1
//
Specifies the file attachment for the current message.
//
A mailFileDescription array contains information about an attachment file.
mMsg.AttachmentFile[1]
= mAttach
//
Send the mail
mRet
= mSes.mailSend ( mMsg )
IF
mRet <> mailReturnSuccess! THEN
MessageBox("Mail
Send", 'Mail not sent')
RETURN
END
IF
//
End the mail session, breaking connection between the PowerBuilder application
and mail. If the mail
//
app was already running when PowerBuilder began the mail session, it is left in
the same state
mSes.mailLogoff()
//DESTROY
the Mail session that was created with the CREATE statement.
DESTROY
mSes
You
can extend the message object by completing the following steps:
Using
the PFC:
1) Go to your Application Painter
2) Open the Application's 'Properties'
window.
3) Click on the 'Variable Types' tab
4) Change the Message attribute from
'message' to 'n_msg'
5) Add any necessary
functions/instance variables to n_msg.
Not
using the PFC:
1) Create a 'Standard' Non-visual user
object of type 'message'
2) Name it 'n_message' or whatever you
like.
3) Go to your Application Painter
4) Open the Application's 'Properties'
window.
5) Click on the 'Variable Types' tab
6) Change the Message attribute from
'message' to 'n_message'
7) Add any necessary
functions/instance variables to n_message.
These instance variables that you add to the message object become yours to reference whenever you like and you
don't have to worry about PB stepping on their values. All you have to worry about is one of your developers or yourself
stepping on those values.
The message object is very short lived. Since it's global and used by PB as well, what you pass in it is not guaranteed
to be that way for long. For this reason, it is often been suggested to override the open event of a PFC window, grab the
object/value you need from the message object, then call the ancestor (to trigger the pfc_preopen and post the pfc_postopen
events).As for how to how to grab what you want so it's there when you need it later, create a local instance of the object and
copy all the attributes over. The best way to do this is to add a function on your object called something like
of_duplicateSelf(). It should take an argument of the same type as itself by reference and copy everything over eg:
function integer
u_myObject.of_duplicateSelf(reference u_myObject au_myObject)
au_myObject.is_x = this.is_x
au_myObject.ib_y = this.ib_y
au_myObject.ii_z = this.ii_z
return 1
end function
Then,
to use it...
u_myObject lu_temp
u_myObject lu_myObject
lu_temp = message.powerobject // Cast it
to type u_myObject
lu_myObject = create u_myObject
lu_temp.of_duplicateSelf(lu_myObject)
destroy lu_myObject
Finding the mouse position relative to the current window, the frame etc. Create a structure as follows and save as
s_mouselocation
xposition ulong
yposition ulong
Declare
this external function
FUNCTION
boolean GetCursorPos(ref s_mouselocation mousepos) LIBRARY
"User32.dll"
Place
the following code in clicked event script of a window
s_mouselocation lstr_mouseloc // var of structure type
boolean lb_retval
lb_retval = GetCursorPos( lstr_mouseloc )
if lb_retval=true then
MessageBox("Cursor
Position", "X = " + string(lstr_mouseloc.xposition) + " Y =
"&
+ string(lstr_mouseloc.yposition))
MessageBox("Cursor
Position", "X = " +
string(PixelsToUnits(PointerX(),XPixelsToUnits!)) + " Y = "&
+
string(PixelsToUnits(PointerY(),YPixelsToUnits!)))
Else
MessageBox("Error","Error finding the mouse
position.")
End if
For
western-style alphabets ...
1. In your string table, associate a string id
with actual string values for the language.
2. Place the id of the appropriate string in
the tag field for every control and datawindow text object.
3. In the open event of the window, loop through the window's control array. For each control in the control array, load
the string associated with the control's string id that is stored in its tag field and assign it dynamically at run time. For
each datawindow
control, loop through the list of text objects and do the same thing.
You can easily encapsulate this behavior in a generic service object. You can then call a function in the service object
to replace all the strings at runtime.
This way, you do not have to have a separate window for each language. Because translated strings will have varying
length depending on the language, you have to make sure that the text objects are big enough to accomodate the
translation for each supported language. We have found that by making the fields 33% bigger than what is necessary
for the English string, we can safely fit the translated string for
most languages.
16bit:
FUNCTION UINT WNetGetConnection( ref string ln, ref string rn, ref uint cb )
library "user.exe"
32bit:
FUNCTION UINT WNetGetConnectionA( ref string ln, ref string rn, ref uint cb )
library "mpr.dll"
Use
the following script in a listbox userobject, for instance in the constructor
event. The instance variable
ii_bShowNotConnected
lets you also display not connected drive letters. Default, set it to FALSE.
string ls_LocalName
string ls_RemoteName
int i_iX
uint ui_cb
FOR iX = 1 to 26
ls_LocalName = char ( 64 + iX ) + ":"
l_sRemoteName = space(128)
ui_cb = 127
IF WNetGetConnectionA( ls_LocalName, ls_RemoteName, ui_cb ) =
0 THEN
This.AddItem ( ls_LocalName + "~t- " +
ls_RemoteName )
ELSE
IF ii_bShowNotConnected THEN
This.AddItem ( ls_LocalName + "~t- not
connected" )
END IF
END IF
NEXT
PFC DDDW - MULTIPLE ROWS FROM A
LOOKUP
METHOD
1: use ia_returnVal[] as you would for a single row, but keeping appending
values for each selected row.
long ll_row, ll_counter
ll_row =
dw_master.getSelectedRow(0)
DO WHILE ll_row > 0
ll_counter++
inv_selectionAttrib.ia_returnVal[ll_counter] =
dw_master.object.cust_id[ll_row]
ll_counter++
inv_selectionAttrib.ia_returnVal[ll_counter] =
dw_master.object.cust_name[ll_row]
ll_row = dw_master.getSelectedRow(ll_row)
LOOP
METHOD 2: Create a custom non-visual user object. Declare an instance variable of data type Any. Optionally you
can set AutoInstantiate to True. Declare the custom non-visual in your window (as an instance or locally in an event
or window function) Inside a function or an event you can use dot notation to populate the non-visual. Inside
pfc_default event for the window you can issue a CloseWithReturn
(inv_selectdata.ia_data)
Add
a button to the PowerBar as follows:
Right click on PowerBar.
Choose "customize"
Customize dialog box will appear
Select icon from custom palette and drag it
down to current toolbar in the position you want it to appear.
Toolbar Item Command dialog box will appear.
Type --> winhelp
C:\pwrs\Pb5\Sys\pbpfc050.HLP in Command Line <substitute your path to
file>
Type label such as "PFC Help" in
Item Text and Item Microhelp
Click OK
You now have a convenient toolbar button to
bring up PFC help
PFC NUMERIC SERVICE - HEX
EXTENSIONS
Function:
n_cst_numerical.of_bin2hex
Scope:
protected object function
Prototype:
string of_bin2hex (string as_binary);
//
Object: n_cst_numerical
//
Method: of_bin2hex
//
Author: Boris Gasin 9/27/96
//
Arg : as_binary
//
Return: string
//
Desc : converts a 4 bit binary to hex.
used internally by of_hex
as_binary
= Right( "000" + as_binary, 4)
CHOOSE
CASE as_binary
CASE "0000"
return "0"
CASE "0001"
return "1"
CASE "0010"
return "2"
CASE "0011"
return "3"
CASE "0100"
return "4"
CASE "0101"
return "5"
CASE "0110"
return "6"
CASE "0111"
return "7"
CASE "1000"
return "8"
CASE "1001"
return "9"
CASE "1010"
return "A"
CASE "1011"
return "B"
CASE "1100"
return "C"
CASE "1101"
return "D"
CASE "1110"
return "E"
CASE "1111"
return "F"
CASE ELSE
return "?"
END
CHOOSE
Function:
dpt_n_cst_numerical.of_hex
Scope:
public object function
Prototype:
string of_hex (long al_long);
//
Object: n_cst_numerical
//
Method: of_hex
//
Author: B. Gasin
//
Date : 5/21/97
//
Arg : al_long
//
Return: string hex representation
of the decimal number.If any argument's value is NULL, reurns NULL.
// Description: Determines the Hex representation of a Decimal number.
string ls_hex = ""
string ls_binary = ""
string ls_4bit = ""
//Check
parameters
If
IsNull(al_long) or al_long< 0 Then
string ls_null
SetNull(ls_null)
Return ls_null
End
If
If
al_long = 0 Then Return '0'
//
Convert Dec to binary
ls_binary
= of_binary(al_long)
IF
IsNull(ls_binary) THEN return ls_binary
//
Convert binary to hex
DO
// get the first 4 bits from the right
ls_4bit = Right(ls_binary, 4)
// Convert to hex
ls_hex = of_bin2hex(ls_4bit) + ls_hex
// Trim the rest
ls_binary = Left(ls_binary,
Len(ls_binary) - 4)
LOOP
Until ls_binary = ""
Return
ls_hex
There
is a bug in of_getBit() that truncates negative and >32K
values. Here is the fix.
Function:
n_cst_numerical.of_getbit
Scope:
public object function
Prototype:
boolean of_getbit (long al_decimal,
unsignedinteger ai_bit);
//
Object: n_cst_numerical
//
Method: of_getbit
//
Author: B. Gasin
//
Date : 4/7/97
//
Return: boolean
//
Desc : Override the PFC function to
provide support for negative numbers and fix a bug that truncates numbers >
32k
Boolean
lb_null, lb_negative, lb_result
//Check
parameters
If
IsNull(al_decimal) or IsNull(ai_bit) then
SetNull(lb_null)
Return lb_null
End
If
lb_negative
= (Sign(al_decimal) = -1)
IF
lb_negative THEN al_decimal = Abs(al_decimal) - 1
//Assume
ai_bit is nth bit counting right to left with leftmost bit being bit one.
al_decimal is a binary number, base 10 long.
lb_resulkt
= (Int(Mod(al_decimal / 2 ^(ai_bit -
1), 2)) > 0) Then lb_result = True Else lb_result = False
IF
lb_negative THEN lb_result = NOT lb_result
return
lb_result
PFC TRANSACTIONS - OPTIMISTIC
LOCKING, ONE OPINION
Datawindows use optimistic locking, which is to say, they assume it will be unlikely two users will try to update the same
record at the same time, so users can always read the row into the DW, and it will be checked for concurrency at update
time. If someone else has modified the row in the meantime, the DW will automatically notify the user and ask for a
course of action to resolve the situation.
To catch the occasions where multiple users try to update the same row, PB allows developers to choose several update
options, specifying what will be used in the WHERE clause to catch concurrent updates. If you choose "Key Columns",
only the key value(s) will be included in the WHERE clause and you effectively have no concurrency detection. If you select
"Key and Updateable Columns" or "Key and Modified columns", those extra columns will be used to qualify the UPDATE
statement to catch situations where another user has updated the row since it was read into the DW. In many cases "Key
and Modified Columns" is the most desirable since it allow two users to update the same record as long as they don't try
to change the same
column. If you want to prevent
any concurrent updates, choose "Key
and Updateable Columns".
It is almost always a bad idea to use
pessimistic locking such as you
describe in real-life client/server applications. Too much chance for client PC lockups or
someone loading a screen then going for
lunch, preventing anyone else from accessing that that row.
PFC WINDOWS - PLACING CONTROLS
1. Though standard PowerBuilder native controls are displayed on the control palette, they should never be used in a PFC
application. Never mix and match native controls with PFC controls. All controls must be inherited from a corresponding
PFC control. For example, a command button has a corresponding standard visual user object u_cb. PFC controls notify
the parent window every time they get focus. This becomes important when the message router is looking for the last
control that had focus. The only icon that should be used on this palette is the one to select user objects. In a PFC
environment, consider removing native controls in the
window and user object painters.
2. All PFC controls are contained in the pfemain.pbl library. These controls are extensions from the PFC libraries. The
visual
controls are prefaced with a u_, and
the non-visual controls, such as a datastore, are prefaced with a n_.
3. When placing user objects (controls) on your window be sure to select the user object icon on the Painter palette,
not on
the Powerbar.
SET DEFAULT PRINTER FROM WITHIN
PB
This code is limited to 32 bit programming since under 16 bit a system reboot would be required. Under 32 bit the
registry effects the system defaults automatically allowing for dynamic changes at runtime. Remember that the printer
you dynamically change to must already be configured on that system.
Win95
requires an extra step to setting a new default printer.
/******************************************************************
Variable
Explaination:
li_rtn
= Return code to verify if the WIN.INI update succeeded.
ls_default
= String variable that contains the default printer name.
ls_driver
= String variable that holds the printer's driver.
ls_port
= String variable that holds the printer's port.
ls_printer
= String that will contain the concatenated printer's driver and port.
ls_key
= String variable containing the registry path to the printer information.
Object
Explaination:
sle_def
= Single line edit that contains the default printer's name.
sle_def2
= Single line edit that contains the default printer's port and driver.
sle_new
= Single line edit that contains the new printer's name.
sle_new2
= Single line edit that contains the new printer's port and driver.
*******************************************************************/
//Variable
declarations:
int
li_rtn
string
ls_default, ls_driver, ls_port, ls_printer, ls_key
//STEP
1: Get the current default printer name.
RegistryGet("HKEY_LOCAL_MACHINE\Config\0001\System\CurrentControlSet\Control\Print\Printers",
"default", ls_default)
sle_def.text
= ls_default
//STEP
2: Get the default printers driver and port.
ls_key
="HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Print\Printers\"+sle_def.text
RegistryGet(ls_key,
"Printer Driver", ls_driver)
RegistryGet(ls_key,
"Port", ls_port)
sle_def2.text
= ls_driver + "," + ls_port
//STEP
3: Set a new default printer name.
int
li_rtn
li_rtn
=RegistrySet( &
"HKEY_LOCAL_MACHINE\Config\0001\System\CurrentControlSet\Control\Print\Printers",
"default", sle_new.text)
if
li_rtn = 1 then
Messagebox("Setting new printer name",
"Successful")
else
Messagebox("Setting new printer
name", "Failed")
end
if
//STEP
4: Get the new default printer's driver and port.
ls_printer
= sle_new.text
ls_key
="HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Print\Printers\"+ls_printer
RegistryGet(ls_key,
"Printer Driver", ls_driver)
RegistryGet(ls_key,
"Port", ls_port)
sle_new2.text
= ls_driver + "," + ls_port
//STEP
5: Set the new default printer name, driver and port in the WIN.INI file.
ls_printer
= sle_new.text + "," + sle_new2.text
li_rtn
= SetProfileString("c:\windows\win.ini", "Windows",
"device",ls_printer)
if
li_rtn = 1 then
Messagebox("Win.ini Update",
"Successful")
else
Messagebox("Warning", "An
error has occurred")
end if
Windows
NT Code Example:
/*
Set a new default printer name under Windows NT. More complicated.
Windows NT stores printers as "name,driver,port" so we need to:
1. Get a list of printers for the current user with their
driver and port information
2. Convert the names of network printers - Windows Printer
Name "HP4 on SERVER" is actually "\\SERVER\HP4" in the
registry
3. Look up the printer we need - "HP 4Plus on
SERVER"
4. Store the correct name - "\\SERVER\HP 4Plus" -
and driver,port in the registry as the default printer.
*/
integer iReturn, iPrinter,
iPos, iNumPrinters
string sSystemName[], sWindowsName, sDriverPort, sPrinter
sPrinter = sle_1.text
// 1. Get a list of
printers
iReturn = RegistryValues("HKEY_CURRENT_USER\Software\Microsoft\Windows
NT\CurrentVersion\Devices", sSystemName)
if iReturn <> 1 then
MessageBox("Error", "Unable to get list of Windows
Printers.~r~n" + &
"Unable to set " + sPrinter + " as the default printer")
return
end if
iNumPrinters =
upperBound(sSystemName)
for iPrinter = 1 to iNumPrinters
if left(sSystemName[iPrinter], 2) = '\\' then
// network name, convert to Windows name
iPos = Pos(sSystemName[iPrinter], '\', 3)
sWindowsName = Right(sSystemName[iPrinter],
Len(sSystemName[iPrinter]) - iPos) + " on " +&
Mid(sSystemName[iPrinter], 3, iPos - 3)
else
// Windows name, use as is
sWindowsName = sSystemName[iPrinter]
end if
if sWindowsName =
sPrinter then exit
next
if iPrinter >
iNumPrinters then
// Unable to find printer in list
MessageBox("Error", "Printer " + sPrinter + "
could not be found.~r~n" + "Unable to set " + sPrinter +
" as the default printer")
return
end if
iReturn = RegistryGet("HKEY_CURRENT_USER\Software\Microsoft\Windows
NT\CurrentVersion\Devices", &
sSystemName[iPrinter], sDriverPort)
if iReturn <> 1 then
MessageBox("Error", "Unable to get printer
details.~r~n" + "Unable to set " + sPrinter + " as the
default printer")
return
end if
sDriverPort =
sSystemName[iPrinter] + ',' + sDriverPort
iReturn = RegistrySet("HKEY_CURRENT_USER\Software\Microsoft\Windows
NT\CurrentVersion\Windows", "Device", sDriverPort)
if iReturn <> 1 then
MessageBox("Error", "Unable to set " + sPrinter +
" as the default printer")
return
end if
One thing most developers don't realise is that a right mouse button popup menu should be invoked by a right mouse
]button UP event. (See how MS explorer does it) the right mouse button down event should be used to
select/de-select/extend select etc of datawindow rows. Unfortunately PowerBuilder uses the rmbuttondown event
to suppress its internal rmb popup menu.
So
to prevent the automatic RMB menu (comes from PowerBulder not Win95) from
appearing:
Create
an event called rbuttonup and map it to the pbm_dwnrbuttonup event. Use the
following code:
// Display the RMB menu
this.of_RMBPopUp(xpos,ypos,row,dwo)
In
the rbuttondown event use the following code.
// Prevent default popup menu
return 1
Note:
return 0 will cause the internal rmb popup menu to be displayed. (Paul
Blackmore)
Oracle uses the semi-colon as the statement terminator. Submitting a single SQL statement has ONE semi-colon
appended to the end. Creating a trigger/function/procedure contains more than one SQL statement. Each statement
is terminated with the semi-colon. If the SQL used in the Execute Immediate contains one or more carriage return/line
feeds, then Powerbuilder, god knows why, searches for the semi-colon, and only submits that part of the SQL. If there
are no carriage return/line feeds in the SQL, powerbuilder just sends the SQL. If I remove ALL carriage return/line
feeds from the SQL, everything works
fine regardless of the type of SQL. SELECT/UPDATE/CREATE etc.
STORED PROCEDURE WITH OUTPUT PARAMETER
When executing a stored procedure with an OUT parameter, you can FETCH the result:
DECLARE Proc1 FOR GetAddrCity( :lAddressId )
EXECUTE
Proc1 ;
FETCH
lProc INTO :lsCity ;
CLOSE lProc ;
Structure:
str_wsadata
unsignedinteger version
unsignedinteger highversion
character
description[257]
character
systemstatus[129]
nsignedinteger maxsockets
unsignedinteger maxupddg
string
vendorinfo
External
function declarations:
function
int WSAStartup (uint UIVerionrequested, ref str_wsadata lpWSAdata)library
"wsock32.DLL"
function
int WSACleanup() library "wsock32.DLL"
function
int WSAGetLastError() library "wsock32.DLL"
function
int gethostname(ref string name, int namelen) library "wsock32.DLL"
function
string GetHost(string lpszhost,ref blob lpszaddress) library
"pbws32.dll"
PowerScript
code:
String
ls_ipaddress, ls_hostname
Blob{4}
lb_hostadress
Integer
li_version, li_rc
str_wsadata
lstr_wsadata
ls_hostname
= Space(128)
li_version
= 257
If
WSAStartup(li_version, lstr_wsadata) = 0 Then
If GetHostName(ls_hostname,
Len(ls_hostname)) < 0 Then
li_rc = WSAGetLastError()
Else
GetHost(ls_hostname, lb_hostadress)
ls_ipaddress = String(Asc(String(BlobMid(lb_hostadress, 1, 1)))) +
"."
ls_ipaddress +=
String(Asc(String(BlobMid(lb_hostadress, 2, 1)))) + "."
ls_ipaddress +=
String(Asc(String(BlobMid(lb_hostadress, 3, 1)))) + "."
ls_ipaddress +=
String(Asc(String(BlobMid(lb_hostadress, 4, 1))))
li_rc = 0
End If
Else
li_rc = WSAGetLastError()
End
If
WSACleanup()
Activate | Just before the window becomes active |
Clicked | User clicks in an unoccupied area of the window (any area with no visible, enabled control) |
Close | When window is closed. |
CloseQuery | When you remove a window from display (close it) |
Deactivate | When window becomes inactive. |
DoubleClicked | When user double-clicks in an unoccupied area of the window (any area with no visible, enabled object). |
DragDrop | When a dragged control is dropped on the window. |
DragEnter | Dragged control enters the window. |
DragLeave | Dragged control leaves the window. |
DragWithin | Dragged control is within the window. |
Hide | Just before the window is hidden. |
HotLinkAlarm | After a DDE server app has sent (changed) data and the client DDE application has received it. |
Key |
When user presses a key and the insertion point is not in RichTextEdit or DataWindow edit control. |
MouseDown |
When the user presses the left mouse button in an unoccupied area of the window (any area with no visible,enabled object). |
MouseMove | When the pointer is moved within the window. |
MouseUp |
When user releases the left mouse button in an unoccupied window area (any area with no visible, enabled object). |
Open | When a script executes the Open function for a window. |
Other | When a Windows message occurs that is not a PowerBuilder event. |
RButtonDown | When right mouse button is pressed in an unoccupied window area (any area with no visible, enabled object). |
RemoteExec | When a DDE client application has sent a command.See the platform note at the beginning of this section |
RemoteHotLinkStart | When a DDE client application wants to start a hot link. |
RemoteHotLinkStop | When a DDE client application wants to end a hot link. |
RemoteRequest | When a DDE client application requests data. |
RemoteSend | When a DDE client application has sent data.S |
Resize | When the user or a script opens or resizes a window. |
Show | When a script executes the Show function for this window. The event occurs just before the window is displayed. |
SystemKey | When the insertion point is not in a line edit and the user presses ALT or ALT + another key |
Timer | When a specified number of seconds elapses after the Timer function has been called. |
ToolbarMoved | In an MDI frame window, when the user moves the FrameBar or SheetBar |
Put
this function in an ancestor window:
Function:
wf_center()
int iScreenHt, iScreenWid
environment le_env
// Get screen
size from environment
GetEnvironment( le_env )
iScreenHt = PixelsToUnits(
le_env.ScreenHeight, YPixelsToUnits! )
iScreenWid = PixelsToUnits(
le_env.ScreenWidth, XPixelsToUnits! )
//
Centre me!
this.Move( ( iScreenWid - this.Width ) / 2, ( iScreenHt - this.Height ) / 2 )