Jump to content
Fivewin Brasil

Criação de DLL com código xBase para uso com Harbour.


rochinha

Recommended Posts

Amiguinhos,


Aquele velho sonho de conseguir executar código xBase contido em uma .DLL compilada com Harbour se concretizou para mim.


Noites sem dormir e muita pesquisa, testes, compilações, chingamentos e sem sapeca-iá-iá, mas consegui.


Meus primeiros testes foram com exemplos existentes em todo lugar, Harbour, xHarbour, Fivewin, etc.


Busquei informações de como o RunDLL32 do Windows trabalhava e fiz minhas tentativas.


Num primeiro momento consegui fazer um EXE executar uma função em uma DLL mas só executava a primeira função que encontrava. Bom já era um começo, mas ao retornar ao EXE paulava.


Tentei com RunDLL32 chamado do EXE e não enfrentava mais este problema, mas tinha uma demora de uns segundos e não era o que eu queria.


Fiz meu próprio RunDLL32 mas ainda tinha de executá-lo indiretamente.


Bom enfim, cheguei onde queria e o primeiro passo para isto foi compilando o código de minha DLL:

rochadll.prg


/*
* Jose Carlos da Rocha
* Trabalho com DLL de codigo xBase para uso com Harbour
* Sao Paulo - 09/09/2014
* Baseados nos exemplos BabuDLL e outros
*/
#include "fivewin.ch"

#pragma BEGINDUMP
#include <windows.h>
#include <hbvm.h>
#include <hbapiitm.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;
}

void pascal __export HBDLLENTRY( char * cProcName )
{
hb_itemDoC( cProcName, 0, 0 );
return 0;
}

void pascal __export CusTstBrw()
{
hb_itemDoC( "CusTstBrw", 0 );
}

void pascal __export HBDLLENTRY2( char * cProcName, PHB_ITEM pParam1, PHB_ITEM pParam2 )
{
hb_itemDoC( cProcName, 2, pParam1, pParam2 );
}
#pragma ENDDUMP

/*
* MENUITEM "&Clientes..." ACTION HbDllEntry( "Customer" ) MESSAGE "Manutencao de Clientes"
* Esta funcao foi chamada atraves de parametro da funcao exportavel hbDLLEntry()
* Chamada indireta, pois a funcao abaixo passa por outra funcao para agir
* Aqui o exemplo CUSTOMER foi imputado na DLL para demonstrar que trechos grandes de codigo
* podem residir dentro de uma DLL e serem chamados a partir de um EXE externo.
*/
#include "Customer.ch"

function Customer()
local oWnd, oBar
local oClients, oClient
//local oName, cName

SET _3DLOOK ON

USE Customer SHARED NEW ALIAS Clients
USE Sales SHARED NEW
SELECT Clients

DEFINE WINDOW oWnd TITLE "Reporting tools" MDI ;
MENU BuildMenu(oClients) COLOR "N/W"

DEFINE BUTTONBAR oBar OF oWnd SIZE 60, 60 2007

DEFINE BUTTON OF oBar ACTION MsgInfo( "Click" ) ;
FILENAME "..\bitmaps\attach.bmp" PROMPT "Attach"

DEFINE BUTTON OF oBar ACTION MsgInfo( "Click" ) ;
FILENAME "..\bitmaps\calendar.bmp" PROMPT "Calendar"

DEFINE BUTTON OF oBar ACTION MsgInfo( "Click" ) ;
FILENAME "..\bitmaps\people2.bmp" PROMPT "Clients"

DEFINE BUTTON OF oBar ACTION MsgInfo( "Click" )

SET MESSAGE OF oWnd TO "Testing the FiveWin Report Class" CENTERED

ACTIVATE WINDOW oWnd

CLOSE DATABASES

return nil

function BuildMenu(oClients)
local oMenu
MENU oMenu
MENUITEM "&DataBases"
MENU
MENUITEM "&Clients..." ACTION BrwClients(oClients) ;
MESSAGE "Clients management"
MENUITEM "&Report..." ACTION GenReport()
SEPARATOR
MENUITEM "&End" ACTION oWnd:End() ;
MESSAGE "End this test"
ENDMENU
oMenu:AddMdi() // Add standard MDI menu options
ENDMENU
return oMenu

