Dll's o Lib's externas

519 views
Skip to first unread message

David Field

unread,
Oct 12, 2019, 6:23:06 PM10/12/19
to Harbour Users
Hola,

Usando Harbour tengo que realizar una interfase con un sistema ajeno (Compaq) del cual proveen una LIB que se puede hacer interfase usando VB, C#, C++ y Delphi

Para hacer la interfase se requiere enviar información definida en estructuras eg:
// Definición de estructura de datos de unidad de peso y medida ------------------------------------
typedef struct Unidad
{
  char cNombreUnidad[ kLongNombre + 1 ];
  char cAbreviatura[ kLongAbreviatura + 1 ];
  char cDespliegue[ kLongAbreviatura + 1 ];
} UNIDAD;

typedef UNIDAD FAR * LPFREGUNIDAD;

En manual de xHarbour (yo uso Harbour pero me apoyo en este manual) veo que es posible definir y usar estas estructuras
// Create OSVERSIONINFOEX structure object
oVerInfo := (struct OSVERSIONINFOEX)

He compilado el ejemplo que viene en el manual exitosamente pero no funciona, quiza por que sea algo viejo???

Tampoco he podido usar DLL's, en Harbour al compilar me dice que no existen las funciones GetProcAddress(), CallDll(), LoadLib(), FreeLib(), DLLCall()

He usado DllLoad("Kernel32") y me regresa un pointer y wapi_getprocaddress(nDll,"GetVoumeInformation") y me regresa otro pionter, pero y de ahí, cómo llamo a la funcion?

Alguna idea de cómo puedo realizar este proyecto?

Gracias,
David Field

jparada

unread,
Oct 13, 2019, 3:54:01 PM10/13/19
to Harbour Users
Hola David,
Tienes que bajar de nivel a lenguaje C por el tema de las estructuras de datos, con llamadas a la dll no lo vas a poder resolver, a menos de una forma fácil, además como es algo muy específico, ya te habrás dado cuenta que no hay información al respecto, la verdad es complicado.

Saludos,
Javier

David Field

unread,
Oct 14, 2019, 2:57:50 PM10/14/19
to Harbour Users
Hola Javier,

Gracias por responder, pensé que quizá me pudiera evitar programación en C ya que no lo conozco.

Pudiera alguien recomendarme algún manual para el C que utilizamos con Harbour?

Gracias
David Field

Diego Fazio

unread,
Oct 15, 2019, 7:17:39 AM10/15/19
to Harbour Users
David, para salir del paso(o tal vez para siempre), no pensaste es armarte un .exe en VB y mediante intercambio de archivos linkearlo con Harbour? No se especificamente que hace tu libreria pero no creo que tengas problema. 
Respecto a tu consulta, hay ejemplos como \contrib\hbmisc\tests\calldll.prg . Yo igualmente no haria esto de llamar a la .dll desde hb, sino que crearia una nueva funcion en C para Harbour HB_FUNC( funcion ), trabajaria todo ahi con el ejemplo que calculo debes tener para c++ y enviandole los datos necesarios a la funcion(desde hb) que esta me devuelva los resultados(a hb). Es mejor por el tema del manejo de las estructuras que tiene C. Replicar esto en Harbour no es tan simple. 

Diego.

Appliserver

unread,
Oct 15, 2019, 9:39:20 AM10/15/19
to harbou...@googlegroups.com

On 10/13/2019 12:23 AM, David Field wrote:
> Tampoco he podido usar DLL's, en Harbour al compilar me dice que no
> existen las funciones GetProcAddress(), CallDll(), LoadLib(),
> FreeLib(), DLLCall()
>
#include "xhb.lib" podria solver el problema de estas llamadas.
Dan

jparada

unread,
Oct 15, 2019, 10:38:55 AM10/15/19
to Harbour Users
Hola Diego,
Puedes por favor ampliar el tema de lo que comentas de "linkear" VB con Harbour, o te refieres a crear el exe en VB y hacer un simple hb_run, si es más que esto, por favor puedes dar más detalles.

Gracias.

Saludos,
Javier

Diego Fazio

unread,
Oct 15, 2019, 10:54:43 AM10/15/19
to Harbour Users
Exactamente eso. Haces todo en VB, ejecutas el .exe y mediante archivo de texto por ejemplo intercambias la entrada/salida de datos. De esa manera salis del paso. 
Para darte mas detalle respecto a la manera de implementarlo desde C y tomarlo en HB, tendria que ver mas especificamente la dll.

Diego.

José M. C. Quintas

