Creating a Harbour .DLL to be Called from Other Languages and hb_vmRequestReenter()

1,418 views
Skip to first unread message

Jeff Stone

unread,
Jul 29, 2015, 1:58:12 PM7/29/15
to Harbour Users
I created the following simple .prg to be compiled into a .DLL:

//DLLTEST2.PRG
#include "inkey.ch"
#include "fileio.ch"
#include "hbclass.ch"
#include "common.ch"
function TESTHB
//set alternate to E:\EY\597harbour\dlltest\test1.dat
set alternate to test1.dat
set alternate on
? "Inside testhb()"
close alternate
return NIL
#pragma BEGINDUMP
        #include <windows.h>
        #include <hbvm.h>
//#include "hbvmopt.h"
        #include <hbapi.h>
        #include <hbapiitm.h>
//#include "hbapierr.h"
//#include "hbstack.h"

        BOOL WINAPI DllEntryPoint( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
        {
           HB_SYMBOL_UNUSED( hinstDLL );
           HB_SYMBOL_UNUSED( fdwReason );
           HB_SYMBOL_UNUSED( lpvReserved );
           switch( fdwReason )
           {
              case DLL_PROCESS_ATTACH:
                   hb_vmInit( FALSE );
                   break;
              case DLL_PROCESS_DETACH:
                   hb_vmQuit();
                   break;
           }
           return TRUE;
        }
        HB_EXPORT char * PASCAL _export C_TEST( const char * cProcName, const char * cText1 )
        {
           PHB_ITEM pResult;
           PHB_ITEM pItem1;
           MessageBox( 0, "We entered the DLL and called the xbase function testhb", "1", 0 );
           pItem1 = hb_itemPutC( NULL, "TEST" );
           hb_itemDoC( "TESTHB", 0, (PHB_ITEM *) 0);
           pItem1 = hb_itemPutC( NULL, cText1 );
           hb_itemRelease( pItem1 );
           hb_itemRelease( pResult );
           return NULL;
        }

#pragma ENDDUMP


The execution of TESTHB never occurs from the call:
        hb_itemDoC( "TESTHB", 0, (PHB_ITEM *) 0);

I've traced the issue into the function hb_itemDoc() where
     PHB_DYNS pDynSym = hb_dynsymFindName( szFunc );
successfully finds "TESTHB" and pDynSym is a TRUE value. However, the line:
    if( hb_vmRequestReenter() )
is subsequently evaluating to FALSE. Consequently, hb_vmProc() is never being called.

Is there an error in my .PRG causing hb_vmRequestReenter()  to execute improperly?

Thanks for any help.

Jeff

elch

unread,
Jul 30, 2015, 4:25:14 AM7/30/15
to Harbour Users, jast...@gmail.com
Hi Jeff,

me again, without knowledge about ;-)


..
    if( hb_vmRequestReenter() )
is subsequently evaluating to FALSE. Consequently, hb_vmProc() is never being called.

Is really hb_vmInit() executed, when the DLL is loaded ? -- else above maybe the result.


And maybe this here helps further ?:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms682596(v=vs.85).aspx

https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583(v=vs.85).aspx


best regards

Rolf

Jeff Stone

unread,
Jul 30, 2015, 9:10:43 AM7/30/15
to Harbour Users, spamd...@iesy.net
Hi Rolf,

I think you are right... hb_vmInit() does not seem to be executing!

I gotta figure out why, but you have pointed me in the correct direction.

Thanks,

Jeff


Jeff Stone

unread,
Jul 31, 2015, 10:30:06 AM7/31/15
to Harbour Users, spamd...@iesy.net, jast...@gmail.com
Okay... problem solved.  The DLL entrypoint wasn't getting called because the code example on which I was basing my tests may work for xHarbour and FiveWin with Harbour but won't work for Harbour alone.  The problem was the declaration of the entrypoint.  Originally, I had:
          BOOL WINAPI DllEntryPoint( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
However, after reading "Building a DLL with Visual C++" at http://www.ni.com/white-paper/3056/en/, I realized the declaration needs to be:
          BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )

