You are here

bBrowser: private investigations (Dire Straits)

private investigations

I long thought where to place the material: in the blog or in the section of articles. Usually, I do small notes, arguing: «well, now it is actual, and through time – it is necessary for nobody». And only more serious things – I make out in the form of articles.

And here has reflected … After all, conversation will go about mistakes and inconveniences, and they – through any time – will be eliminated by the developer. I will tell in confidence that after each exit of updating for bBrowser I carefully look through changes, in search of the corrected. To remove, become already unnecessary, my additions and corrections.


But, opening this subject, I show my method of work, the approach to the solution of problems, the views. I.e., I give the tool to the reader. And it – is much more serious, therefore – hold article!


Object of research – bBrowser v.



It is my first translated article in English. My English is very bad. Therefore, I have simplified my text. Here you will not find beautiful English speech. But, I hope that despite machine translation – to you everything will be clear.


As always, I will say only that I know and I will do that I am able! I would like, that you have attentively read my article, have estimated and have made critical comments. Let's begin! :))


As the tool, библиотеека bBrowser has appeared long ago, gradually forcing out competitors and homemade products. But, I know some people who with pleasure continue to use and develop own development which do not concede to the leader. But, it I have digressed.

I come back. :)). Therefore in bBrowser library there are the mistakes which have arisen because of high speed of development, and because of simple forgetfulness of programmers.


High speed of development usually leads to loss of attention and some worst study of a task. Attention that leads to that some important things don't become is lost or not best method of the solution of the task is selected. Here the safe assistant – the development plan (project) and accuracy of its execution. Despite of pressure from the customer. Since the customer often can't competently decide that is now important, and that - No.


Germans work rather scrupulous therefore gross errors normally don't allow. They always have a plan and they "are morally steady" in respect of a deviation from diagrams. The main source of errors - underestimation of behavior of users. I.e., in most cases – normal simplification because of which reliability of library suffers.


From bBrowser library fully I use only three classes:

  • bBrowser Class
  • bArrayServer Class
  • bIcon Class


I use the class bIcon very little. Therefore, I will tell nothing about bIcon. But, notes are. My article will be devoted to finishings for the classes "bBrowser" and "bArrayServer".


Problems in the class bBrowser:


  1. Error by operation with Visual Styles.
  2. In case of in-cell editing it is impossible to set the size for big controls (ComboBox, etc.).
  3. In case of in-cell editing - there is no processing of an event of a choice by "mouse" in the element ComboBox (and to it similar).
  4. Problems in a coloring.
  5. Incomplete support of the class AdoRecordSet.

Problems in the class bArrayServer:


  1. Errors in case of order creation (CreateOrder).
  2. Errors when deleting the warrant (ClearOrder).
  3. Errors when copying in the file.
  4. Defect in the FieldPut method.
  5. Errors in the OrderInfo method.

It not all. But, for while will suffice. :))





  1. Error by operation with Visual Styles.

On this matter I in details told in the material under the name: «Visual Styles: CA-Visual Objects, bBrowser и все-все-все…». My decision is to use of the improved function:


FUNCTION ms_EnableAppVisualTheme( lEnable := TRUE AS LOGIC,;

                                                            liThemeFlags := -1 AS LONG ) AS LOGIC PASCAL

// Pencil 09.11.2011


// liThemeFlags:

// STAP_ALLOW_NONCLIENT         Specifies that the nonclient areas of application windows will have visual styles applied.

// STAP_ALLOW_CONTROLS           Specifies that the common controls used in an application will have visual styles applied.

// STAP_ALLOW_WEBCONTENT     Specifies that web content displayed in an application will have visual styles applied.


            LOCAL dwStyle AS DWORD


// At first we work with means of bBrowser

            bvsEnableVisualStyles( lEnable )


// It isn't necessary, since already made by means of bvsEnableVisualStyles

//          EnableAppVisualTheme( lEnable )


// Compulsorily we do as in CA-Vo (since in bBrowser it - in a different way)  :))

            IF liThemeFlags >= 0

                        dwStyle := DWORD( _CAST, liThemeFlags )


                        IF lEnable

                                   dwStyle := _OR( STAP_ALLOW_NONCLIENT, STAP_ALLOW_CONTROLS, STAP_ALLOW_WEBCONTENT )


                                   dwStyle := STAP_ALLOW_NONCLIENT




            SetThemeAppProperties( dwStyle )