function BrwClients(oClients)
local oBrw, oIco, oBarBrw
if oClients != nil
return nil
endif
DEFINE ICON oIco FILENAME "..\icons\customer.ico"
DEFINE WINDOW oClients TITLE "Clients management" ;
MDICHILD ICON oIco
DEFINE BUTTONBAR oBarBrw OF oClients
DEFINE BUTTON OF oBarBrw ACTION ShowClient(oClients)
@ 2, 0 LISTBOX oBrw FIELDS OF oClients ;
SIZE 500, 500 // ON CHANGE ChangeClient(oClients)
oClients:SetControl( oBrw )
ACTIVATE WINDOW oClients ;
VALID( oClients := nil, .t. ) // We destroy the object
return nil

function GenReport()
local oWnd, oIco
DEFINE ICON oIco FILENAME "..\icons\print.ico"
DEFINE WINDOW oWnd MDICHILD TITLE "Clients report" ;
VSCROLL HSCROLL ICON oIco
ACTIVATE WINDOW oWnd
return nil

function ShowClient(oClients)
local oIco, oClient
local oName, cName
if oClient != nil
return nil
endif
DEFINE ICON oIco FILENAME "..\icons\Person.ico"
DEFINE DIALOG oClient RESOURCE "Client" ;
ICON oIco TITLE "Detalhes"
REDEFINE SAY ID 3 OF oClient // To get the proper color
REDEFINE SAY ID 4 OF oClient
REDEFINE SAY ID 5 OF oClient
REDEFINE GET oName VAR cName ID ID_NAME OF oClient
REDEFINE BUTTON ID ID_NEXT OF oClient ACTION GoNext(oClients,oName)
SELECT Sales // We select Sales to properly initialize the Browse
REDEFINE LISTBOX FIELDS ID ID_SALES OF oClient
ACTIVATE DIALOG oClient CENTERED NOWAIT ;
VALID ( oClient := nil, .t. ) // Destroy the object
SELECT Clients
return nil

function ChangeClient(oClients,oName)
if oClients != nil
cName = AllTrim( Clients->Last ) + ", " + Clients->First
oName:Refresh()
endif
return nil

function GoNext(oClients,oName)
if oClients != nil
oClients:oControl:GoDown()
else
SKIP
if EoF()
GO BOTTOM
endif
endif
ChangeClient(oClients,oName)
return nil

/*
* MENUITEM "&Browse..." ACTION HbDllEntry( "CusTeste" ) MESSAGE "Browse de Clientes"
* Esta funcao foi chamada atraves de parametro da funcao exportavel hbDLLEntry()
* Chamada indireta, pois a funcao abaixo passa por outra funcao para agir
*/
function CusTeste()
USE Customer SHARED NEW ALIAS Clients
Browse()
CLOSE DATABASES
return .t.

/*
* MENUITEM "&Customer..." ACTION CusTstBrw() MESSAGE "Browse de Clientes"
* Esta funcao foi chamada dentro da DLL usando o proprio nome ao inves de
* usar hbDLLEntry(), desta forma ficou mais legal
*/
function CusTstBrw()
USE Sales SHARED NEW
Browse()
CLOSE DATABASES
return .t.


Dentro do código da DLL existem aplicativos que serão chamados pela aplicação principal, Customer(), CusTeste() e CusTstBrw().


Atentem para o seguinte trecho:


#pragma BEGINDUMP
...
void pascal __export HBDLLENTRY( char * cProcName )
...
void pascal __export CusTstBrw()
...
#pragma ENDDUMP


Vejam que HBDLLENTRY e CusTstBrw são exportadas, ou seja, são visíveis as chamadas à DLL.


Então como compilar este .PRG e transformá-lo em uma .DLL?


Eu usei o bom-e-velho BUILDH.BAT com algumas alterações, vejam os trechos que foram modificados:


%hdir%\bin\harbour %1 /n /i%fwh%\include;%hdir%\include /w0 /p %3 /d__HARBOUR__ > comp.log
IF ERRORLEVEL 1 GOTO COMPILEERRORS
@type comp.log