So, here is the dll prg that works:
//DLLTEST2.PRG
#pragma BEGINDUMP
        #include <stdio.h>
        #include <windows.h>
        #include <hbvm.h>
        #include <hbapi.h>
        #include <hbapiitm.h>
 //BOOL WINAPI DllEntryPoint( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )  !!THIS DOESN'T WORK
 BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )       //This Works!
        {
         FILE *fileptr;
          HB_SYMBOL_UNUSED( hinstDLL );

          HB_SYMBOL_UNUSED( lpvReserved );
          switch( fdwReason )
           {
              case DLL_PROCESS_ATTACH:
                   MessageBox( 0, "We're in DLLMain", "1", 0 );

                   hb_vmInit( FALSE );
                   break;
              case DLL_PROCESS_DETACH:
                   hb_vmQuit();
                   break;
           }
           return TRUE;
        }

        HB_EXPORT char * PASCAL _export C_TEST( const char * cProcName, const char * cText1 )
        {
           PHB_ITEM pResult;
           PHB_ITEM pItem1;
           //hb_dynsymList();  //function I added to dynsym.c to list all callable functions

           pItem1 = hb_itemPutC( NULL, "TEST" );
           hb_itemDoC( "TESTHB", 0, (PHB_ITEM *) 0);
           MessageBox( 0, "We entered the DLL and called the xbase function testhb", "1", 0 );
           MessageBox( 0, cProcName, "1", 0 );
           MessageBox( 0, cText1, "1", 0 );
           hb_itemRelease( pItem1 );
           hb_itemRelease( pResult );
           //return hb_itemGetC( pResult );
           return NULL;
        }
#pragma ENDDUMP
#include "inkey.ch"
#include "fileio.ch"
#include "hbclass.ch"
#include "common.ch"
function TESTHB

set alternate to E:\EY\597harbour\dlltest\test1.dat
//set alternate to test1.dat
set alternate on
?
? "Inside testhb()"
?
close alternate
return NIL


...and here is a working prg to call the DLL for testing:
#require "hbwin"
#require "hbxpp"
#include "simpleio.ch"
#include "dll.ch"
#include "hbdyn.ch"

 FUNCTION Main ()
 Local hDLL, cFarProc
 ? "Before DLL loaded"
 hDLL := hb_libLoad( "DllTest2.dll" )
 IF ! Empty( hDLL )
  ? "DLL has been loaded"
  hb_DynCall( { "C_TEST@8", hDLL, HB_DYN_CALLCONV_STDCALL }, "msg1", "msg2" )
  ? "C_TEST@8 called"
  hb_libFree( hDLL )
 endif
 return nil
                       
I hope this is helpful to others.

Regards,

Jeff

Massimo Belgrano

unread,
Jul 31, 2015, 10:43:04 AM7/31/15
to harbou...@googlegroups.com
Tkanks for share here


--
--
You received this message because you are subscribed to the Google
Groups "Harbour Users" group.
Unsubscribe: harbour-user...@googlegroups.com
Web: http://groups.google.com/group/harbour-users

