PCODE DLL example

758 views
Skip to first unread message

Randy

unread,
Oct 28, 2011, 10:47:32 AM10/28/11
to Harbour Users
Hello all,

Is there a simple "Hello World" example of how to create and use
Harbour PCODE DLLs?

Thanks,
Randy.

Klas Engwall

unread,
Oct 30, 2011, 12:03:12 PM10/30/11
to harbou...@googlegroups.com
Hi Randy,

> Is there a simple "Hello World" example of how to create and use
> Harbour PCODE DLLs?

Here is an example that I used when I started working with dlls in
Harbour for my EDI converter. It loads, calls and unloads two different
dlls over and over again in a loop. I wanted to test replaceable dlls
that did the same thing in slightly different ways for different parties
I was communicating with, so one of my main points in the test was to
use identical function names in each dll.

HB_LibLoad() and HB_LibFree() are used to load and unload the dlls. I
use HB_ExecFromArray() to call the individual functions and a test
function that I call IsLinkedIn() to check that those functions exist.
In my xhb days I used HB_LibLoad() to call the dll functions, but that
function does not exist in Harbour, so I wrote my own as a wrapper for
HB_ExecFromArray(). For sentimental reasons, I guess :-)

There are of course other, simpler ways to do the calling once a dll has
been loaded.

Regards,
Klas

//================================================

// dlltest.prg

function Main()
local pDll, i, j, lIsLinkedIn, nResult, cFunc

for i := 1 to 10 // Run this code a number of times
? "Iteration", i
for j := 1 to 2
// Build the name of the dll we want to load, load it
// and store the pointer in pDll:
pDll := HB_LibLoad( "pcdll" + str( j, 1 ) + ".dll" )
? 'pDll ', pDll
//
//
cFunc := "TestFunc1"
lIsLinkedIn := IsLinkedIn( cFunc )
if lIsLinkedIn
? "Function " + cFunc + " is linked in"
// This displays a message that is stored in the dll:
nResult := HB_LibDo( cFunc )
? 'Result ', nResult
else
? "Function " + cFunc + " is not linked in"
endif
//
//
cFunc := "TestFunc2"
lIsLinkedIn := IsLinkedIn( cFunc )
if lIsLinkedIn
// This displays a message that is built by the dll using
// one part that is stored in the dll and and one part that
// is passed in the second argument:
// nResult := hb_execfromarray( cFunc,;
// { "Calling dll #" + str( j, 1 ) + " from the exe" } )
nResult := HB_LibDo( cFunc,;
"Calling dll #" + str( j, 1 ) + " from the exe" )
? 'Result ', nResult
else
? "Function " + cFunc + " is not linked in"
endif
//
//
? "Back in the EXE"
// Unload the dll when finished and loop to load the
// second dll:
HB_LibFree( pDll )
next
next

? '-----'

// This is run after all dlls have been released:

cFunc := "TestFunc1"
? 'Testing ' + cFunc
lIsLinkedIn := IsLinkedIn( cFunc )
if lIsLinkedIn
? "Function " + cFunc + " is linked in"
nResult := HB_LibDo( cFunc )
? 'Result ', nResult
else
? "Function " + cFunc + " is not linked in"
endif

return NIL

// ----------------------------------

function IsLinkedIn( cFuncName )

local lIsLinkedIn := .F.

cFuncName := alltrim( cFuncName )

if right( cFuncName, 1 ) != ')'
cFuncName += '()'
endif

if type( cFuncName ) == 'UI'
lIsLinkedIn := .T.
endif

return lIsLinkedIn

// ----------------------------------

function HB_LibDo( ... )

local aParams := HB_aParams()
local nParamCount, cParam

nParamCount := len( aParams )
cParam := aParams[ 1 ]

if nParamCount > 1
adel( aParams, 1 )
asize( aParams, nParamCount - 1 )
endif

return hb_execfromarray( cParam, aParams )

//================================================

// pcdll1.prg

function TestFunc1()
? "Inside dll #1"
return 1

function TestFunc2( xAlert )
? "Inside dll #1 ", xAlert
return 2

//================================================

// pcdll2.prg

function TestFunc1()
? "Inside dll #2"
return 3

function TestFunc2( xAlert )
? "Inside dll #2 ", xAlert
return 4

//================================================

#
# dlltest.hbp
#
-shared
-strip
-n
-w2
-es2

# project type
-hbexe

#output file
-odlltest

# source files
dlltest.prg

//================================================

#
# pcdll1.hbp
#
-shared
-nohblib
-strip
-n
-w2
-es2