echo -O2 -e%1.exe -I%hdir%\include;%bcdir%\include %1.c > b32.bc
%bcdir%\bin\bcc32 -M -c @b32.bc
copy %bcdir%\lib\uuid.lib
:ENDCOMPILE

IF EXIST %1.rc %bcdir%\bin\brc32 -r -I%bcdir%\include %1
rem IF EXIST %1.rc %vcdir%\bin\rc -r -d__FLAT__ %1

echo %bcdir%\lib\c0d32.obj + > b32.bc


Quem usa este .BAT dirá, mas não tem nada de diferente neste trecho, mas tem sim e está na última linha, onde se vê echo %bcdir%\lib\c0d32.obj + > b32.bc


Quando compilamos nossos programas para gerar executáveis o arquivo c0w32.obj é o usado, mas neste caso usaremos o c0d32.obj.


Outra linha alterada no nosso BUILDH.BAT é a linha de chamada do iLink32.exe


if %GT% == gtgui %bcdir%\bin\ilink32 -Gn -aa -s -Tpd @b32.bc


No lugar de -Gn -aa -s -Tpe eu alterei para -Gn -aa -s -Tpd, onde e é para EXE e d é para DLL.


Bom, depois de gerada a DLL é possivel testá-la sem precisar do uso do aplicativo principal, bastando usar para isto o RunDLL32.exe do Windows, executando um comando simples:


%windir%\System32\RunDLL32 rochadll.dll,CusTstBrw


Leve em consideração que os testes foram feitos dentro da pasta SAMPLES do Fivewin, portanto as tabelas CUSTOMER.DBF e SALES.DBF serao usadas.


Se a DLL foi bem gerada um browse aparecerá mostrando os registros da tabela.


Agora vamos a parte do aplicativo principal. Este poderá ser compilado normalmente usando o BUILDH.BAT:

rocha.prg


/*
* Jose Carlos da Rocha
* Trabalho com DLL de codigo xBase para uso com Harbour
* Sao Paulo - 09/09/2014
* Baseados nos exemplos BabuDLL e outros
*/
#include "FiveWin.ch"

FUNCTION Main()

local oWndMain, oBarMain

DEFINE WINDOW oWndMain TITLE "Janela dentro do EXE" MDI MENU BuildMenuMain() COLOR "N/W"

DEFINE BUTTONBAR oBarMain OF oWndMain SIZE 60, 60 2007

DEFINE BUTTON OF oBarMain ACTION WinExec( "RunDLL32.exe rochadll.dll,CusTstBrw" )
DEFINE BUTTON OF oBarMain ACTION UseDLL( "CusTstBrw", "rochadll.dll" )

SET MESSAGE OF oWndMain TO "Testing the FiveWin DLLs" CENTERED

ACTIVATE WINDOW oWndMain MAXIMIZED VALID MsgYesNo( "Quer sair?" )

RETURN nil

FUNCTION BuildMenuMain()
local oMenu
MENU oMenu
MENUITEM "Administracao"
MENU
MENUITEM "&Clientes..." ACTION HbDllEntry( "Customer" ) MESSAGE "Manutencao de Clientes"
MENUITEM "&Browse..." ACTION HbDllEntry( "CusTeste" ) MESSAGE "Browse de Clientes"
MENUITEM "&Customer..." ACTION CusTstBrw() MESSAGE "Browse de Clientes"
SEPARATOR
MENUITEM "&Sair" ACTION oWnd:End() ;
MESSAGE "Sair do sistema"
ENDMENU
oMenu:AddMdi() // Add standard MDI menu options
ENDMENU
return oMenu

FUNCTION UseDLL( cFuncName, cDllName )
local hDLL, cFarProc
hDLL = LoadLibrary( cDllName )
if hDll > 32
cFarProc := GetProcAddress( hDLL, "DLLSYMINIT", .T., _INT )
CallDLL( cFarProc )
Eval( &( "{||" + cFuncName + "() }" ) )
endif
return nil

//-------------------------------------------------------------------------//
#include "dll.ch"

DLL32 FUNCTION CusTstBrw() AS LONG PASCAL LIB "rochadll.dll"

