Hi Przemek!
I know what you did and why. The same will work also in Harbour
if you use exactly the same C compiler and linker. Anyhow it's
really dirty hack and the worse thing you could do.
Believe me do if you would know all internal details of such
hack with all possible problems and how many things might break
final binaries then you never used such method.
The clean method to not create such bindings which later you
are trying to overload at link time is using DYNAMIC declaration
in .prg code but before you will use it you should understand how
it really works.
Please forget about everything what you have done so far and start
with clean mind ;-)
No problem. AFAIK this aproach uses to give good results!
BTW xHarbour users can also read it because most of things below
are also valid for this compiler.
Let's consider on MinGW only. Other C compilers are very similar
but they cannot use .DLL libraries directly and need import libraries
so such library have to be created together with PCODE DLLs using
-implib hbmk2 option.
1. Main application is linked in shared mode with harbour*-dll using
-shared hbmk2 switch. It is suggested method to use PCODE DLL.
In such case everything is trivial and works out of the box
without any hacks and modifications in the code. i.e.:
/*** dllcode.prg ***/
proc MY_PROC()
? PROCNAME() + ": hello !!!"
return
init proc start
? "INIT PROC", PROCNAME()
return
exit proc end
? "EXIT PROC", PROCNAME()
return
/*** maincode.prg ***/
proc main()
? "start"
my_proc()
? "end"
?
return
to create PCODE dll simply use:
hbmk2 -shared -strip -hbdyn dllcode.prg -n -w -es2
and dllcode.dll is generated, if you are using other then MinGW C
compiler then you should add -cflag=-DHB_DYNLIB (to force exporting
public functions inside .prg code - only MinGW supports auto export
feature) and -implib switches (to create import library - only MinGW
supports direct DLL usage by linker),
to compile and link final application use:
hbmk2 -shared -strip -L. -ldllcode maincode.prg -n -w -es2
Please note that both DLL and final code are linked with harbour*.dll
(-shared switch) and to link final application I added -L. and -ldllcode
so linker was able to find dllcode.dll in current directory and
MY_PROC()
function inside.
As you can see all works without any problems and you do not need any
tricks and/or hacks and/or additional code. You do not have to even
touch
original code.
It's possible because linker was able to cleanly resolve all references
at link time. No manual hacks which can only cause unexpected crash.
In this version code inside PCODE DLL can access all Harbour exported
functions (in case of MinGW all public functions when automatic export
feature is enabled).
2. Main application is linked in shared mode with harbour*-dll using
-shared hbmk2 switch but PCODE dll is not linked statically at link
time but rather loaded dynamically by HB_LIBLOAD().
/*** dllcode.prg ***/
DYNAMIC QOUT, PROCNAME // added to inform compiler and linker that
// references to above functions have to be
// resolved dynamically at runtime
proc MY_PROC()
? PROCNAME() + ": hello !!!"
return
init proc start
? "INIT PROC", PROCNAME()
return
exit proc end
? "EXIT PROC", PROCNAME()
return
/*** maincode.prg ***/
DYNAMIC MY_PROC // declare functions from dynamically loaded DLLs
// as DYNAMIC
proc main()
local hLib
? "start"
hLib := hb_libload( "dllcode" )
if empty( hLib )
? "Cannot load library."
else
my_proc()
hb_libfree( hLib )
endif
? "end"
?
return
to create PCODE dll use:
hbmk2 -nohblib -strip -hbdyn -lhbmaindllp dllcode.prg -n -w -es2
please note that I added hbmaindllp which is linked with final PCODE
DLL binaries and used -nohblib to not link any harbour libraries.
With this switch I'm sure that I do not link the same core code twice
with PCODE DLL and with final binaries and if I forget to declare some
functions as DYNAMIC then I will have link time error so I can easy
add missing declarations.
To compile and link final application use:
hbmk2 -shared -strip maincode.prg -n -w -es2
Please note that in this version I haven't added -ldllcode because
now it will be loaded dynamically by hb_libload() and linker does
not have to know about it.
And that's all. You can test maincode.exe to see how it works.
This version needs to declare functions called from external modules
as DYNAMIC to inform compiler and linker that they are bound dynamically
at runtime so it should not create any static references and special
code from hbmaindllp linked with PCODE DLL.
But as you can see it also does not need any link time hacks.
All can be compiled and linked cleanly without even single warning.
If you have big application with a lot of functions in the core
part and you want to use them in PCODE DLLs then you can make your
life easier and automatically generate .ch file with all such
functions using this code in your main application:
#if 1 // uncomment it for tests */
proc main()
genFuncList()
return
#endif
proc genFuncList()
local aFunc, hFile
aFunc := getFuncList()
asort( aFunc )
hFile := fcreate("dynamic.ch")
aeval( aFunc, {|x| fwrite( hFile, "DYNAMIC " + x +
hb_osNewLine() ) } )
fclose( hFile )
return
func getFuncList()
local aFunc, nCount := __dynsCount(), nDst:=0, n
aFunc := array( nCount )
for n := 1 to nCount
if __dynsIsFun( n )
aFunc[ ++nDst ] := __dynsGetName( n )
endif
next
asize( aFunc, nDst )
return aFunc
It generates "dynamic.ch" file with DYNAMIC declaration for all
functions registered in HVM. Then in your .prg files used to create
PCODE DLLs you can simply add:
#include "dynamic.ch"
and you will not have to declare them manually.
3. Main application is linked in static mode with harbour static libraries
and PCODE DLL is linked statically just like in point 1.
Such version needs Harbour static libraries compiled with -DHB_DYNLIB
so if you want to use it then you have to recompile Harbour core code
using:
set HB_USER_CFLAGS=-DHB_DYNLIB
This condition can be eliminated by adding small extenssion to existing
core code and HBMK2.
The test code is the same as in point 1 above.
to create PCODE dll use:
hbmk2 -nohblib -strip -hbdyn -lhbmaindllp -cflag=-DHB_DYNLIB \
dllcode.prg -n -w -es2
Now -cflag=-DHB_DYNLIB is necessary also for MinGW because using
hbmaindll disables auto export feature in MinGW.
To compile and link final application use:
hbmk2 -static -strip -L. -ldllcode maincode.prg -n -w -es2
and that's all. Please only remember to recompile Harbour to
create HVM lib which exports hb_vmExecute() and hb_vmPorcessSymbols()
or the final binaries will not work. You will see only
start
end
messages on the screen.
4. Main application is linked in static mode with harbour static libraries
but PCODE dll is not linked statically at link time but rather loaded
dynamically by HB_LIBLOAD() just like in point 2.
Such version also needs Harbour static libraries compiled with
-DHB_DYNLIB - see point 3 for above.
The test code is the same as in point 2.
to create PCODE dll use:
hbmk2 -nohblib -strip -hbdyn -lhbmaindllp -cflag=-DHB_DYNLIB \
dllcode.prg -n -w -es2
to compile and link final application use:
hbmk2 -static -strip maincode.prg -n -w -es2
and that's all.
Yes, that case seems to fit exactly what I need!
I'll surelly try that.
It seems to work like this using Blinker, and it offered yet the
option to replace or not to replace a doubly defined symbol when
loading the DLL, but we couldn't implement it unfortunatelly.
Forget about link time function overloading. Modern C compilers
can inline code at different levels, i.e. during compilation and
in such case it's not possible to decompile compiled modules,
cleanly extract the code and insert different one in the same
place. In general such operation is forbidden and modern compiler
reports it as link time error.
ok
That is not my case of use - we use harbour self contained
executable + pCode dll.
See versions 3 and 4 in above description.
In xHarbour 0.9 it seems that hb_vmProcessSymbols and hb_vmExecute
were declared inside maindllp.c but just as wrappers to
(VM_DLL_EXECUTE) macro. Is this part of the same metodology you
described?
They are declared in [hb]maindllp in all versions of Harbour and
[x]Harbour but these are only wrappers to real functions. MAINDLLP
is nothing more then manually written import library. It can work
_ONLY_ when corresponding symbols in harbour*.dll or in main EXE file
are exproted so they are visible for GetProcAddress() function.
I see.
If you are using statically linked application then it's necessary
to recompile Harbour using with HB_DYNLIB macro to force exporting
hb_vmProcessSymbols() and hb_vmExecute() functions. You can make it
by setting HB_USER_CFLAGS envvar, i.e.:
set HB_USER_CFLAGS=-DHB_DYNLIB
before compiling harbour code.
As I wrote above it's possible to eliminate this condition by
small modification in HVM library and hbmk2.
ok. I'll do that.
In xHarbour it is __EXPORT__ macro and C_USR envvar though in recent
version some symbols are always exported but it disables auto export
functionality so it does not work in xHarbour MinGW builds.
>We still do not support HRL (libraries for HRB files what in
>some cases can seriously reduce HRB functionality) anyhow HRB
>is portable and always preferred format which has also many
>features which are not available for DLLs.
This HRL support could eliminate the need to use DLLs in my case. Is
it planned to be done? How would it reduce HRB funcionality?
It's possible but if you haven't used HRB so far then it's
big chance that you haven't understood me correctly and you
are asking for sth what you do not know how to use. DLL does
not have such functionality so if you lived without it so far
then I do not understand why you need it to migrate from DLL
to HRB. I think that you should try to use HRB files 1-st.
As you said , I really never used HRB modules and I assumed that HRL files
would be a loadable library of compiled prg modules. Did I assume wrong?
Please note that you can store them anywhere. I.e. in your
own ZIP file or some other compressed file which you can
automatically decompress and load at runtime. I.e. use this
tool to collect all HRB files in current directory into
single mylib.dat file:
proc main()
local aFiles, file
aFiles := directory( "*.hrb" )
for each file in aFiles
file := hb_memoread( file[ 1 ] )
next
hb_memowrit( "mylib.dat", hb_zCompress( hb_serialize( aFiles ) ) )
return
and then load it at runtime using this function:
func loadmylib( cFile )
local cBody, aHRB, hrb
cBody := hb_memoread( cFile )
if !empty( cFile )
aHRB := hb_deserialize( hb_zUncompress( cBody ) )
for each hrb in aHRB
hrb := hb_hrbLoad( hrb )
next
endif
return aHRB
Yes. I think I would need something like that to work with hrb modules.
Please remember that it returns array with handles to HRB files.
When this array is destroyed then all loaded HRB files are automatically
unloaded so you should store it some variable as long as you need them
Then it's enough to assign NIL to this variable to force automatic
unloading. Of course you can extend above code and add some error
checking, password encryption, etc. All of the above can be done
in few lines using Harbour core functions.
Great!
Thank you very very much Przemek!
Best regards
Leandro
_______________________________________________
Harbour mailing list (attachment size limit: 40KB)
Harbour@harbour-project.org
http://lists.harbour-project.org/mailman/listinfo/harbour