---
You received this message because you are subscribed to the Google Groups "Harbour Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to harbour-user...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Massimo Belgrano
Delta Informatica S.r.l. (Cliccami per scoprire 

Paul Hemans

unread,
Jul 31, 2015, 7:59:58 PM7/31/15
to Harbour Users
This is really good news.

Jeff Stone

unread,
Aug 3, 2015, 1:11:15 PM8/3/15
to Harbour Users
I have updated the programs further to show how to pass a string variable from the calling .prg and have the .dll modify the value.  I currently have the calling .prg variable getting updated by C code in the .dll rather than Harbour code.  Maybe someone can refine this code to do that?

Calling .prg:
#require "hbwin"
#require "hbxpp"
#include "simpleio.ch"
#include "dll.ch"
#include "hbdyn.ch"

 FUNCTION Main ()
 Local hDLL, strback
 StrToSet = "This is a test - origval"   //Note: I'm guessing that you have to initialize this field so that
                                         //it's length is >= the length of any string the .DLL may set it to!!

 ? "Before DLL loaded"
 hDLL := hb_libLoad( "DllTest2.dll" )
 IF ! Empty( hDLL )
  ? "DLL has been loaded"
  //hb_DynCall( { "C_TEST@8", hDLL, HB_DYN_CALLCONV_STDCALL }, "msg1", "msg2" )
  hb_DynCall( { "fpimport0@4", hDLL, HB_DYN_CALLCONV_STDCALL }, "TESTHB0" )
  ? "fpimport0@4 to TESTHB0 called"
  //hb_DynCall( { "fpimport1@8", hDLL, HB_DYN_CALLCONV_STDCALL }, "TESTHB1", "msg passed to dll" )
  hb_DynCall( { "fpimport1@8", hDLL, HB_DYN_CALLCONV_STDCALL }, "TESTHB1", @StrToSet )  //@ is needed here or field cannot be updated
  ? "fpimport1@8 to TESTHB1 called with StrToSet changed to", StrToSet
  hb_libFree( hDLL )
 endif
 return nil
              


DLL .prg
//DLLTEST2.PRG
#pragma BEGINDUMP
        #include <stdio.h>
        #include <windows.h>
        #include <hbvm.h>
        #include <hbapi.h>
        #include <hbapiitm.h>
 //BOOL WINAPI DllEntryPoint( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )  !!THIS DOESN'T WORK
 BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )       //This Works!
        {
         FILE *fileptr;
          HB_SYMBOL_UNUSED( hinstDLL );

          HB_SYMBOL_UNUSED( lpvReserved );
          switch( fdwReason )
           {
              case DLL_PROCESS_ATTACH:
                   MessageBox( 0, "We're in DLLMain", "1", 0 );

                   hb_vmInit( FALSE );
                   break;
              case DLL_PROCESS_DETACH:
                   hb_vmQuit();
                   break;
           }
           return TRUE;
        }
 HB_EXPORT void PASCAL _export fpimport0(char * cProcName)
 {
  hb_itemDoC (cProcName, 0, 0);
 }
 //Note that the second arg is char * const as we are being passed a pointer that
 //we DON'T want to change but we DO want to allow the value being pointed at the be changed
 HB_EXPORT char * PASCAL _export fpimport1( const char * cProcName, char * const cText1 )
        {
           char * stringoutptr;
           const char testc = "string back";

           PHB_ITEM pResult;
           PHB_ITEM pItem1;
           //hb_dynsymList();  //function I added to dynsym.c to list all callable functions
           pItem1 = hb_itemPutC( NULL, cText1 );
           pResult = hb_itemDoC( cProcName, 1, pItem1);
           MessageBox( 0, "We returned from Call to TESTHB1", "2", 0 );
           stringoutptr = hb_itemGetC( pResult );
           MessageBox( 0, stringoutptr, "3", 0 );
           strcpy(cText1, stringoutptr); //Here is where we a updating the original string field passed to the .dll
                                         //I'm guessing that we cannot increase the string length beyond what it was
                                         //originally
           hb_itemRelease( pItem1 );
           hb_itemRelease( pResult );
           MessageBox( 0, "We returned from Call to TESTHB1 3", "4", 0 );
           hb_retc(stringoutptr);
        }
#pragma ENDDUMP
#include "inkey.ch"
#include "fileio.ch"
#include "hbclass.ch"
#include "common.ch"
function TESTHB0
//creating a txt file to prove we entered Harbour function
set alternate to E:\EY\597harbour\dlltest\test0.dat
set alternate on
?
? "Inside testhb0()"

?
close alternate
return NIL
function TESTHB1(stringin)
//creating a txt file to prove we entered Harbour function
//this time we include the passed string in the txt file
//to prove we received it correctly

set alternate to E:\EY\597harbour\dlltest\test1.dat
set alternate on
?
? "Inside testhb1() with arg ",stringin
?
close alternate
return "Returned Value from TESTHB1"





elch

unread,
Aug 4, 2015, 9:28:16 AM8/4/15
to Harbour Users
Hi Jeff,

beforehand:
how to return [string] values out of a DLL function, or more precise: how to manage therefore allocated RAM, i don't really know.
Hopefully someone can point out a strategy, using global "STATICs" and a seperate function for deallocating looks not as a good approach to me.

---
These 'function attributes' "PASCAL" and "_export" are somehow specific to this decade[s] old BCC.
This will not compile with e.g. MinGW.
There is already used this "HB_EXPORT" macro, which is C-compiler specific translated. ( look for include/hbdefs.h, HB_EXPORT[_ATTR] )
Maybe using that is enough, IMO would be worth a try ...

---
Your DLL.prg does not compile at me, because fpimport1() does not end with a: return ( char * ).
You cannot use here hb_retc(), because that will push a value only onto the Harbour 'virtual machine' [VM] stack.
For sure NOT what you want, as moreover that is done for the VM of the DLL !
All these hb_ret*() are only used for HB_FUNC()s ...

---
I assume you already have build Harbour 3.2 itself from source for your BCC.
Then try creating your DLL with: hbmk2 dll.prg -hbdynvm -trace
[ this "-trace" is for displaying the commands used by hbmk2 ]
That approach should make the build process more easy.
If that does not work, maybe show your formerly used command to create the DLL.
[ sooner or later a change to more modern e.g. MinGW would be advised also by others  ... ]