DLL32 FUNCTION HBDLLENTRY( cProc AS LPSTR ) AS LONG PASCAL LIB "rochadll.dll"
DLL32 FUNCTION HBDLLENTRY2( cProc AS LPSTR, pItem1 AS LONG, pItem2 AS LONG ) AS LONG PASCAL LIB "rochadll.dll"
DLL32 FUNCTION HBDLLENTRY3( cProc AS LPSTR, pItem1 AS _INT, pItem2 AS _INT ) AS _INT PASCAL LIB "rochadll.dll"


Vejamos agora algumas caracteristicas:


...
DEFINE BUTTON OF oBarMain ACTION WinExec( "RunDLL32.exe rochadll.dll,CusTstBrw" )
...

No trecho acima faço execução de uma função dentro da .DLL usando execução de aplicativo externo.



...
DEFINE BUTTON OF oBarMain ACTION UseDLL( "CusTstBrw", "rochadll.dll" )
...

Neste trecho, usando funções do Harbour exemplifico como chamar uma função existente na .DLL. Esta função deve ser EXPORTável e para tal foi necessária a definição de chamada desta função:


...
DLL32 FUNCTION CusTstBrw() AS LONG PASCAL LIB "rochadll.dll"
...


Abaixo vemos as formas de chamar nossas funções usando HbDllEntry, mas particularmente acho feio assim:


...
MENUITEM "&Clientes..." ACTION HbDllEntry( "Customer" ) MESSAGE "Manutencao de Clientes"
MENUITEM "&Browse..." ACTION HbDllEntry( "CusTeste" ) MESSAGE "Browse de Clientes"
...


A função HbDllEntry() enxerga as funções que não são EXPORTáveis.


Vale lembrar que as .DLL não ficaram pequenas, mas o fator levado em consideração é que para a manutenção fica mais produtivo ter várias .DLLs no sistema e ao modificar uma ou outra, podemos atualizar somente elas sem prejuízo ao EXE principal.


Outra coisa importante:


As telas usadas no aplicativo, não importando qual .DLL poderá usá-la, deverão ser compiladas com o .EXE principal.


O Harbour usado foi a versão 3.2-17626.


O download deste trabalho encontra-se no 4shared.com.


Tenho grande certeza, que seria possivel chamar código xBase contido nas .DLLs através de outras linguagens como Delphi ou Visual Basic. Vale a pena testar e retornar.


Link to comment
Share on other sites

Amiguinhos,
Aquele velho sonho de conseguir executar código xBase contido em uma .DLL compilada com Harbour se concretizou para mim.
Noites sem dormir e muita pesquisa, testes, compilações, chingamentos e sem sapeca-iá-iá, mas consegui.
Meus primeiros testes foram com exemplos existentes em todo lugar, Harbour, xHarbour, Fivewin, etc.
Busquei informações de como o RunDLL32 do Windows trabalhava e fiz minhas tentativas.
Num primeiro momento consegui fazer um EXE executar uma função em uma DLL mas só executava a primeira função que encontrava. Bom já era um começo, mas ao retornar ao EXE paulava.
Tentei com RunDLL32 chamado do EXE e não enfrentava mais este problema, mas tinha uma demora de uns segundos e não era o que eu queria.
Fiz meu próprio RunDLL32 mas ainda tinha de executá-lo indiretamente.
Bom enfim, cheguei onde queria e o primeiro passo para isto foi compilando o código de minha DLL:
rochadll.prg
/*
 * Jose Carlos da Rocha
 * Trabalho com DLL de codigo xBase para uso com Harbour
 * Sao Paulo - 09/09/2014
 * Baseados nos exemplos BabuDLL e outros
 */
#include "fivewin.ch"
 
#pragma BEGINDUMP
        #include <windows.h>
        #include <hbvm.h>
        #include <hbapiitm.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;
        }
 
        void pascal __export HBDLLENTRY( char * cProcName )
        {
           hb_itemDoC( cProcName, 0, 0 );
           return 0;
        }   
 
        void pascal __export CusTstBrw()
        { 
           hb_itemDoC( "CusTstBrw", 0 );
        }
 
        void pascal __export HBDLLENTRY2( char * cProcName, PHB_ITEM pParam1, PHB_ITEM pParam2 )
        {
           hb_itemDoC( cProcName, 2, pParam1, pParam2 );
        }