unread,
Oct 15, 2019, 12:26:41 PM10/15/19
to harbou...@googlegroups.com
WRONG !!!!

xhb.lib is for XHarbour compatibility.

Why change Harbour to work as XHarbour?

It is a lib for conversion, not for all Harbour life !!

Only as a sample of hb_DynCall(), but part of a RMChart class

#include "hbdyn.ch"
   METHOD Init()                           INLINE ::nHandle :=
hb_libLoad( "RMChart.dll" )
   METHOD Destroy()                        INLINE hb_libFree( ::nHandle )
  METHOD CallDllStd( cName, ... )         INLINE hb_DynCall( { cName,
::nHandle, HB_DYN_CALLCONV_STDCALL }, ... )

   METHOD AddBarSeries(a,b,c, ... )        INLINE ::CallDllStd(
"RMC_ADDBARSERIES", a, b, ::ToDouble( c ), ... )
   METHOD AddRegion( ... )                 INLINE ::CallDllStd(
"RMC_ADDREGION", ... )
   METHOD AddCaption( ... )                INLINE ::CallDllStd(
"RMC_ADDCAPTION", ... )

José M. C. Quintas

jparada

unread,
Oct 15, 2019, 1:30:30 PM10/15/19
to Harbour Users
Hi José,
I'm sorry for the OT, have you still your ADO class available?.

I can't find it and I would like to do some tests, can you please provide it to me?.

Regards,
Javier

David Field

unread,
Oct 16, 2019, 7:46:37 PM10/16/19
to Harbour Users
José,

I don´t know if I am doing something wrong or hb_dynaCall is not working.
Here is an example of what I am doing and it return NIL as result.


STATIC s_hDLL := { => }
STATIC s_mutex := hb_mutexCreate()

#define DC_CALL_STD            0x0020
#define MAX_PATH                  260

Procedure Main(...)
    LOCAL cVolumeName := Replicate( CHR(0), MAX_PATH+1 )
    LOCAL nNameSize   := Len( cVolumeName )
    LOCAL nResult, cSerial, cVolume := "C:"

    cSerial := U2Bin(0)

    nResult :=                    ;  // * C prototype *
        CallDLL32( "GetVolumeInformation", "Kernel32.dll", ;
                        cVolume               , ;  //   LPCTSTR lpRootPathName ,
                      @cVolumeName           , ;  //   LPTSTR lpVolumeNameBuffer ,
                        nNameSize             , ;  //   DWORD nVolumeNameSize ,
                      @cSerial               , ;  //   LPDWORD lpVolumeSerialNumber ,
                        0                     , ;  //   LPDWORD lpMaximumComponentLength ,
                        0                     , ;  //   LPDWORD lpFileSystemFlags ,
                        0                     , ;  //   LPTSTR lpFileSystemNameBuffer ,
                        0                       )  //   DWORD nFileSystemNameSize )

    // format serial number as FFFF:FFFF
    cSerial := NumToHex( Bin2U(cSerial), 8 )
    cSerial := Stuff( cSerial, 5, 0, ":" )
? hb_ValToStr(nResult)
? Left( cVolumeName, At( Chr(0), cVolumeName ) - 1 )
UnloadAllDll()
RETURN

#include "hbdyn.ch"

PROCEDURE UnloadAllDll()

   hb_mutexLock( s_mutex )
   s_hDLL := { => }
   hb_mutexUnlock( s_mutex )

   RETURN

FUNCTION CallDll32( cFunction, cLibrary, ... )
   RETURN hb_DynaCall1( cFunction, cLibrary, NIL, ... )

#define __PLATFORM__WINDOWS

#if define( __PLATFORM__WINDOWS )
   /* Use Windows system .dll calling convention on Windows systems,
      like in original lib. Original .lib was a Windows-only solution.
      [vszakats] */
   #define _DEF_CALLCONV_ HB_DYN_CALLCONV_STDCALL
#else
   #define _DEF_CALLCONV_ HB_DYN_CALLCONV_CDECL
#endif