---
Your new example is a different case as before: calling from a Harbour PRG your DLL.
This IMO should result into running TWO Harbour VMs:
the one for the PRG and one for the DLL -- very ugly ...
Have an urgent view for such case into:
tests/dyn.c and tests/dyn.prg
But you IMO can NOT re-assign the value for the last case [char *] in dyn.c without memory leak ...

best regards
Rolf

Jeff Stone

unread,
Aug 4, 2015, 11:08:05 AM8/4/15
to Harbour Users
Hi Rolf,

Thanks for the feedback.  It's interesting that you cannot compile with MinGW as that's what I compiled both the .DLL and the calling program with.  Below are my compilation commands for the .DLL and the calling program, respectively.  (If it makes any difference, I am using a recent nightly build and related source files.)

C:\hb32\bin\hbmk2.exe -workdir=E:\EY\597harbour\dlltest\temp -hbdynvm -trace -comp=mingw -map dlltest2



call setmingwpath
C
:\hb32\bin\hbmk2.exe -incpath=C:\hb32\contrib\hbxpp -debug -b -std calldlltest2 hbxpp.hbc


I do see the technical error in declaring fpimport1 as returning a (char *).  I failed to clean this up after I realized that I could not pass back a string pointer and was randomly trying the hb_retc(stringoutptr).  So, here is cleaned up .DLL code:

//DLLTEST2.PRG
#pragma BEGINDUMP
        #include <stdio.h>
        #include <windows.h>
        #include <hbvm.h>
        #include <hbapi.h>
        #include <hbapiitm.h>
 //BOOL WINAPI DllEntryPoint( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )  !!THIS DOESN'T WORK
 BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )       //This Works!
        {
         FILE *fileptr;
          HB_SYMBOL_UNUSED( hinstDLL );
          HB_SYMBOL_UNUSED( lpvReserved );
          switch( fdwReason )
           {
              case DLL_PROCESS_ATTACH:
                   MessageBox( 0, "We're in DLLMain", "1", 0 );
                   hb_vmInit( FALSE );
                   break;
              case DLL_PROCESS_DETACH:
                   hb_vmQuit();
                   break;
           }
           return TRUE;
        }
 HB_EXPORT void fpimport0(char * cProcName)
 {
  hb_itemDoC (cProcName, 0, 0);
 }
 //Note that the second arg is char * const as we are being passed a pointer that
 //we DON'T want to change but we DO want to allow the value being pointed at the be changed
 HB_EXPORT void fpimport1( const char * cProcName, char * const cText1 )

        {
           char * stringoutptr;
           const char testc = "string back";
           PHB_ITEM pResult;
           PHB_ITEM pItem1;
           //hb_dynsymList();  //function I added to dynsym.c to list all callable functions
           pItem1 = hb_itemPutC( NULL, cText1 );
           pResult = hb_itemDoC( cProcName, 1, pItem1);
           MessageBox( 0, "We returned from Call to TESTHB1", "2", 0 );
           stringoutptr = hb_itemGetC( pResult );
           MessageBox( 0, stringoutptr, "3", 0 );
           strcpy(cText1, stringoutptr); //Here is where we a updating the original string field passed to the .dll
                                         //I'm guessing that we cannot increase the string length beyond what it was
                                         //originally
           hb_itemRelease( pItem1 );
           hb_itemRelease( pResult );
           MessageBox( 0, "We returned from Call to TESTHB1 3", "4", 0 );
        }
#pragma ENDDUMP
#include "inkey.ch"
#include "fileio.ch"
#include "hbclass.ch"
#include "common.ch"
function TESTHB0
//creating a txt file to prove we entered Harbour function
//set alternate to E:\EY\597harbour\dlltest\test0.dat
set alternate to test0.dat

set alternate on
?
? "Inside testhb0()"
?
close alternate
return NIL
function TESTHB1(stringin)
//creating a txt file to prove we entered Harbour function
//this time we include the passed string in the txt file
//to prove we received it correctly
//set alternate to E:\EY\597harbour\dlltest\test1.dat
set alternate to test1.dat

set alternate on
?
? "Inside testhb1() with arg ",stringin
?
close alternate
return "Returned Value from TESTHB1"

So, does this resolve the Dyn.c memory leak issue?