#pragma ENDDUMP
 
/*
 * MENUITEM "&Clientes..." ACTION HbDllEntry( "Customer" ) MESSAGE "Manutencao de Clientes"
 * Esta funcao foi chamada atraves de parametro da funcao exportavel hbDLLEntry()
 * Chamada indireta, pois a funcao abaixo passa por outra funcao para agir
 * Aqui o exemplo CUSTOMER foi imputado na DLL para demonstrar que trechos grandes de codigo
 * podem residir dentro de uma DLL e serem chamados a partir de um EXE externo.
 */
#include "Customer.ch"
 
function Customer()
   local oWnd, oBar
   local oClients, oClient
   //local oName, cName
 
   SET _3DLOOK ON
 
   USE Customer SHARED NEW ALIAS Clients
   USE Sales    SHARED NEW
   SELECT Clients
 
   DEFINE WINDOW oWnd TITLE "Reporting tools" MDI ;
      MENU BuildMenu(oClients) COLOR "N/W"
 
   DEFINE BUTTONBAR oBar OF oWnd SIZE 60, 60 2007
 
   DEFINE BUTTON OF oBar ACTION MsgInfo( "Click" ) ;
      FILENAME "..\bitmaps\attach.bmp" PROMPT "Attach"
 
   DEFINE BUTTON OF oBar ACTION MsgInfo( "Click" ) ;
      FILENAME "..\bitmaps\calendar.bmp" PROMPT "Calendar"
 
   DEFINE BUTTON OF oBar ACTION MsgInfo( "Click" ) ;
      FILENAME "..\bitmaps\people2.bmp" PROMPT "Clients"
 
   DEFINE BUTTON OF oBar ACTION MsgInfo( "Click" )
 
   SET MESSAGE OF oWnd TO "Testing the FiveWin Report Class" CENTERED
 
   ACTIVATE WINDOW oWnd 
 
   CLOSE DATABASES
   
return nil
 
function BuildMenu(oClients)
   local oMenu
   MENU oMenu
      MENUITEM "&DataBases"
      MENU
         MENUITEM "&Clients..." ACTION  BrwClients(oClients) ;
            MESSAGE "Clients management"
         MENUITEM "&Report..." ACTION GenReport()
         SEPARATOR
         MENUITEM "&End" ACTION oWnd:End() ;
            MESSAGE "End this test"
      ENDMENU
      oMenu:AddMdi()              // Add standard MDI menu options
   ENDMENU
   return oMenu
 
function BrwClients(oClients)
   local oBrw, oIco, oBarBrw
   if oClients != nil
      return nil
   endif
   DEFINE ICON oIco FILENAME "..\icons\customer.ico"
   DEFINE WINDOW oClients TITLE "Clients management" ;
      MDICHILD ICON oIco
   DEFINE BUTTONBAR oBarBrw OF oClients
   DEFINE BUTTON OF oBarBrw ACTION ShowClient(oClients)
   @ 2, 0 LISTBOX oBrw FIELDS OF oClients ;
      SIZE 500, 500 // ON CHANGE ChangeClient(oClients)
   oClients:SetControl( oBrw )
   ACTIVATE WINDOW oClients ;
      VALID( oClients := nil, .t. )        // We destroy the object
   return nil
 
function GenReport()
   local oWnd, oIco
   DEFINE ICON oIco FILENAME "..\icons\print.ico"
   DEFINE WINDOW oWnd MDICHILD TITLE "Clients report" ;
      VSCROLL HSCROLL ICON oIco
   ACTIVATE WINDOW oWnd
   return nil
 
function ShowClient(oClients)
   local oIco, oClient
   local oName, cName
   if oClient != nil
      return nil
   endif
   DEFINE ICON oIco FILENAME "..\icons\Person.ico"
   DEFINE DIALOG oClient RESOURCE "Client" ;
      ICON oIco TITLE "Detalhes"
   REDEFINE SAY ID 3 OF oClient   // To get the proper color
   REDEFINE SAY ID 4 OF oClient
   REDEFINE SAY ID 5 OF oClient
   REDEFINE GET oName VAR cName ID ID_NAME OF oClient
   REDEFINE BUTTON ID ID_NEXT OF oClient ACTION GoNext(oClients,oName)
   SELECT Sales     // We select Sales to properly initialize the Browse
   REDEFINE LISTBOX FIELDS ID ID_SALES OF oClient
   ACTIVATE DIALOG oClient CENTERED NOWAIT ;
      VALID ( oClient := nil, .t. )           // Destroy the object
   SELECT Clients
   return nil
 