FUNCTION hb_DynaCall1( cFunction, cLibrary, nCount, ... )

   LOCAL aParams
   LOCAL hHandle

   IF HB_ISSTRING( cFunction ) .AND. ;
      HB_ISSTRING( cLibrary )

      hb_mutexLock( s_mutex )

      IF ! cLibrary $ s_hDLL
         s_hDLL[ cLibrary ] := hb_libLoad( cLibrary )
      ENDIF

      hHandle := s_hDLL[ cLibrary ]

      hb_mutexUnlock( s_mutex )

      IF HB_ISNUMERIC( nCount ) .AND. nCount >= 0 .AND. nCount < PCount() - 3
         aParams := ASize( hb_AParams(), nCount )
         RETURN hb_DynCall( { cFunction, hHandle, HB_DYN_CALLCONV_STDCALL }, hb_ArrayToParams( aParams ) )
      ELSE
         RETURN hb_DynCall( { cFunction, hHandle, HB_DYN_CALLCONV_STDCALL }, ... )
      ENDIF
   ENDIF

   RETURN NIL

FUNCTION StrPtr( x )
   RETURN x

FUNCTION PtrStr( x )
   RETURN x

Any ideas what I am doing wrong?

Thanks,
David Field

El martes, 15 de octubre de 2019, 11:26:41 (UTC-5), José M. C. Quintas escribió:

Grigory Filatov

unread,
Oct 17, 2019, 7:40:18 AM10/17/19
to Harbour Users
Hello David,

> Any ideas what I am doing wrong?

Please be so kind to try the following working updated code:


STATIC s_hDLL := { => }
STATIC s_mutex := hb_mutexCreate()

#define DC_CALL_STD            0x0020
#define MAX_PATH                  260

Procedure Main(...)
    LOCAL cVolumeName := Replicate( CHR(0), MAX_PATH+1 )
    LOCAL nNameSize   := Len( cVolumeName )
    LOCAL nResult, cSerial, cVolume := "C:\"

    cSerial := U2Bin(0)

    nResult :=                    ;  // * C prototype *
        CallDLL32( "GetVolumeInformationA", "Kernel32.dll", ;
                        cVolume               , ;  //   LPCTSTR lpRootPathName ,
                      @cVolumeName           , ;  //   LPTSTR lpVolumeNameBuffer ,
                        nNameSize             , ;  //   DWORD nVolumeNameSize ,
                      @cSerial               , ;  //   LPDWORD lpVolumeSerialNumber ,
                        0                     , ;  //   LPDWORD lpMaximumComponentLength ,
                        0                     , ;  //   LPDWORD lpFileSystemFlags ,
                        0                     , ;  //   LPTSTR lpFileSystemNameBuffer ,
                        0                       )  //   DWORD nFileSystemNameSize )

    // format serial number as FFFF:FFFF
    cSerial := NumToHex( Bin2U(cSerial), 8 )
    cSerial := Stuff( cSerial, 5, 0, ":" )
? hb_ValToStr(nResult)
? Left( cVolumeName, At( Chr(0), cVolumeName ) - 1 )
? cSerial
wait
UnloadAllDll()
RETURN

#include "hbdyn.ch"

PROCEDURE UnloadAllDll()

   hb_mutexLock( s_mutex )
   s_hDLL := { => }
   hb_mutexUnlock( s_mutex )

   RETURN

FUNCTION CallDll32( cFunction, cLibrary, ... )
   RETURN hb_DynaCall1( cFunction, cLibrary, NIL, ... )

#define __PLATFORM__WINDOWS

#if define( __PLATFORM__WINDOWS )
   /* Use Windows system .dll calling convention on Windows systems,
      like in original lib. Original .lib was a Windows-only solution.
      [vszakats] */
   #define _DEF_CALLCONV_ HB_DYN_CALLCONV_STDCALL
#else
   #define _DEF_CALLCONV_ HB_DYN_CALLCONV_CDECL
#endif

STATIC FUNCTION hb_DynaCall1( cFunction, cLibrary, nCount, ... )

   LOCAL aParams
   LOCAL hHandle

   IF HB_ISSTRING( cFunction ) .AND. ;
      HB_ISSTRING( cLibrary )

      hb_mutexLock( s_mutex )

      IF ! cLibrary $ s_hDLL
         s_hDLL[ cLibrary ] := hb_libLoad( cLibrary )
      ENDIF

      hHandle := s_hDLL[ cLibrary ]

      hb_mutexUnlock( s_mutex )

      IF HB_ISNUMERIC( nCount ) .AND. nCount >= 0 .AND. nCount < PCount() - 3
         aParams := ASize( hb_AParams(), nCount )
         RETURN hb_DynCall( { cFunction, hHandle, HB_DYN_CALLCONV_STDCALL }, hb_ArrayToParams( aParams ) )
      ELSE
         RETURN hb_DynCall( { cFunction, hHandle,  hb_bitOr( HB_DYN_CALLCONV_STDCALL, HB_DYN_CTYPE_BOOL ) }, ... )
      ENDIF
   ENDIF

   RETURN NIL