With respect to there being 2 VMs running, my primary objective was to create a Harbour .DLL that could be called by another language.  To prove that it worked, I created a Harbour calling .prg, which I compiled, so that everyone else could see it work for themselves.  Since the calling program was compiled into a .EXE, does that erase the 2 VM issue?

Regarding a .DLL passing back a string, I don't think it's possible because of memory management issues.  Our Visual FoxPro .DLL could not pass back strings either.  As a matter of practice, the DLL functions never passed back values.  Instead the DLL functions were passed addresses of fields  from C (VFP parameters were called by reference using @) which enabled the DLL to change the contents of the C addresses directly.  This is why string fields must be maximum sized by the calling program before invoking a .DLL function because the .DLL function could otherwise corrupt the memory management of the calling program. 

I really appreciate your reviewing the code as I want to minimize issues as I convert our VFP .DLL to Harbour.

Regards,

Jeff





elch

unread,
Aug 4, 2015, 2:33:12 PM8/4/15
to Harbour Users
Hi Jeff,


.. It's interesting that you cannot compile with MinGW

FYI, for a quick test i used 64bit MinGW 4.8.1 from the TDM-GCC.

Maybe the warning level is different to your version, but it shows me

one warning for: const char testc = "string back"; where a '*' is missing.

and two more warnings, as "testc" and "fileptr" variables are not used.


And i only compiled the DLL part ...

For your PRG that loads the DLL, i must use correct function names, aka "fpimport1" not "fpim...@8",

whereas that '@8' is only some info for the compiler/ linker about the size of the transmitted params [ here two 4-bit pointers in 32bit environment ].


## Does the resulting DLL work for VFP, without that "PASCAL" and _export ?


 
call setmingwpath
C
:\hb32\bin\hbmk2.exe -incpath=C:\hb32\contrib\hbxpp -debug -b -std calldlltest2 hbxpp.hbc

Yes, in hbxpp [ xBase++ contrib ] is also something for DLL usage, but that is lastly only above/ around the basic: hb_dynCall().

I think we should not complex things more than needed ;-)

Needed #define[s] should be fine in "hbdyn.ch" -- see dyn.prg in test dir.


So, does this resolve the Dyn.c memory leak issue?
IMO NO.
First: memory leak test i only can do in Linux with Valgrind, which will show me the line in source.
So not for a DLL test in Windows, there i'm alone on my gut feeling and basic logic.

Sure, we can execute some thousend times and watch about memory increase, but that wouldn't leed us not to the origin of such a problem.


In detail: hb_itemGetC() is allocating RAM, that is not free-ed.

[ check: src/vm/itemapi.c -- that hb_xgrab() there -- the counterpart would be a hb_xfree() ]

To strcpy() more bytes than before allocated, to the starting address in RAM given by the 'char *' -- is the ultra-classic 'buffer overflow'.

At least good for 'unpredictable behaviour', as who can estimate what we overwrite.

Joke: wanting real 'backdoor programming', you should use memcpy() or strncpy() with the size of allocated RAM, as then the trailing 'zero char' will miss .. :-)

No fun: string [ and pointer ] handling at C-level deserves serious attention ! -- and there are some bulky books abouts ...


BTW, neither allocating nor de-allocating RAM cleans [ aka filling with zeros ] the claimed RAM area.

So it is still there after a hb_xfree( stringoutptr ), but not predictable how long -- so after that a: return stringoutptr; is also NO option.

 
.. Since the calling program was compiled into a .EXE, does that erase the 2 VM issue?
As little as i understood of that really complex topic, hb_vmInit() ever starts a new VM.

Regarding a .DLL passing back a string, I don't think it's possible because of memory management issues.
A passed 'string' for 'C' is just a pointer into the RAM where it starts. De-allocating such 'area' another topic.
My idea was about a global to function static pointer variable, (possible free-ed if allready allocated) and assigned/ returned with function call,
-- and free-ed with an seperate functionl later afterwards or when DLL is unloading before hb_vmQuit().
But only that IMO wouldn't be thread safe, you maybe need thread local storage ...

best regards
Rolf

Jeff Stone

unread,
Aug 4, 2015, 5:15:29 PM8/4/15
to Harbour Users
Hi Rolf,

The .DLL does work without "PASCAL" and _export. With fpimport1 is written as: 
    HB_EXPORT void fpimport1( const char * cProcName, char * const cText1 )
the .DLL is showing the function as fpimport1 rather than fpimport1@8.