function ChangeClient(oClients,oName)
   if oClients != nil
      cName = AllTrim( Clients->Last ) + ", " + Clients->First
      oName:Refresh()
   endif
   return nil
 
function GoNext(oClients,oName)
   if oClients != nil
      oClients:oControl:GoDown()
   else
      SKIP
      if EoF()
         GO BOTTOM
      endif
   endif
   ChangeClient(oClients,oName)
   return nil
 
/*
 * MENUITEM "&Browse..."   ACTION HbDllEntry( "CusTeste" ) MESSAGE "Browse de Clientes"
 * Esta funcao foi chamada atraves de parametro da funcao exportavel hbDLLEntry()
 * Chamada indireta, pois a funcao abaixo passa por outra funcao para agir
 */
function CusTeste()
   USE Customer SHARED NEW ALIAS Clients
   Browse()
   CLOSE DATABASES
   return .t.
 
/*
 * MENUITEM "&Customer..." ACTION CusTstBrw() MESSAGE "Browse de Clientes"
 * Esta funcao foi chamada dentro da DLL usando o proprio nome ao inves de
 * usar hbDLLEntry(), desta forma ficou mais legal
 */
function CusTstBrw()
   USE Sales SHARED NEW
   Browse()
   CLOSE DATABASES
   return .t.
Dentro do código da DLL existem aplicativos que serão chamados pela aplicação principal, Customer(), CusTeste() e CusTstBrw().
Atentem para o seguinte trecho:
#pragma BEGINDUMP
        ...
        void pascal __export HBDLLENTRY( char * cProcName )
        ...
        void pascal __export CusTstBrw()
        ...
#pragma ENDDUMP
Vejam que HBDLLENTRY e CusTstBrw são exportadas, ou seja, são visíveis as chamadas à DLL.
Então como compilar este .PRG e transformá-lo em uma .DLL?
Eu usei o bom-e-velho BUILDH.BAT com algumas alterações, vejam os trechos que foram modificados:
%hdir%\bin\harbour %1 /n /i%fwh%\include;%hdir%\include /w0 /p %3 /d__HARBOUR__ > comp.log
IF ERRORLEVEL 1 GOTO COMPILEERRORS
@type comp.log
 
echo -O2 -e%1.exe -I%hdir%\include;%bcdir%\include %1.c > b32.bc
%bcdir%\bin\bcc32 -M -c @b32.bc
copy %bcdir%\lib\uuid.lib
:ENDCOMPILE
 
IF EXIST %1.rc %bcdir%\bin\brc32 -r -I%bcdir%\include %1
rem IF EXIST %1.rc %vcdir%\bin\rc -r -d__FLAT__ %1
 
echo %bcdir%\lib\c0d32.obj + > b32.bc
Quem usa este .BAT dirá, mas não tem nada de diferente neste trecho, mas tem sim e está na última linha, onde se vê echo %bcdir%\lib\c0d32.obj + > b32.bc
Quando compilamos nossos programas para gerar executáveis o arquivo c0w32.obj é o usado, mas neste caso usaremos o c0d32.obj.
Outra linha alterada no nosso BUILDH.BAT é a linha de chamada do iLink32.exe
if %GT% == gtgui %bcdir%\bin\ilink32 -Gn -aa -s -Tpd @b32.bc
No lugar de -Gn -aa -s -Tpe eu alterei para -Gn -aa -s -Tpd, onde e é para EXE e d é para DLL.
Bom, depois de gerada a DLL é possivel testá-la sem precisar do uso do aplicativo principal, bastando usar para isto o RunDLL32.exe do Windows, executando um comando simples:
%windir%\System32\RunDLL32 rochadll.dll,CusTstBrw
Leve em consideração que os testes foram feitos dentro da pasta SAMPLES do Fivewin, portanto as tabelas CUSTOMER.DBF e SALES.DBF serao usadas.
Se a DLL foi bem gerada um browse aparecerá mostrando os registros da tabela.
Agora vamos a parte do aplicativo principal. Este poderá ser compilado normalmente usando o BUILDH.BAT:
rocha.prg
/*
 * Jose Carlos da Rocha
 * Trabalho com DLL de codigo xBase para uso com Harbour
 * Sao Paulo - 09/09/2014
 * Baseados nos exemplos BabuDLL e outros
 */