Hope that helps.

--
Regards,
Grigory Filatov

David Field

unread,
Oct 18, 2019, 2:28:27 PM10/18/19
to Harbour Users
Hello Grigory,

Thank you for your responce.
I've tried it with your input and also calling hb_dynaCall1 specifying number of parameter and still no luck.

Here are the changes I made to the original prg:
    nResult :=                    ;  // * C prototype *
        hb_DynaCall1( "GetVolumeInformationA", "Kernel32.dll", 8,;

                        cVolume               , ;  //   LPCTSTR lpRootPathName ,
                      @cVolumeName           , ;  //   LPTSTR lpVolumeNameBuffer ,
                        nNameSize             , ;  //   DWORD nVolumeNameSize ,
                      @cSerial               , ;  //   LPDWORD lpVolumeSerialNumber ,
                        0                     , ;  //   LPDWORD lpMaximumComponentLength ,
                        0                     , ;  //   LPDWORD lpFileSystemFlags ,
                        0                     , ;  //   LPTSTR lpFileSystemNameBuffer ,
                        0                       )  //   DWORD nFileSystemNameSize )

and also in hb_Dynacall1
      IF HB_ISNUMERIC( nCount ) .AND. nCount >= 0 .AND. nCount < PCount() - 3
         aParams := ASize( hb_AParams(), nCount )
         RETURN hb_DynCall( { cFunction, hHandle, HB_DYN_CALLCONV_STDCALL }, hb_ArrayToParams( aParams ) )
      ELSE
         RETURN hb_DynCall( { cFunction, hHandle,  hb_bitOr( HB_DYN_CALLCONV_STDCALL, HB_DYN_CTYPE_BOOL ) }, ... )
//            RETURN hb_DynCall( { cFunction, hHandle, HB_DYN_CALLCONV_STDCALL }, ... )
      ENDIF

Thanks,
David Field
Message has been deleted

Pete

unread,
Oct 19, 2019, 4:03:50 AM10/19/19
to Harbour Users
Hi,

On Friday, 18 October 2019 21:28:27 UTC+3, David Field wrote:
.....
I've tried it with your input and also calling hb_dynaCall1 specifying number of parameter and still no luck.

then try the below code. It's plain harbour code (and works).

/*
    mydyn.prg
    compile: hbmk2 mydyn
*/

#include "hbdyn.ch"
#define MAX_PATH 260

PROCEDURE Main()

   #if defined( __PLATFORM__WINDOWS )

   LOCAL hLib, nResult, cVolume, cVolumeName, nNameSize, nSerial, cSerial

    cVolume := "C:\"
    cVolumeName := SPACE(MAX_PATH+1)
    nSerial := 0
    nNameSize := MAX_PATH+1
   
    SetColor( "G+/N" )
    CLS
   
    ? "Get volume info using Kernel32.dll --> GetVolumeInformationA"
    ?
   hLib := hb_libLoad( "Kernel32.dll" )
   
    IF ! Empty( hLib )
   
        ? "Kernel32.dll loaded!"
       
        nResult := hb_DynCall( { "GetVolumeInformationA", hLib, HB_DYN_CALLCONV_STDCALL }, ;
                                cVolume     ,;
                                @cVolumeName, ;
                                nNameSize   , ;
                                @nSerial    , ;
                                0, 0, 0, 0 )

        ? "Calling GetVolumeInformationA: " + Iif( nResult > 0, "Succeeded!", "Failed!" )
       
        // always unload DLL when done!
        ? "Unloading DLL:",  hb_libFree( hLib )
        ?
        cVolumeName := Alltrim( cVolumeName )
        cSerial     := hb_NumToHex( nSerial )
        ? "Volume:", cVolumeName
        ? "Serial:", Stuff( cSerial, 5, 0, "-" )
       
    ELSE
   
        ? "Kernel32.dll failed to load!"
       
    ENDIF
   
    #else
   
        ? "Windows only code! Cannot run on this platform."
       
   #endif

   RETURN

regards,
Pete

Grigory Filatov

unread,
Oct 19, 2019, 12:35:03 PM10/19/19
to Harbour Users
Hello David,

Please take a look for the following updated line also:

    LOCAL nResult, cSerial, cVolume := "C:\"

It should be solution fro your problem.

HTH,
Grigory

пятница, 18 октября 2019 г., 21:28:27 UTC+3 пользователь David Field написал:
Reply all
Reply to author
Forward
0 new messages