Wednesday, February 25, 2009

How to add code created by Visual xHarbour to a xHarbour Application [Updated 25th Feb 2009]

As you are probably aware, CA-Clipper codes can readily be recompiled in Xharbour into a W32 executable. In my scenario, I have a complex processing engine originally written in CA-Clipper 5.2e which can then be called within my Delphi applications

I had earlier used CA-Clipper 5.2e +Fivewin to create a W16 executable which was then called via winexec() from my Delphi Application. Being 16-bits meant that NTVDM was called automatically by Windows. What I wanted was a true W32 executable which will not require the usage of NTVDM.

There are 2 scenarios involved
1) Automatic processing without user input
2) Need for user input before automatic processing takes place

For Scenario 1, all I needed was to recompiled in Xharbour to transform my 5.2e CA-Clipper Codes into a 32-bit true windows application. It is that easy :-)

For Scenario 2, I needed user to input a parameter. Problem is that XHarbour does not have a simple Window Dialog function such as Delphi's InputQuery. So what's the solution ?

The Solution

1) Use Visual xHarbour (VXH) to create a windows form with
a) label control named MessageLabel
b) edit control named InputValueEdit
c) button control named OKBtn
d) button control named CancelBtn

2) Saved the Windows Form as

//MsgGetForm.prg

GLOBAL EXTERNAL Application
GLOBAL cMessage
GLOBAL cCaption
GLOBAL cInputValue

#include "vxh.ch"
#include "MsgGetForm.xfm"
//---------------------------------------- End of system code ----------------------------------------//

//----------------------------------------------------------------------------------------------------//
METHOD CancelBtn_OnClick( Sender ) CLASS MsgGetForm
::close()
RETURN Self
//----------------------------------------------------------------------------------------------------//
METHOD MsggetForm_Form_OnActivate( Sender ) CLASS MsgGetForm
::MessageLabel:caption:=cMessage
::caption:=cCaption
::InputValueEdit:caption:=cInputValue

RETURN Self
//----------------------------------------------------------------------------------------------------//
METHOD OKBtn_OnClick( Sender ) CLASS MsgGetForm
cInputValue:= ::InputValueEdit:Caption
::close()
RETURN Self


MsgGet_XFM.prg (automatically generated by VXH)

#include "vxh.ch"
//---------------------------------------- End of system code ----------------------------------------//

//------------------------------------------------------------------------------------------------------------------------------------

CLASS __MsgGet INHERIT Application
// Components declaration
// Controls declaration
METHOD Init() CONSTRUCTOR

// Event declaration
ENDCLASS

METHOD Init( oParent ) CLASS __MsgGet
::Super:Init( oParent )


// Populate Components
// Properties declaration

::Create()

// Populate Children
RETURN Self

//MsgGetForm.xfm (text definition based on the window form created with VHB)
//---------------------------------------------------------------------------------------------------------------------------------
//
CLASS MsgGetForm INHERIT WinForm
// Components declaration
// Controls declaration
DATA MessageLabel EXPORTED
DATA InputValueEdit EXPORTED
DATA OKBtn EXPORTED
DATA CancelBtn EXPORTED
METHOD Init() CONSTRUCTOR

// Event declaration
METHOD MsggetForm_Form_OnActivate()
METHOD OKBtn_OnClick()
METHOD CancelBtn_OnClick()

ENDCLASS

METHOD Init( oParent ) CLASS MsgGetForm
::Super:Init( oParent )

::EventHandler[ "OnActivate" ] := "MsggetForm_Form_OnActivate"

// Populate Components
// Properties declaration
::Left := 10
::Top := 10
::Width := 534
::Height := 139
::Name := "MsgGetForm"
::Caption := "MsgGetFormCaption"
::ClipChildren := .T.
::ClipSiblings := .T.

::Create()

// Populate Children
WITH OBJECT ( ::MessageLabel := LABEL( Self ) )
:Left := 41
:Top := 8
:Width := 160
:Height := 16
:Name := "MessageLabel"
:Caption := "MessageLabel"
:Border := .F.
:ClipChildren := .T.
:ClipSiblings := .T.
:TabStop := .F.
:Create()
END //MessageLabel

WITH OBJECT ( ::InputValueEdit := EDIT( Self ) )
:Left := 41
:Top := 32
:Width := 445
:Height := 22
:Name := "InputValueEdit"
:ClientEdge := .T.
:Border := .F.
:ClipSiblings := .T.
:Create()
END //InputValueEdit

WITH OBJECT ( ::OKBtn := BUTTON( Self ) )
:Left := 178
:Top := 61
:Width := 77
:Height := 30
:Name := "OKBtn"
:Caption := "&OK"
:Border := .F.
:ClipChildren := .T.
:ClipSiblings := .T.
:EventHandler[ "OnClick" ] := "OKBtn_OnClick"
:Create()
END //OKBtn

WITH OBJECT ( ::CancelBtn := BUTTON( Self ) )
:Left := 268
:Top := 62
:Width := 79
:Height := 31
:Name := "CancelBtn"
:Caption := "&Cancel"
:Border := .F.
:ClipChildren := .T.
:ClipSiblings := .T.
:EventHandler[ "OnClick" ] := "CancelBtn_OnClick"
:Create()
END //CancelBtn

::Show()

RETURN Self


2) Next, I added a wrapper function MsgGet() function in my Main.prg

// function in msggetform.prg
FUNCTION MsgGet(cFormCaption,cFormMessage,cFormInputValue)
// GLOBALs in MsgGetForm.prg
cMessage:=cFormMessage
cCaption:=cFormCaption
cInputValue:=cFormInputValue
__MsgGet( NIL ):Run( MsgGetForm( NIL ), HB_aParams() )


RETURN cInputValue

3) In the calling prg

cRetVal:=MsgGet('Create Data','Input No. of Working Days', @mNo_of_days)

That's it !

What is my wish list for the xHarbour people ? Provide a standard windows function as part of xHarbour :-)

Comments from David A. Smith , comp.lang.xHarbour
Some options are:
1) make a DLL of your "scenario" code, and call it as an unloadable library. (Not sure if there are limitations on collecting user input in a DLL.)
2) call your "scenario" code via OLE / xBScript
3) do it the way you have been doing it, just translate / recompile.

No comments:

Post a Comment