# project type
-hbdynvm

# output file
-opcdll1

# source files
pcdll1.prg


//================================================

#
# pcdll2.hbp
#
-shared
-nohblib
-strip
-n
-w2
-es2

# project type
-hbdynvm

# output file
-opcdll2

# source files
pcdll2.prg

//================================================

Klas Engwall

unread,
Oct 30, 2011, 7:24:32 PM10/30/11
to harbou...@googlegroups.com
Hi again,

> Is there a simple "Hello World" example of how to create and use
> Harbour PCODE DLLs?

There was one more thing I should have mentioned: If your dlls need any
functions that do not exist in harbour-<nn>.dll (contribs, 3rd party,
your own), then you must REQUEST them in the main prg of the exe and
declare them as DYNAMIC in every prg that goes into a dll. Never link
anything into a dll that you might one day need to link also into the
exe ... or you will be in trouble. And besides, the total size of all
your binaries will be smaller.

You can use the same list of DYNAMICally declared functions in all dll
sources, whether the specific functions are called from that particular
dll or not, because you are just telling the linker not to complain
about not finding them.

And with the little trick below you only have to maintain one header
file with one single list of functions and can use it for both the exe
and the dlls:

Regards,
Klas

// =============================

// dynamic.ch
//
// Header file to replace linking of certain functions into
// pcode dlls with calling them from the main exe instead.
// This file must be #include'd in the prg files that go
// into the dll. APP_MAIN must not be #defined in those prg
// files. Include it also in the main prg file of the main
// exe, #define APP_MAIN before #include'ing "dynamic.ch"
// and then #undef APP_MAIN again after the #include.

#ifdef APP_MAIN
#define KEY_WORD REQUEST
#else
#define KEY_WORD DYNAMIC
#endif

KEY_WORD SOMEFUNCTION
KEY_WORD SOMEOTHERFUNCTION
KEY_WORD YOUGETTHEPICTURE

// =============================

Randy

unread,
Oct 31, 2011, 10:35:31 AM10/31/11
to Harbour Users
Hi Klas,

Thank you very much for your detailed example! I have a couple more
questions that hopefully you can help me with:

1. I first tried this in Harbour v1.0.1 and I got it working but I
noticed that certain Harbour functions that involved code blocks (eg.
AEVAL and ASCAN) could not be found by the DLL code even though I
referenced them using the DYNAMIC command. Is this because the PDCODE
DLL was not completed in v1.0.1 or does the PCODE DLL framework not
support all Harbour functions?

2. In v1.0.1, I needed to include C_USR=-DHB_DYNLIB when I build
Harbour. I am now trying this in v3 (to see if it resolves the
problems I mentioned above) - Do I need to make a change to one of the
make files on the CONFIG\ folder before I build Harbour v3?

Thanks!

Randy.

Klas Engwall

unread,
Oct 31, 2011, 7:15:57 PM10/31/11
to harbou...@googlegroups.com
Hi Randy,

> Thank you very much for your detailed example! I have a couple more
> questions that hopefully you can help me with:
>
> 1. I first tried this in Harbour v1.0.1 and I got it working but I
> noticed that certain Harbour functions that involved code blocks (eg.
> AEVAL and ASCAN) could not be found by the DLL code even though I
> referenced them using the DYNAMIC command. Is this because the PDCODE
> DLL was not completed in v1.0.1 or does the PCODE DLL framework not
> support all Harbour functions?

Ooh, Harbour 1.x i too old for me to say anything about, but I know that
there have been some changes in the years between. I made my first dll
tests with Harbour in mid 2010. At that time a big dynamic.ch with
"everything" in it had to be #include'd, and there was a utility called
genFuncList() to create it. That requirement had been removed when I
returned to dlls a few month later. Now I only have to list functions
from contribs and other libs that are not included in Harbour-<nn>.dll.

> 2. In v1.0.1, I needed to include C_USR=-DHB_DYNLIB when I build
> Harbour. I am now trying this in v3 (to see if it resolves the
> problems I mentioned above) - Do I need to make a change to one of the
> make files on the CONFIG\ folder before I build Harbour v3?

I build Harbour from SVN with no special settings. Everything I need is
taken care of automatically.

I use ASCAN in some places in the main exe (that uses harbour-<nn>.dll)
but not in any of the pcode dlls that the exe loads. So I copied the
{"Tom", "Mary", "Sue"} example from the Clipper Norton Guide to the dll
source I posted yesterday, just to see what happened, and as expected it
ran just fine.

Regards,
Klas

Reply all
Reply to author
Forward
0 new messages