#include "FiveWin.ch"
 
FUNCTION Main()
 
   local oWndMain, oBarMain
   
   DEFINE WINDOW oWndMain TITLE "Janela dentro do EXE" MDI MENU BuildMenuMain() COLOR "N/W"
   
          DEFINE BUTTONBAR oBarMain OF oWndMain SIZE 60, 60 2007
 
          DEFINE BUTTON OF oBarMain ACTION WinExec( "RunDLL32.exe rochadll.dll,CusTstBrw" )
          DEFINE BUTTON OF oBarMain ACTION UseDLL( "CusTstBrw", "rochadll.dll" )
 
          SET MESSAGE OF oWndMain TO "Testing the FiveWin DLLs" CENTERED
 
   ACTIVATE WINDOW oWndMain MAXIMIZED VALID MsgYesNo( "Quer sair?" )
   
   RETURN nil
 
FUNCTION BuildMenuMain()
   local oMenu
   MENU oMenu
      MENUITEM "Administracao"
      MENU
         MENUITEM "&Clientes..." ACTION HbDllEntry( "Customer" ) MESSAGE "Manutencao de Clientes"
         MENUITEM "&Browse..."   ACTION HbDllEntry( "CusTeste" ) MESSAGE "Browse de Clientes"
         MENUITEM "&Customer..." ACTION CusTstBrw() MESSAGE "Browse de Clientes"
         SEPARATOR
         MENUITEM "&Sair" ACTION oWnd:End() ;
            MESSAGE "Sair do sistema"
      ENDMENU
      oMenu:AddMdi()              // Add standard MDI menu options
   ENDMENU
   return oMenu
 
FUNCTION UseDLL( cFuncName, cDllName )
   local hDLL, cFarProc
   hDLL = LoadLibrary( cDllName )
   if hDll > 32
      cFarProc := GetProcAddress( hDLL, "DLLSYMINIT", .T., _INT )
      CallDLL( cFarProc )
      Eval( &( "{||" + cFuncName + "() }" ) )
   endif
   return nil
 
//-------------------------------------------------------------------------//
#include "dll.ch"
 
DLL32 FUNCTION CusTstBrw() AS LONG PASCAL LIB "rochadll.dll"
 
DLL32 FUNCTION HBDLLENTRY( cProc AS LPSTR ) AS LONG PASCAL LIB "rochadll.dll"
DLL32 FUNCTION HBDLLENTRY2( cProc AS LPSTR, pItem1 AS LONG, pItem2 AS LONG ) AS LONG PASCAL LIB "rochadll.dll"
DLL32 FUNCTION HBDLLENTRY3( cProc AS LPSTR, pItem1 AS _INT, pItem2 AS _INT ) AS _INT PASCAL LIB "rochadll.dll"
Vejamos agora algumas caracteristicas:
          ...
          DEFINE BUTTON OF oBarMain ACTION WinExec( "RunDLL32.exe rochadll.dll,CusTstBrw" )
          ...
No trecho acima faço execução de uma função dentro da .DLL usando execução de aplicativo externo.
          ...
          DEFINE BUTTON OF oBarMain ACTION UseDLL( "CusTstBrw", "rochadll.dll" )
          ...
Neste trecho, usando funções do Harbour exemplifico como chamar uma função existente na .DLL. Esta função deve ser EXPORTável e para tal foi necessária a definição de chamada desta função:
...
DLL32 FUNCTION CusTstBrw() AS LONG PASCAL LIB "rochadll.dll"
...
Abaixo vemos as formas de chamar nossas funções usando HbDllEntry, mas particularmente acho feio assim:
...
         MENUITEM "&Clientes..." ACTION HbDllEntry( "Customer" ) MESSAGE "Manutencao de Clientes"
         MENUITEM "&Browse..."   ACTION HbDllEntry( "CusTeste" ) MESSAGE "Browse de Clientes"