//          RETURN ( VerifyThemeState( ) )      // For CA-Vo 28

            RETURN ( IsThemeEnabled( ) )         // For CA-Vo 27



Unfortunately, for my English-speaking friends, I for the present don't know when I can make transfer of this and other articles. I will try to make it as soon as possible.



  1. In case of in-cell editing it is impossible to set the size for big controls (ComboBox, etc.).

Probably, many when editing data in the browser used enhanced features of editing. The class bBrowser allows to do it. For example:  oColumn:ViewValueAs := #ComboBox


I too with pleasure use ComboBox. There are also other large controls which aren't located in the sizes of a cell, and reveal in case of the editing beginning. And here the main shortcoming – impossibility to leave from standard height ( height=80 )…


(Attention!!! This situation is watched only when you have no Manifest module. If to add a line: «RESOURCE CREATEPROCESS_MANIFEST_RESOURCE_ID RC_RT_MANIFEST %appwizdir%\» – this problem won't be shown.)


The best method of the decision – to apply method redefinition. So I created the class successor:


CLASS msBrowser INHERIT bBrowser

// Pencil 07.12.2007


Within this class we can add missing variables or directly change the sizes of created controls. I foresee that developers of bBrowser through any time will add missing parameters. Therefore, I didn't begin to risk. I defined the necessary sizes in the EditCreate method:


METHOD EditCreate( iColumn, iRow, iRecNo, symClass, iStyle, uSpecial ) CLASS msBrowser

// Pencil 04.01.2010



***  the piece was eaten by mice ***



                        IF IsNil( iStyle )


// Pencil 22.06.2010

// Changed sorting

//                                 iStyle := WS_CHILD+WS_TABSTOP+CBS_AUTOHSCROLL

                                   iStyle := WS_CHILD+WS_TABSTOP+CBS_SORT+CBS_AUTOHSCROLL



                        IF symClass = #ComboBox


// Pencil 22.06.2010

// Extended the dropdown list

//                                 oEdit := ComboBox{SELF, 8001, Point{srcArea.Left, srcArea.Top}, Dimension{srcArea.Right-srcArea.Left, 80}, uSpecial, iStyle}


oPoint := Point{ srcArea.Left, srcArea.Top }

oDim    := Dimension{ srcArea.Right - srcArea.Left, 200 }

oEdit := ComboBox{ SELF, 8001, oPoint, oDim, uSpecial, iStyle }



***  the piece was eaten by mice ***




  1. In case of in-cell editing - there is no processing of an event of a choice by "mouse" in the element ComboBox (and to it similar).

Let's continue talk about ComboBox uses as an editing element. Probably, some colleagues noted that if it will reveal up and will superimpose itself titles of columns, that, after "mouse" click (a choice of suitable point) the list will be closed. But, on the screen there will be a defect in the form of a rectangular spot (on title of columns and above if the list revealed above columns). And it will remain, while you won't make compulsory updating of data of a window, or you will need to click once again on a cell.



In CA-Visual Objects (and in SP3 version 2.8) in general there is no processing of an event of a choice by "mouse" of an element of the dropping-out list. And developers of bBrowser didn't begin to correct it in the library. It leads to that after click by "mouse" work proceeds while you don't end it, having created an event of the completion of editing.


Let's correct:


METHOD EventHandler( oEvent ) CLASS msBrowser

 // Pencil 13.02.2012


            // The click of "mouse" according to the ComboBox list – means a choice

            // Earlier, the choice was fixed only by ENTER key press


            LOCAL hWnd AS PTR

            LOCAL oChild AS OBJECT



            IF oEvent:uMsg == WM_COMMAND


                        hWnd := PTR( _CAST, oEvent:lParam )

                        IF .NOT. hWnd == NULL_PTR


                                   oChild := __WCGetControlByHandle( hWnd )


// We check object: it is ComboBox?

                                   IF .NOT. oChild == NULL_OBJECT .AND.;

IsInstanceOf( oChild, #ComboBox )


// CBN_SELENDOK - Specifies that the choice is made in the opened list

                                               IF HiWord( oEvent:wParam ) == CBN_SELENDOK


                                               // der Owner mu? das Event verarbeiten, da ansonsten bei einer

                                               // Combobox nicht der Value richtig gesetzt wird

                                                           SELF:oCommandOwner:Dispatch( oEvent )



                                                           // If we edit

                                                           IF SELF:InEdit()


                                                                       IF .NOT. SELF:EditClose() // We finish


                                                                                  SELF:EditCancel() // We interrupt...




                                                           RETURN SELF:oCommandOwner:EventReturnValue







            RETURN ( SUPER:EventHandler( oEvent ) )



  1. Problems in a coloring.

What do I understand as it? These are problems in case of a coloring of lines and columns in the browser. Normally the coloring becomes by means of ColorCondition (lower an example from bBrowser description):


// define color condition and add it to the browser

oColorCondition := bColorCondition{"Server:BIRTHDAY<CToD('01/01/60')",;





To us say that it to make very simply: Server:BIRTHDAY. Even if this field isn't described.

Rather simply. It works… But, if there are a lot of fields (more than three) if there is a lot of different difficult coloring, in the program starts to be buggy. Thus errors unclear. And directly to define a problem - it is difficult.


So in what a problem? And a problem that string expression reveals by means of macro substitution: SELF:uCondition := &("{|Server, Column, Row| "+uCondition+"}")


And in Ca-Vo - there are restrictions. Whose here problems (Ca-Vo or bBrowser) – me doesn't interest. But, I would recommend to developers: do the correct examples not to accustom programmers to bad style to programming. Fields in servers should be described in advance or to apply the FieldGet method:


oColorCondition := bColorCondition{ "Server:FieldGet(#KOL) = 0 .AND. Server:FieldGet(#REZ) = 0", oBrowser:Server,, oBrush }


oBrowser:ColorCondition:Add( oColorCondition )


If to write here so, errors won't be! It is not dependent on number of fields and a level of complexity of a coloring.



  1. Incomplete support of the class AdoRecordSet.

In bBrowser AdoRecordSet support is declared. Not to be unfounded in the statement – I will give a small slice from SDK:


METHOD Use(oServer, auField, auOpen, auFormat) CLASS bBrowser

// =========================================================

// Beschreibung:          Aktuellen Server entfernen und neuen Server setzen.


// Syntax:        bBrowser:Use(oServer, [auField], [auOpen], [acHeader | onaFormat])


// Historie:       07.10.1998      JB        Methode implementiert

//                      09.12.2005      JB        Vorhandene Spaltenobjekte werden zerstцrt.

//                      10.02.2009      JB        Optimierung implementiert, sodass der aktuelle Datensatz

//                                                                     immer vollstдndig sichtbar ist.

//                      21.09.2009      JB        Beim ServerType #DBase wird der Filter vermerkt.

//                      17.10.2009      JB        Ermittlung des Status fьr die Infowerte korrigiert.

// =========================================================



***  the piece was eaten by mice ***



IF Empty(SELF:symServerType)


IF IsInstanceOf(SELF:oServer, #DBServer) .OR.;

(IsInstanceOf(SELF:oServer, #bCacheServer) .AND.;

IsInstanceOf(SELF:oServer:DataSource, #DBServer))


            SELF:symServerType := #Dbase


ELSEIF IsInstanceOf(SELF:oServer, #SQLSelect) .OR.;

(IsInstanceOf(SELF:oServer, #bCacheServer) .AND.;

IsInstanceOf(SELF:oServer:DataSource, #SQLSelect))


            SELF:symServerType := #SQL


ELSEIF IsInstanceOf(SELF:oServer, #ADOServer) .OR.;

(IsInstanceOf(SELF:oServer, #bCacheServer) .AND.;

IsInstanceOf(SELF:oServer:DataSource, #ADOServer))


            SELF:symServerType := #SQL


ELSEIF IsInstanceOf(SELF:oServer, #ADORecordSet) .OR.;

(IsInstanceOf(SELF:oServer, #bCacheServer) .AND.;

IsInstanceOf(SELF:oServer:DataSource, #ADORecordSet))


     SELF:symServerType := #SQL





***  the piece was eaten by mice ***



As we see, AdoRecordSet (and not only) are supported as the server for bBrowser. That's wonderful! But, programmers forgot to correct the code that it used standard methods of the class AdoRecordSet. I found it when decided to apply the library «Библиотека CA-Visual Objects для MySQL». It is difficult to understand, why developers didn't familiarize with the list of the standard AdoRecordSet methods...


But, that the mechanism earned, I had to write missing methods: DbStruct, Deleted, FieldPos, FieldSpec, FieldSym, GoBottom, GoTo, GoTop, LastRec, Notify, RecCount, RecNo, RegisterClient, ResetNotification, Skip, SuspendNotification, UnRegisterClient, Used.





  1. Errors in case of order creation (CreateOrder).

At the heart of bArrayServer - very old idea of the class ArrayServer, i.e. operation possibility with an array, as with the normal server. Simply and conveniently. But, there is a mass of "reefs". It because a normal array very not typified thing. Absolutely there are no warranties that in the necessary column there will be data of the declared type. Besides, programmers have a big temptation to expand the list of supported types. For example, in SDK bArrayServer it is visible that authors added the types "U" (indefinite) and «O» (object)… But, if you something expand, prepare the solution of problems.


The CreateOrder method is applied to creation of the order. Sorting of data, as one would expect, becomes by means of the ASort function. But, in CA-Vo this function badly works, if it is offered to sort data with NIL, objects or heterogeneous data! Well, and where protection against it? Hey, developers!


Let's consider this method still a little bit… We see that it is similar on with the same name of the class DbServer (it conveniently). But, in case of warrant creation – it is created by the unnamed!.. I.e., it is given, but empty. Why so to do?


Certainly, the order can give a name by means of the OrderInfo method (to it we still will return). But, it is inconvenient. The best method – to redefine a method:


CLASS msArrayServer INHERIT bArrayServer

// Pencil 03.12.2008


METHOD CreateOrder( cOrderName, uExpression, uForCondition, uWhileCondition,;

                                   lDescend, cbEval, iInterval ) CLASS msArrayServer


// Pencil 10.12.2008


// METHOD CreateOrder(uExpression, uForCondition, uWhileCondition,;

//                                 lDescend, cbEval, iInterval) CLASS bArrayServer



***  the piece was eaten by mice ***



// Name

//          SELF:auOrder[SELF:iOrderCount, BASOI_NAME]          := ""


            IF !IsNil( cOrderName ) .AND. IsString( cOrderName )


                        cOrderName := AllTrim( cOrderName )

                        cNameOrder := Upper( cOrderName )


                        cNameOrder := ""


            SELF:auOrder[ SELF:iOrderCount, BASOI_NAME ]           := cNameOrder



***  the piece was eaten by mice ***



// FOR-Bedingung ausfьhren

IF .NOT. lForCondition .OR. Eval(uConditionBlock, SELF)


            // aktuellen Datensatz einsortieren

            // aktuellen Datensatz einsortieren

            iValueCount += 1


// We catch an error in advance

//          auValue[iValueCount, BASOI_KEYLIST_VALUE] := Eval(uExpressionBlock, SELF)


            uValue := Eval( uExpressionBlock, SELF )

            IF IsNil( uValue )


oError:Description := ;

"In the order (" + cExpression + ") will turn out NIL value :" + CRLF +;

" there is no specified field or NIL in data."


                        BREAK oError



            IF ValType( uValue ) == "O"


oError:Description := ;

      " In the order (" + cExpression + ") there is a sorting of objects." + CRLF +;

                              " bArrayServer doesn't sort it..."


                        BREAK oError



            auValue[ iValueCount, BASOI_KEYLIST_VALUE ] := uValue



***  the piece was eaten by mice ***



Here, somewhere so … And the main thing – is closer to syntax of the appropriate DbServer method.



  1. Errors when deleting the warrant (ClearOrder).

Here too it is interesting. Information on orders is stored in an array. In attempt of closing of the order which number doesn't get to value range in an array – becomes nothing, but FALSE value returns. It, in general, correctly, but isn't so inconvenient. Because to this order occurs nothing, and we with it can't make anything. Moreover, if number of the order quits for the existing range is an appreciable error, an error of the programmer. My judgement that in this case – it is necessary to return TRUE:


METHOD ClearOrder( iOrderNo ) CLASS msArrayServer

// Pencil 13.10.2009

            // We correct: if the order isn't present, then, it is cleared!


            // angegebene Sortierung lцschen

            IF SELF:iOrderCount = 0


//                      RETURN FALSE

                        RETURN TRUE


            ELSEIF IsNumeric( iOrderNo )


                        IF iOrderNo = 0

                                   iOrderNo := SELF:iOrderNo



                        IF .NOT. Between( iOrderNo, 1, SELF:iOrderCount )


//                                 RETURN FALSE

                                   RETURN TRUE




So it is pleasant to me more!



  1. Errors when copying in the file.

Here (in the CopyToFile method) I don't accept at once three things:

·        At the beginning, situation processing when as parameter nonexistent fields are transferred is made. But, it isn't finished – the developer obviously hurried.

·        The situation isn't processed, when objects are in the field stored (and it allows bArrayServer!)

·        When copying data in DBF the situation when this file should be in the OEM coding isn't processed. There it is written only to ANSI!


We correct:



***  the piece was eaten by mice ***



            FOR iPos:=1 UPTO iSize

                        IF IsNumeric(auFieldID[iPos]) .AND. auFieldID[iPos]>0 .AND.;

auFieldID[iPos] <= SELF:FCount


                                   iField := auFieldID[iPos]

                        ELSEIF IsString(auFieldID[iPos])

                                   iField := SELF:FieldPos(auFieldID[iPos])

                        ELSEIF IsSymbol(auFieldID[iPos])

                                   iField := SELF:FieldPos(auFieldID[iPos])

                        ELSEIF IsInstanceOfUsual(auFieldID[iPos], #DataField)

                                   iField := SELF:FieldPos(auFieldID[iPos]:Name)


                                   iField := 0



// Pencil 14.02.2009


//                      IF iField>0

//                                 oDataField := SELF:aDataFields[iField]

//                                 oFieldSpec := oDataField:FieldSpec

//                                 IF Instr(oFieldSpec:ValType, "CDLN") .OR.;

//                                             (oFieldSpec:ValType="M" .and. symFormat=#DBF)


//                                             AAdd(auField, {iField, oFieldSpec:ValType,;

//                                                         oFieldSpec:Length, oFieldSpec:Decimals})

//                                 ENDIF

//                      ENDIF


                        IF iField = 0


auTemp := { iField, "U", 0, 0 }

AAdd( auField, auTemp )



                                   oDataField := SELF:aDataFields[ iField ]

                                    oFieldSpec := oDataField:FieldSpec

                                   IF Instr( oFieldSpec:ValType, "CDLN" ) .OR.;

( oFieldSpec:ValType = "M" .AND. symFormat = #DBF )


                                               AAdd( auField, { iField, oFieldSpec:ValType,;

oFieldSpec:Length, oFieldSpec:Decimals } )



auTemp := { iField, "U", 0, 0 }

AAdd( auField, auTemp )





***  the piece was eaten by mice ***



            ELSEIF symFormat=#DBF


// Pencil 30.01.2009


lAnsiDBF := odbsTarget:Info( DBI_ISANSI )




                        auValue := ArrayCreate(iFieldCount)

                        WHILE .NOT. SELF:EoF .AND. iRecCount>0 .AND.;

(IsNil(uWhileCondition) .OR. Eval(uWhileCondition, SELF))


                                    IF IsNil(uForCondition) .OR. Eval(uForCondition, SELF)


                                               FOR iField:=1 UPTO iFieldCount


// Pencil 14.02.2009


//                                                         auValue[iField] := SELF:FIELDGET(auField[iField, 1])

IF auField[ iField, 1 ] > 0


            uValue := SELF:FIELDGET( auField[ iField, 1 ] )



            IF .NOT. lAnsiDBF .AND. auField[ iField, 2 ] == "C" .AND. IsString( uValue )

                        auValue[ iField ] := Ansi2Oem( uValue )


                        auValue[ iField ] := uValue





                                               lDeleted := SELF:Deleted


                                               // export current record

                                               IF odbsTarget:Append()


                                                           FOR iField := 1 UPTO iFieldCount


// Pencil 14.02.2009


//          odbsTarget:FIELDPUT(auField[iField, 1], auValue[iField])

IF auField[ iField, 1 ] > 0

            odbsTarget:FIELDPUT( iField, auValue[ iField ] )





***  the piece was eaten by mice ***



I understand that the Reader waits from me complete listing of my alterations in methods. It is fair. But, believe me – the text will turn out huge (it already and so – rather big)! And if you understand CA-Vo, also those bastings with interest will suffice you that I provided. So, god to you in the help! At you all to turn out!



  1. Defect in the FieldPut method.

I remind, in ArrayServer all of us time fight for compliance of data to the declared structure. Plus, NIL values (if you are going to do the order) are very undesirable.


Let's complete the FieldPut method:


            oFieldSpec := SELF:FieldSpec( uField )

            IF .NOT. oFieldSpec == NULL_OBJECT


                        cType := oFieldSpec:ValType


// Pencil 19.05.2012


// Add control of admissible types of data

                        IF InList( cType, "C", "M", "D", "L", "N", "O" )


// Let's add check on NIL

                                   IF uValue == NIL


                                               IF cType == "C" .OR. cType == "M"

                                                           uValue := NULL_STRING

                                               ELSEIF cType == "D"

                                                           uValue := NULL_DATE

                                               ELSEIF cType == "L"

                                                           uValue := FALSE

                                               ELSEIF cType == "N"

                                                           uValue := 0

                                               ELSEIF cType == "O"

                                                           uValue := NULL_OBJECT





  1. Errors in the OrderInfo method.

It concerns the OrderInfo method. Here I have only two remarks:

  • As parameter it is impossible to transfer a order name (only number!)
  • At the instruction DBOI_NUMBER – returns not number of the specified order in the list of orders, and number current that completely disperses from work of the similar DbServer method.


Let's redefine one more method:


METHOD OrderInfo( iInfoType, uReserved, uOrder, uValueNew ) CLASS msArrayServer

// Pencil 19.05.2012

            // We correct OrderInfo method work


//METHOD OrderInfo(iInfoType, uReserved, iOrderNo, uValueNew) CLASS bArrayServer

//METHOD OrderInfo( kOrderInfoType, oFSIndex, uOrder, uOrdVal ) CLASS DbServer

//FUNCTION DBORDERINFO    (nOrdinal,cBagName, uOrder, xNewVal)


            LOCAL wOrder AS DWORD

            LOCAL cOrder AS STRING



            IF         IsNil( uOrder )


                        wOrder := SELF:iOrderNo                


// We learn to work with a string name of a field

            ELSEIF IsString( uOrder )


                        cOrder := AllTrim( uOrder )

                        IF .NOT. cOrder == NULL_STRING


                                   cOrder := Upper( cOrder )

                                   wOrder := AScan( SELF:auOrder, { |x| x[ BASOI_NAME ] == cOrder } )


                                   wOrder := 0



            ELSEIF IsLong( uOrder ) .AND. Between( uOrder, 1, SELF:iOrderCount )


                        wOrder := uOrder


                        wOrder := 0



// We correct a mistake with DBOI_NUMBER

            IF iInfoType == DBOI_NUMBER

                        RETURN wOrder




            RETURN ( SUPER:OrderInfo( iInfoType, uReserved, wOrder, uValueNew ) )


Hurrah! You at all don't represent to yourselves, as I was tired, while all this wrote. If I knew in advance that I will spend so much time – probably, would spit and refused. But, after all – finished work. I hope, it is useful to you.



20.05.2012 г.   Pencil (Карандаш).




It's really a great and useful piece of information. I'm happy that you just shared this helpful info with us. Please keep us up to date like this. Thanks for sharing.


Unfortunately, your site has no relation to this article (bBrowser and Ca-Vo). Advertizing of outside sites - here is forbidden. Therefore, the manager deleted your link.

I think this is one of the most important information for me. And i'm glad reading your article. But wanna remark on some general things, The web site style is wonderful, the articles is really nice

D. Good job, cheers

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer