//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
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
//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
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
#require "hbwin"
#require "hbxpp"#include "simpleio.ch"
#include "dll.ch"
#include "hbdyn.ch"
FUNCTION Main ()
--
--
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.
#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
//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"
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
//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"
.. 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?
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?
Regarding a .DLL passing back a string, I don't think it's possible because of memory management issues.
StrToSet = "String To Be Set by DLLxxxxxxxxxxxxxx"
DECLARE fpimport1 IN E:\EY\597harbour\dlltest\dlltest2.dll string cProc, string @ msg
? fpimport1("TESTHB1", @StrToSet)
QUIT
but the window did not close.
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
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
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.
--