...
A função HbDllEntry() enxerga as funções que não são EXPORTáveis.
Vale lembrar que as .DLL não ficaram pequenas, mas o fator levado em consideração é que para a manutenção fica mais produtivo ter várias .DLLs no sistema e ao modificar uma ou outra, podemos atualizar somente elas sem prejuízo ao EXE principal.
Outra coisa importante:
As telas usadas no aplicativo, não importando qual .DLL poderá usá-la, deverão ser compiladas com o .EXE principal.
O Harbour usado foi a versão 3.2-17626.
O download deste trabalho encontra-se no 4shared.com.
Tenho grande certeza, que seria possivel chamar código xBase contido nas .DLLs através de outras linguagens como Delphi ou Visual Basic. Vale a pena testar e retornar.

Blz...

Link to comment
Share on other sites

Amiguinhos,

Uma pequena manutenção deverá ser feita no código rochadll.prg.

Na função hbDLLEntry(), a linha que tem um return 0; pode ser excluída sem prejuízo:

        void pascal __export HBDLLENTRY( char * cProcName )
        {
           hb_itemDoC( cProcName, 0, 0 );
           return 0; // -------------------- Retirar esta linha
        }   
 
Link to comment
Share on other sites

Amiguinhos,

Rochinha, bom dia.

Você também pode utilizar o HBMK2 para criar a DLL, os parametros -hbdyn ou -hbdynvm fazem isto.

Abs,

Alexandre

Eu não uso o HBMK2, ainda não tenhos as manhãs do barato. Mas, baseado no que foi apresentado, se puder postar um script, agradeço.

@braços.

Link to comment
Share on other sites

Rochinha, vou parabenizar vc pelo seu empenho e dedicação, alias diga-se de passagem, é constante, em compartilhar conquistas e vitórias q ampliam horizontes de muitos como pude ver em posts de colegas, q diferentes de mim, entenderam a aplicabilidade da sua contribuição.

Peço desculpas pela minha ignorância e/ou falta de visão, mas qual seria a usabilidade/utilidade disto no dia a dia? Poderia citar um exemplo prático?

[]´s

Link to comment
Share on other sites

Amiguinhos,

Ale_Bass,

Valeu! É que sou daquele tipo que gosta de ver todo o código que vai ser executado, tipo, prefiro um arquivo de lote, faço páginas de site no notepad, etc.

Mas tem tempos que venho querendo usar o HBMK2 para simplificar a compilação de sistemas, ainda mais quando já está gigantesco o conjunto de arquivos .PRG e RC.

Fladimir,

imagine as duas situações:

1 - Você fez uma rotina show-de-bola e que seria uma maravilha para a comunidade, se não fosse pelo fato de que você usa o Harbour ano 1900 para Borland e alguém usa xHarbour ano 2030 com MingW. Na hora da compilação dá pau, falta, biblioteca, código C incompatível, etc e outros etc.

2 - Você tem um sistema monolítico, e está em pleno vapor de atualizações, manutenções, criações, possui um leque gigantesco de clientes e toda vez que precisa atualizar o cliente, precisa derrubar toda a rede e blábláblá.

Se o seu sistema possui módulos em DLL você pode atualizar um ou outro sem comprometimento do restante.

Se a sua rotina show-de-bola for usada por alguém ela não ficará somente no âmbito da linguagem, dependendo, pode ser acessada através de um aplicativo VisualBasic, Delphi, etc.

Portanto, resta entender onde a tecnologia se aplica e no momento certo voce a usará.

@braços.

Link to comment
Share on other sites

Amiguinhos,

Apesar de ter iniciado o tópico, outro colega do Forum PC Toledo deu continuidade, testando com outros compiladores e obteve ótimos, senão excelentes resultados em relação ao uso de MSVC e MingW.

Se alguém usa Fivewin e tem preferência por estes compiladores poderá fazer uso e entender o funcionamento da parafernália toda com estes compiladores.

$u$$e$$o.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...