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

Reply via email to