Code samples |  Downloads |  Purchase |  Questions & Answers

Code samples

code #1 - Hello world


//declare a Perl interpreter
n_perl perl
//instantiate the interpreter
perl = create n_perl
//importing (integrated) Powerbuilder package with ":all" option (enable syntactic sugar)
perl.use("Powerbuilder", "qw(:all)")
string ls_script = "Messagebox('perl side', 'Hello world', 'exclamation!', 'yesno!')"
//execute Perl script
perl.eval( ls_script )
//close and destroy Perl interpreter
destroy perl
		
code sample
This exemple show how to call a PowerBuilder built-in function.

code #2 - PowerBuilder objects manipulation


//declare a Perl interpreter
n_perl perl
//instantiate the interpreter
perl = create n_perl
constant string CRLF = "~r~n"
string ls_script = &
"use Powerbuilder qw(:all);" + CRLF + &
"$ds=Create('datastore');" + CRLF + &
"$ds->dataobject('dw_helper_color');" + CRLF + &
"return join ',', map{ $ds->GetItemString($_,'name') } grep{ $ds->GetItemNumber($_,'r') > 0 } 1..$ds->Rowcount;"
//execute Perl script
string ls_result
ls_result = perl.eval( ls_script )
if isnull(ls_result) then
	//in case of error, get the last error message
	ls_result = perl.GetLastEvalError( )
end if
//close and destroy Perl interpreter
destroy perl
Messagebox( "Colors that have a positive red component", ls_result )
		
code sample

code #3 - a more advanced example


//declare a Perl interpreter
n_perl perl
//instantiate the interpreter
perl = create n_perl
perl.use("Powerbuilder", "qw(:all)")
//declaring MyPerlSub Perl side subroutine
perl.eval( "sub MyPerlSub{ Messagebox( 'from Perl', join ',',@_); GetApplication() }" )
application app
//calling Perl side subroutine with PB native some datatypes
app = perl.call_sub( "MyPerlSub", "a string", 0.32, -42, perl )
//doing something with returned value
Messagebox("Got from call_sub", app.appname  )
//close and destroy Perl interpreter
destroy perl
	
code sample   code sample

code #4 - writing a PowerBuilder callback


//declare a Perl interpreter
n_perl perl
//instantiate the interpreter
perl = create n_perl
//declare our callback object + event and make it available from Perl-side throught PBCallback alias
perl.register_callback( THIS, "ue_pbcallback", "PBCallback")
perl.eval( "PBCallback( 'a string argument' )" )
//close and destroy Perl interpreter
destroy perl

//and declare the following event
event type string ue_pbcallback(string as_arg);
return string( Messagebox("event ue_pbcallback", "I got as_arg = ~r~n" + as_arg ) )
end event
	
code sample

code #5 - handle Perl script error

To do that, inherit the n_perl object via the menu "File>Inherit", then select the embeddingperl*.pbd library that you have included in your project, make sure the "Objects of Type" field is set to "User Objects" and select the n_perl object.
Open the even on_error script and write the following line of code:

return messagebox( classname(), "On_Error called for:~r~n"+error_text, Exclamation!)
	
Note that the return value is not used and reserved for futur usage, if you want to stop process you may throw an exception/signal error.

And then save as n_cst_perl. Now, write the following Powerscript in your application to test:

//declare a Perl interpreter
n_perl perl
//instantiate the interpreter
perl = create n_perl
//evaluate something in error
perl.eval( "retur n 1+2+3" )
//close and destroy Perl interpreter
destroy perl
	
code sample
Note that you can write previous Powerscript without scripting on_error event,
and call perl.GetLastEvalError( ) which return the last evaluation error recorded.

code #6 - using Perl packages


//declare a Perl interpreter
n_perl perl
//instantiate the interpreter
perl = create n_perl
//prepare ODBC connection a demo database - [ Profile EAS Demo DB V105 Unicode ]
SQLCA.DBMS = "ODBC"
SQLCA.AutoCommit = False
SQLCA.DBParm = "ConnectString='DSN=EAS Demo DB V105 Unicode;UID=dba;PWD=sql'"
CONNECT;
if sqlca.sqlcode <> 0 then
    Messagebox( "Connection", "Failed: " + sqlca.sqlerrtext, StopSign! )
    Halt