I was also able to execute the .DLL from VFP using the following lines:
StrToSet = "String To Be Set by DLLxxxxxxxxxxxxxx"
DECLARE fpimport1 IN E
:\EY\597harbour\dlltest\dlltest2.dll  string  cProc, string @ msg
? fpimport1("TESTHB1", @StrToSet)
QUIT


Interestingly enough, the call by VFP to the DLL caused a command window to open

but the window did not close.

I'll have to investigate more.

Regards,

Jeff




elch

unread,
Aug 5, 2015, 2:46:49 AM8/5/15
to Harbour Users
Yes Jeff,


Interestingly enough, the call by VFP to the DLL caused a command window to open

that is a 'good sign' !

as Harbour starts running, when DLL is initially loaded ;-)

and opened default window for output, it should close with hb_vmQuit(), aka DLL unloading.


Creating the DLL with additional:

-gtnul

for the hbmk2 command line magically suppressed that.

[ gtnul is one, or more precise: none, window type linkable for Harbour screen output ]

Does that also work for you ?



---

BTW, you should add a: SET ALTERNATE OFF

just before the: close alternate

This 'alternate' is rarely used by me, just saw it that way in NG docs:

http://x-hacker.org/ng/53guide/ngde71b.html


best regards

Rolf

elch

unread,
Aug 5, 2015, 8:00:51 AM8/5/15
to Harbour Users

Hi Jeff,


made me a bit fun, tried returning a string value by reference, and also the way i mentioned earlier.

Added some validation checks for params and result, to be a bit more on the safe side.

In case of param or else error, a NULL pointer is returned -- looks ok for Harbour PRG, how VFP reacts on that i don't know.

Looks so far memory leak free to me .. ;-)


I am unsure, if you need additional protection for the static var: szResult.

As long as the executable[s] are single threaded, maybe not according to this:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms682594(v=vs.85).aspx

But as you know, Harbour executables can be made working multi threaded, and then we would need something.


So have a look and report, if and how that works for you.

[ To re-enable debug feedback, remove the comment for the #define DEBUG ]


best regards

Rolf

dyndll.zip

Jeff Stone

unread,
Aug 5, 2015, 10:57:35 AM8/5/15
to Harbour Users
Hi Rolf,

Your files compiled and ran cleanly.  I then tried calling fpimport1 from VFP.  It executed fine as well.  Your code is definitely more error proof than mine with the tests of the the string length.

Interestingly enough, I retested my prior .dll version in VFP again.  After executing fpimport1, I then issued the CLEAR DLLs command.  The popup command window that was created by calling the .DLL did not close.  I then compiled jeff_dll with DEBUG defined and called the fpimport1 routine from the jeff_dll in VFP.  In this instance, no command window popped up. I'm not sure why one pops up in my version and why it doesn't close when CLEAR DLLs is issued in VFP.

Anyway, nice refinements to the code.  The return by reference approach passes the burden of checking the DLL returned string length to the calling program which may be preferable in some instances.  Another option for updating the string would be to have TESTHB1 do it by having its argument passed by reference. However, then TESTHB1 would need to perform the length check.  There's no getting around the issue when having a .DLL updating a variable length field such as a string.

My next steps in building an Harbour .dll to replace our VFP .DLL will be to figure out if the Harbour .DLL can contain its functions as part of Class as VFP requires us to do if we want to register the .DLL.  I seem to recollect reading someone's post awhile ago that a Harbour .DLL cannot be registered.  If this is the case, then there is no need for a Class/Method framing of the .DLL.

Regards,

Jeff

ks.l...@gmail.com

unread,
Apr 12, 2017, 3:00:53 AM4/12/17
to Harbour Users
Hello folks,

thank you very much for that example.
i could really use it.

I call the HB DLL function from a Delphi/Pascal application, its working,
BUT i have to copy the libgcc_s_dw2-1.dll and libwinpthread-1.dll
to the HB DLL(jeff_dll.dll) Directory .
if i dont, i can not load the Lib (LoadLibrary(...))
I build the DLL with MinGW 4.8.2
Any Idea how i can build the dll, so i dont have to include those both files ?
best regards 
Karsten

Don Lowenstein

unread,
Apr 12, 2017, 10:46:14 AM4/12/17
to harbou...@googlegroups.com

If you are running in a Windows environment, put their .dll’s residing folder in your path statement.

 

Or, You might include the fully qualified .dll filepath / filename in the request to load the .DLL.

--

Reply all
Reply to author
Forward
0 new messages