end if
//let's connect to the database from Perl and as you can
//see, the connection is Shared between Powerbuilder and Perl
//because we use the 'DBHandle' of the connection.
string ls_rpt
ls_rpt = perl.eval( ' &
BEGIN{                                                      &
    push @INC, "../perl-dist/lib", "../perl-dist/site/lib"; &
}                                                           &
use Powerbuilder qw(:all);                                  &
use Win32::ODBC;                                            &
my $handle = sqlca->DBHandle;                               &
my $db = Win32::ODBC->new($handle)                          &
    or die "DNS link error: " . Win32::ODBC::Error();       &
my $rpt;                                                    &
unless($db->Sql( "select top 5 *                            &
                   from contact                             &
                   order by 1" )){                          &
    while($db->FetchRow()){                                 &
        my %row = $db->DataHash();                          &
        $rpt .= $row{id}.": "                               &
                .$row{last_name}." "                        &
                .$row{first_name}.", "                      &
                .$row{city}."\n";                           &
    }                                                       &
}                                                           &
Messagebox( "Perl report", $rpt );                          &
return $rpt . "; $handle";                                  &
')
if perl.GetLastEvalError() <> "" then
    MessageBox("Powerbuilder", perl.GetLastEvalError() )
end if
//close and destroy Perl interpreter
destroy perl
DISCONNECT;
	
code sample
Note: This sample require to install Perl distribution
+ Win32::ODBC (xlat@cpan.org patch included)

code #7 - using pbperl interpreter

EmbeddingPerl come with pbperlXXX.exe which is a Perl interpreter that is bootstraped in Powerbuilder, so it has the advantage to run perl script from command line but given access to Powerbuilder VM.

 c:\embeddingPerl>pbperl105.exe -e "use Powerbuilder qw(:all); Messagebox('console', 'Hello')" 
or
 c:\embeddingPerl>pbperl105 -MPowerbuilder=:all -e "Messagebox('console', 'Hello')"
	
code sample

An interactive mode is available to the pbperl interpreter, so you can hack and proof:

C:\embeddingPerl>pbperl105 +i
pbperl105.exe - (c) 2011-2012, GeNi
type 'help' to display powerbuilder help
type 'history help' to display history commands
type 'exit' to stop interpreter
>use Powerbuilder qw(:all)
>$env=Create('environment')
Powerbuilder::Object=HASH(0x1539dbc)
>GetEnvironment($env)
1
>join '.', map{ $env->GET($_) } qw( pbmajorrevision pbminorrevision pbfixesrevision pbbuildnumber )
10.5.0.4523
>	

code #8 - output to the console

Powerbuilder is not designed to write into the console, but using by windows API, it is possible. But if you use embeddingPerl, just wrap your output arround an eval('print').

//declare a Perl interpreter
n_perl perl
//instantiate the interpreter
perl = create n_perl
perl.eval('print "hello from Powerbuilder!\n";')
//close and destroy Perl interpreter
destroy perl
	
Compile and run this sample produce no output! Why?
This is because Powerbuilder is designed to produce GUI application only, so it does not prepare its executable to allow to write to the console.
Fortunatly, there is a way to patch our Powerbuilder executables and enable them to write to the console.
We wrote a Perl script that edit the exe file, you find it there.
From now our Powerbuilder application become verbose!

C:\embeddingPerl>codesample105-8.exe

C:\embeddingPerl>pbperl105 enable_console.pl codesample105-8.exe
file codesample105-8.exe  => OK.

C:\embeddingPerl>codesample105-8.exe
hello from Powerbuilder!
	

code #9 - write console unit tests


#codesample105-9.pl
use Test::More tests => 3;
use Powerbuilder qw(:all);
AddToLibraryList('codesample105-9.pbl');
my $lnv_math = Create('n_cst_math_service');
is( ref($lnv_math), 'Powerbuilder::Object', 'create n_cst_math_service' );
is( $lnv_math->Of_add(-11, -12) , -23, 'of_add negative numbers' );
is( $lnv_math->Of_add(1, 1), 1 , 'of_add positive intgers' );
undef $lnv_math;

C:\embeddingPerl>pbperl105 codesample105-9.pl
1..3
ok 1 - create n_cst_math_service
ok 2 - of_add negative numbers
not ok 3 - of_add positive intgers
#   Failed test 'of_add positive intgers'
#   at codesample105-9.pl line 8.
#          got: '2'
#     expected: '1'
# Looks like you failed 1 test of 3.
	
Note: This sample require to install Perl distribution (or at least Test::More package and dependencies)

code #10 - trapping GPF

This sample show how to trap General Protection Fault exceptions that normally terminate your application with no chance to save/clean up your work.
code sample code sample code sample
Here, we will give a bad string pointer to the PowerBuilder String( any, string) built in function.

use Powerbuilder qw(:all);	
Powerbuilder::ResumeCppException(1);
#some PB code that cause a GPF
my $s = String( 0x0123, 'address') or 'resumed!';
#or even
Powerbuilder::Class->new('systemfunctions')->GPF();
print "\$s = $s\n";
	

C:\embeddingPerl>pbperl105 codesample10.pl
C++ Exception Throwned { code: 0xc0000005, address: 0x7c350513, context: Powerbuilder_InvokeClassMid in .\PBNIPerl.cpp at 2039 } at /loader/0x152d374/Powerbuilder/Class.pm line 104.
C++ Exception Throwned { code: 0xc0000005, address: 0x10c9189c, context: Powerbuilder_InvokeClassMid in .\PBNIPerl.cpp at 2039 } at /loader/0x152d374/Powerbuilder/Class.pm line 104.
$s = resumed!
	

Downloads

Full installer  EmbeddingPerl 1.3 (supported powerbuilder version from 10 up to 12.5) ~ 57 Mo
 Time limited, a registration key is required to continue to use the library after august 2012.

Free archive  EmbeddingPerl-Light 1.1 (supported powerbuilder version from 10 up to 12.5) ~ 680 ko
 You may need to download the Microsoft Visual C++ Redistribution package ~ 4 Mo.
 The light version do not provide interop between languages, but allow to evaluate Perl code from Powerbuilder.
The light version provide the same interface than full version in order to simplify migration.

The light version is provided "as is", it is free of charge and can be used in your products.

Changelog

v 1.3 -	11/03/2012
==================
- fixe unload dll issues
- fixe XS+PBVM stack corruptions

v 1.2 -	08/03/2012
==================
- fixe Windows 8 issue

v 1.1 -	07/03/2012
==================
- EmbeddingPerl
	- bug fixes while unloading Storable.dll
	- add missing dll dependencies for perl modules in perl\shared folder such as libexpat.dll and so on
	- add perl/setenv.cmd to set a perl environment console
- Installer 
	- fixes issues while uninstalling Embeddingperl (Installer refactored, so it do not use RMDir /r anymore)
	- splited version of PowerBuilder runtime
	- fixe perl-dist relocation issue (nsPerl could not be loaded)
	- add path to perl/bin and perl/shared in global PATH

v 1.0 - 28/02/2012
==================
- Initial beta version

Purchase

To purchase a registration key for EmbeddingPerl, please contact us by email.

Questions & Answers

Q1: How to start with embeddingperl ?
A1: You can follow this steps:
  1. Install embeddingperl for your powerbuilder version.
  2. Copy embeddingperlXXX.pbx, embeddingperlXXX.pbd and perl510.dll in your powerbuilder project (where XXX stand for 100 for powerbuilder 10.0, 105 for 10.5, etc...).
  3. In your project, edit the library list and add embeddingperl.pbd
  4. In a powerscript, declare and create a n_perl object:
    n_perl ln_perl;
    ln_perl = create n_perl

    and try:
    ln_perl.Eval("use Powerbuilder qw(:all); &
    Sys->Messagebox('hello', 'world', 'exclamation!') ")
Now, you are ready to use embeddingperl in your project!
Q2: What does this code "use Powerbuilder qw(:all);" ?
A2: It declare to the current Perl interpreter every facilities that EmbeddingPerl provide such as dynamic method invocation
Q3: Why Perl, the syntax is really {%$@#*&} ?
A3: Yes, that's called sigils (the variable prefix %, $ or @). Perl was choosed because of the high flexibily of the language. The syntax can look a bit tricky, but it is realy natural when you start to use it, and it make the famous Perl sentense TIMTOWTDI very alive.
Q4: How to represent an enumerated value at Perl side ?
A4: TIMTOWTDI: you can use the integer value or the quoted name with then ending '!' exclamation sign.
Q5: I am lost, what design should I use to script my application ?
A5: It depend on your security requierements, and your own framework. TIMTOWTDI
Q6: How to declare my window so I can use it in my scripts ?
A6: just call in_perl.Define_Variable("PERL_window_name", POWERBUILDER_window_variable) and in your script: $PERL_window_name->Show;
Q7: How to register a callback, so my powerscript could be invoked from script ?
A7: Once again, TIMTOWTDI. You can call in_perl.Register_Callback( obj, "event_name", "perl_alias") or just access the method if you have declared the instance object to your Perl script.
Q8: How to create an object?
A8: my $obj = Powerbuilder::Create("classname"); or if you do use Powerbuilder qw(:all); you can just Create("classname")
Q9: How to call a method ?
A9: $obj->Show
$obj->Of_open('some_file_name.txt')
Q10: How to call an event ?
A10: TIMTOWTDI:
$obj->TriggerEvent("event_name")
$obj->PostEvent("event_name")
$obj->CALL(EVENT,"event_name", @args)
$obj->EVENT("event_name", @args)
Q11: How to call a global function ?
A11: TIMTOWTDI
Glob::Topbobject( Sys->GetApplication ) #require ":all" tags
Powerbuilder::Class->new(topbobject)->Topbobject( Sys->GetApplication )
Q12: How to call Built-ins function such as MessageBox ?
A12: TIMTOWTDI:
Messagebox( 'Title', 'message', 'exclamation!' );
#or if you do not use Powerbuilder qw(:all) (with :all tags):
Sys->Messagebox( 'Title', 'message', 'exclamation!' );
Powerbuilder::Class->new(systemfunctions)->Messagebox( 'Title', 'message', 'exclamation!' )
Q13: How to call an external function (global or local)?
A13: Process exactly the same way as you would call a powerscript method.
Q14: How to access globals variables such as SQLCA, MESSAGE, ... ?
A14:sqlca()->autocommit #or if you do not use Powerbuilder qw(:all) (with :all tags):
Glob::GET(sqlca)->autocommit
Q15: How to access a property / field ?
A15: TIMTOWTDI:
$app->appname; #get appname
$app->appname('new appname'); #set appname
$app->appname = 'new appname'; #Invalid syntax
Q16: How to call a class method ?
A16: In the same maner than builtins.
Q17: How to by pass magic when a property is not accessible?
A17: When a property or method contain an invalid character at Perl side such as #.
For a property, just $obj->GET('a%valid$field#name');
for a method: $obj->CALL('a#scriptname',@args)
Q18: How to post an event ?
A18: use the powerobject method PostEvent.
But it doesn't allow you to specify arguments that are not handled by this method.
Here, a workarround could be to write a global subroutine / custom userobject method:
  (none) MyPostScript(n_perl interp, string scriptname, any args[] )
  interp.POST Call_Sub( scriptname, args[] );
Note: it is a good idea to inherit an object from n_perl so you can attach such helpers to the current interpreter instance directly.
Q19: How to access an INDIRECT property ?
A19: Indirect properties are not accessible in the same way than standard properties or constants. It requiere you to know the methods to call as in the declaration syntax.
Q20: Why my script is so slow ?
A20: In huge loop, dynamic call to methods should be written in another way which is much closed to a static call.
At first precompute the Method ID (MID) of the method you have to call very often:
#This example show how to retrieve MID of "of_add" function with the signature 'LLL' for object $object
#You can retrieve event MID by replacing FUNC with EVENT
$mid = Powerbuilder::Class::GetMid( $object->CLASS->id, 'of_add', FUNC, 'LLL' );

then call the method using this MID:
my $sum = Powerbuilder::Object::InvokeMid( $mid, $object->{pbobject}, $a, $b);
Note that this method will force $a and $b to be casted into a Long datatype because of the useage of MID.
Q21: What that / I got "PBNI ... error" in n_perl ?
A21: In some case, an incremental build or a full build is required to regenerate PBVM internal (such as classid, mid and fid); it happend often when you change an object into a multiple inheritance.
Q22: How to throw a Powerbuilder exception from Perl script ?
A22:$exception_object=Powerbuilder::Create('exception');
$exception_object->SetMessage('Perl exception!');
Powerbuilder::ThrowException( $exception_object );

Note: Perl script is not interrupted by this call as a Powerscript "throw" would do, you may "return" from your scope or do other relevant action.
Q23: How to catch a Powerbuilder exception from Perl script ?
A23: It is only possible if the Perl script was surrounding your Powerscript. Eg:
$obj->Divide( 10, 0); #call a Powerscript that will throw a dividebyzeroerror
if(Powerbuilder::HasException){
	my $ex = Powerbuilder::GetException();
	#You MUST clear the exception BEFORE to access it,
	#otherwhise the exception object will not be usable (seam to be locked).
	Powerbuilder::ClearException(); #remove exception from Powerbuilder stack
	if( Messagebox( 'Exception catched!', $ex->GetMessage() .
		"\n\nDo you want to throw this exception ?" ,  'question!', 'yesno!' ) == 1){
		#We want to let caller to handle the exception.
		Powerbuilder::ThrowException( $ex );
	}
}
Q24: How to catch Perl script exception from Powerscript ?
A24: TIMTOWTDI:
- inherite from n_perl object and write a script for on_error event.
- after each call to n_perl object, call GetLastEvalError().
Q25: Why Upperbound / Lowerbound builtins functions does not work properly?
A25: It is a PBNI matter, it seams that is it not possible to call it from PBNI.
But wait, why not using the Perl way?
Just do
my $len = @an_array; #one dim
my $len2 = @{$another_one[0]}; #sub-array

Valid HTML 4.01 Transitional