Intermittant xBrowse ADO error ( again )

Re: Intermittant xBrowse ADO error ( again )

Postby nageswaragunupudi » Sun Aug 09, 2009 3:53 am

Armando wrote:James:

James Bott wrote:So, if I understand correctly, if the record set record count is zero then oRs:RecordCount() will error out?

Yes.

Rick:
I'm glad to hear that.

Regards


I beg to disagree totally.

oRs:RecordCount returns 0 but DOES NOT error out on Empty RecordSet.
Emptiness of a recordset can be checked either by ( oRs:Bof .and. oRs:Eof ) or by ( oRs:RecordCount == 0 ) safely.
( Note: Our discussion is confined to ClientSide recordsets only ).

When recordset is empty almost all other methods ( bookmark, absoluteposition, etc ) fail.

So, a codeblock like this :
Code: Select all  Expand view

::bBookMark := { |n| If( ::oRs != nil .and. ::oRs:State > 0 .and. ::oRs:RecordCount > 0,
                     If( Empty( n ), ::oRs:BookMark, ::oRs:BookMark := 0.0 + n ), 0 ) }
 

should work. Here we are checking not only that oRs is not nil but oRs is in Open State also.
We are also checking that n is not nil and also n is not zero. Setting book mark to zero errors out.
Also in the present win32ole.prg, assigning integer value to BookMark errors out. So we are coverting n to float, if it is integer.

All default codeblocks of xBrowse assume that the datasource ( ADO, RDD, TData, Array/ Hash ) are not nil and are *open*.
They are not written to work when the datasource is made nil or when the datasource is closed.
So if Paint method is called _after closing the datasource_ ( ADO, RDD, etc ), all these codeblocks fail and give runtime errors.
There is no use recoding one or two codeblocks like bBookMark, because Paint method depends on *many* other codeblocks as well.

If we adopt this approach, we need to modify ALL codeblocks for all Set methods to check _each time_, that the datasouce is still not made nil and it is still in open state.

Instead a better and simpler way is to prevent Paint method ( probably Refresh method also ) not to work once the data source is closed or made nil, by setting an appropriate flag.

Incidentally, calling oBrw:End() does not help in any way. It just returns .t. and does nothing else. ( It evaluates oBrw:bValid if any ).
Regards

G. N. Rao.
Hyderabad, India
User avatar
nageswaragunupudi
 
Posts: 10248
Joined: Sun Nov 19, 2006 5:22 am
Location: India

Re: Intermittant xBrowse ADO error ( again )

Postby Rick Lipkin » Sun Aug 09, 2009 2:40 pm

Rao

Just curious .. why do I seem to be having these problems with xbrowse and ADO ?? .. I agree with you there is a 'paint' failure and chain of events if a recordset has been destroyed or NIL ..

I am at a loss to understand why I can not get this to fail every time .. The code I have presented here works ( generally ) flawsly for me .. I can open and close the valid and not get a single error ..

However, the error.log persists with the intermittant failure and chain of mentioned events and you are 'on target' in your assessment.

Your 'flag' would emcompas a lot of code re-write .. I am sure Antonio is probably monitoring this thread .. because there are 'many more eyes' looking at this problem than when we started .. Just curious if I could easily set a 'trap' for this condition to trace where this problem initiates and under what circumstances ..

Any suggestions on where I can place a msginfo() and trap a nil or closed recordset and see under what circumstances I can get a failure ?

Thanks
Rick
User avatar
Rick Lipkin
 
Posts: 2618
Joined: Fri Oct 07, 2005 1:50 pm
Location: Columbia, South Carolina USA

Re: Intermittant xBrowse ADO error ( again )

Postby James Bott » Sun Aug 09, 2009 3:34 pm

What Rao said does shed some new light. I was under the impression that End()ing a control destroyed it, but apparently not. Look at this sample code:

Code: Select all  Expand view
#include "FiveWin.ch"
#include "xbrowse.ch"

function Main()

   local oWnd, oBrw

   DEFINE Window oWnd

   define buttonbar oBar of oWnd
   define button of oBar action (oBrw:end(), oBrw:display(), msgInfo( valtype( oBrw ) ) )

   @ 0, 0 XBROWSE oBrw OF oWnd ARRAY { { "one","two","three" } } AUTOCOLS

   oBrw:CreateFromCode()

   oWnd:oClient:= oBrw

   activate window oWnd


return nil


Calling oBrw:End() followed by oBrw:display() does NOT create an error. The following msgInfo( valtype( oBrw ) ) returns "O" so the object still exists. So perhaps the simple solution is to do:

oBrw:End()
oBrw:= nil

Then close your recordset.

Now I wonder why it doesn't always crash.

James
User avatar
James Bott
 
Posts: 4840
Joined: Fri Nov 18, 2005 4:52 pm
Location: San Diego, California, USA

Re: Intermittant xBrowse ADO error ( again )

Postby Rick Lipkin » Sun Aug 09, 2009 4:08 pm

James

Thanks for your insight .. thought I would have to go on 'prozac' soon .. my DR has told me to 'stay on my medication and I will be fine' :)

All grins aside .. should I put back the default ::bBookmark and place the oBrw := nil and see what happends in production ??

Rick
User avatar
Rick Lipkin
 
Posts: 2618
Joined: Fri Oct 07, 2005 1:50 pm
Location: Columbia, South Carolina USA

Re: Intermittant xBrowse ADO error ( again )

Postby nageswaragunupudi » Sun Aug 09, 2009 4:35 pm

should I put back the default ::bBookmark

Better restore all codeblocks to the FWH defaults.
place the oBrw := nil


This "deceptively" seems to be the simplest solution. which we all missed earlier, because the function _FWH does not call oBrw:HandleEvent, if oBrw is not an object. This appears to solve the problem on hand.

But really this will NOT work. Even after setting oBrw := nil, the Browse Object still "exists". What we made nil is only the local variable that refers to the Browse Object. But there are other references ( in case of dialog, oBrw:aControls[n] and in case of window, one of the elements in the aWindows array ) which still retain reference this Browse object.

The _FWH function, at line 3333 calls aWindows[nAt]:HandleEvent. aWindows[nAt] still points to the same Browse Object and this call in turn calls the Browse's Paint method.

Therefore statement oBrw := nil is of no use at all and does not achieve anything.

Note: We may try this interesting test. Create a simple browse and on right click, set oBrw := nil. Even after that the browse still continues to work ( unless any of the codeblocks refers to oBrw variable ) till we close the window.

As I suggested earlier, anything we do other than what I suggested will not solve this issue.
Regards

G. N. Rao.
Hyderabad, India
User avatar
nageswaragunupudi
 
Posts: 10248
Joined: Sun Nov 19, 2006 5:22 am
Location: India

Re: Intermittant xBrowse ADO error ( again )

Postby Rick Lipkin » Sun Aug 09, 2009 5:13 pm

Gentleman ..

I have restored the origional 9.04 setado method code in xbrowse.prg and here is what the recommended code looks like .. please let me know if I have missed anything .. The code compiles and seems to run for me :

Code: Select all  Expand view

nWd := GetSysMetrics(0) * .79
nHt := GetSysMetrics(1) * .8

lOK := .F.
DEFINE ICON oICO RESOURCE "KEY"

DEFINE WINDOW oWnd1                        ;
      FROM 10,10 to nHt, nWd PIXEL         ;
      TITLE cTITLE                         ;
      MENU BuildMenu( oRsVeh )             ;
      ICON oICO                            ;
      NOMINIMIZE                           ;
      NOZOOM                               ;
      MDICHILD

@ 0, 0 xBROWSE oBrw of oWnd1               ;
       RECORDSET oRsVeh                    ;
       COLUMNS 'VNUMBER',                  ;
              'TYPE',                      ;
              'LICENSE',                   ;
              'AGENCY',                    ;
              'MEMO',                      ;
              'MAKE',                      ;
              'TYPE',                      ;
              'YEAR',                      ;
              'PROG',                      ;
              'MOTORPOOL',                 ;
              'REGION',                    ;
              'LASTSERVCE',                ;
              'NEXTSERVCE',                ;
              'NEXTMILAGE',                ;
              'SERIALNUMB',                ;
              'ACTIVE',                    ;
              'READONLY'                   ;
       COLSIZES 48,50,80,50,70,120,120,55,95,95,140,80,80,80,190,80,80  ;
       HEADERS "Vnum",                     ;
               "Type",                     ;
               "License",                  ;
               "Agency",                   ;
               "See Memo",                 ;
               "Make",                     ;
               "Model",                    ;
               "Year",                     ;
               "Program",                  ;
               "Mtrpool",                  ;
               "Location",                 ;
               "LastServ",                 ;
               "NextServ",                 ;
               "NextMilage",               ;
               "Serial#",                  ;
               "Act",                      ;
               "ReadOnly"                  ;
       AUTOSORT AUTOCOLS LINES CELL

       oBrw:bClrStd := {|| {CLR_BLACK, if( oRsVeh:Fields("readonly"):Value = 'Y', RGB(179,203,204), CLR_WHITE ) } }

     *  oBrw:CreateFromCode()
       oWnd1:oClient := oBrw

       oBrw:aCols[2]:bEditValue := { |x|DispType( oRsVeh) }
       oBrw:aCols[5]:bEditValue := { |y|DispMemo( oRsVeh) }

       oBrw:bLDblClick := { |nRow,nCol | _VehView( "V", oRsVeh ) }
       oBrw:bKeyDown   := { |nKey| _Manual( nKey,oRsVeh ) }

       oBrw:CreateFromCode()    // recommendation from daniel

ACTIVATE WINDOW oWND1 ;
         ON INIT( oBrw:SetFocus(), .F. ) ;
         VALID ( IIF( !lOK, ( oBrw:lCreated := .f., _VehClose(.T.) ), .F. ))

       *  VALID ( IIF( !lOK, ( oBrw:End(), _VehClose(.T.) ), .F. ))
       *  VALID ( IIF( !lOK, _VehClose(.T.), .F. ))

RETURN( .T. )

//------------------------
static FUNCTION _VehClose( lCLEAN )

IF lCLEAN = .T.
   lOK := .T.

   VEHNUM := oRsVEH:Fields("LICENSE"):Value
   FERASE( "C:\DBOVL\VEHNUM.MEM" )
   SAVE All like VEHNUM to C:\DBOVL\VEHNUM.MEM

  * oBRW:cALIAS:=NIL
  oBrw:End()
  oBrw := NIL

  SysReFresh() // notice here per James to trap any pending windows threads

  oRsVEH:CLose()
  oRsVeh := NIL

  _CleanUP()

ENDIF

RETURN(.T.)

//---------------------
Static Func _CleanUP()

oWnd1      := NIL
*oBRw       := NIL
*oRsVEH     := NIL
*lOK
oSay       := NIL
cSay       := NIL

RETURN(.T.)
 
User avatar
Rick Lipkin
 
Posts: 2618
Joined: Fri Oct 07, 2005 1:50 pm
Location: Columbia, South Carolina USA

Re: Intermittant xBrowse ADO error ( again )

Postby James Bott » Sun Aug 09, 2009 10:09 pm

Rao,



>But really this will NOT work. Even after setting oBrw := nil, the Browse Object still "exists".

>Create a simple browse and on right click, set oBrw := nil. Even after that the browse still continues to work

I tried this and the browse does still work after oBrw:=nil as you said. This also is news to me.

>Instead a better and simpler way is to prevent Paint method ( probably Refresh method also ) not to work once the data source is closed or made nil, by setting an appropriate flag.

>As I suggested earlier, anything we do other than what I suggested will not solve this issue.

Can we kill it if we drive a wooden stake in it's heart?

Is there no way to actually kill the browse object? Doesn't windows destroy it at some point?

I assume that this is not always a problem (with all data sources) due to lag time in execution of the paint method by the event que?

If we modify the paint method, don't we also need to do it for other data sources, like DBFs? Wouldn't we need at least two flags for each datasource type--one to flag which datatype the browse is using and another to determine if it still exists?

I also wonder, if we can't kill the oBrw object, then why can we kill the recordset object? Or is the recordset object not killed either, just erroring out?

James
User avatar
James Bott
 
Posts: 4840
Joined: Fri Nov 18, 2005 4:52 pm
Location: San Diego, California, USA

Re: Intermittant xBrowse ADO error ( again )

Postby Rick Lipkin » Sun Aug 09, 2009 10:27 pm

James, Rao

Here are my results from oBrw := NIL in this code ..

Code: Select all  Expand view

//------------------------
static FUNCTION _VehClose( lCLEAN )

IF lCLEAN = .T.
   lOK := .T.

   VEHNUM := oRsVEH:Fields("LICENSE"):Value
   FERASE( "C:\DBOVL\VEHNUM.MEM" )
   SAVE All like VEHNUM to C:\DBOVL\VEHNUM.MEM

  * oBRW:cALIAS:=NIL
  oBrw:End()
  oBrw := NIL

  SysReFresh()

  msgInfo( valtype( oBrw ) )  // U
  msginfo(obrw )                  // nil

  oRsVEH:CLose()
  oRsVeh := NIL

  _CleanUP()

ENDIF

RETURN(.T.)

 


It seems the oBrw object is 'dead' ( in my sceniro ) .. I tried it several times and the results were the same... so am I to assume that 'paint' may continue to fire intermittantly and continue to fail at run-time ?

Rick
User avatar
Rick Lipkin
 
Posts: 2618
Joined: Fri Oct 07, 2005 1:50 pm
Location: Columbia, South Carolina USA

Re: Intermittant xBrowse ADO error ( again )

Postby James Bott » Sun Aug 09, 2009 10:36 pm

Rick,

It seems the oBrw object is 'dead' ( in my sceniro ) .. I tried it several times and the results were the same... so am I to assume that 'paint' may continue to fire intermittantly and continue to fail at run-time ?


As Rao said the local oBrw is dead, but the browse still exists in oWnd in windows. Try this code, press the button, and you will be amazed that the browse is still there and working (thus the paint method can still be called).

So, we still do not have a good solution.

James
-------------------------

Code: Select all  Expand view
#include "FiveWin.ch"
#include "xbrowse.ch"

function Main()

   local oWnd, oBrw

   DEFINE Window oWnd

   define buttonbar oBar of oWnd
   define button of oBar action oBrw:=nil

   @ 0, 0 XBROWSE oBrw OF oWnd ARRAY { { "one","two","three" } } AUTOCOLS

   oBrw:CreateFromCode()

   oWnd:oClient:= oBrw

   activate window oWnd


return nil
User avatar
James Bott
 
Posts: 4840
Joined: Fri Nov 18, 2005 4:52 pm
Location: San Diego, California, USA

Re: Intermittant xBrowse ADO error ( again )

Postby Rick Lipkin » Mon Aug 10, 2009 12:33 am

James

I do confirm the browse behavior .. you can destroy the object and the browse still functions .. however when you try to close on valid .. oBrw:End() fails because the object is already destroyed..

I realize no one would probably code such a chain of events .. and you can easily modify the code as I did in the rem'd out button line to call oWnd:End() .. but I to wonder why if you destroy the oBrw object why you would NOT receive a run-time error ?? ..

Rick

Code: Select all  Expand view

// main.prg

static lOk,oBrw


#include "FiveWin.ch"
#include "xbrowse.ch"


//----------------------------
function Main()

   local oWnd

   lOK := .F.

   DEFINE Window oWnd

   define buttonbar oBar of oWnd
   define button of oBar action oBrw:=nil
  * define button of oBar action (oBrw:end(), oBrw:display(), msgInfo( valtype( oBrw ))) //,oWnd:End() ) )

   @ 0, 0 XBROWSE oBrw OF oWnd ARRAY { { "one","two","three" } } AUTOCOLS

   oBrw:CreateFromCode()

   oWnd:oClient:= oBrw

   ACTIVATE WINDOW oWND ;
         ON INIT( oBrw:SetFocus(), .F. ) ;
         VALID ( IF( !lOK, ( _CloseUM(.T.) ), .F. ))

   return nil

//------------------------
static FUNCTION _CloseUM( lCLEAN )

IF lCLEAN = .T.
   lOK := .T.

   oBrw:End()
   oBrw := NIL

  msgInfo( valtype( oBrw ) )  // U
  msginfo(obrw )              // nil


   SysReFresh()
ENDIF

RETURN(.T.)
 
User avatar
Rick Lipkin
 
Posts: 2618
Joined: Fri Oct 07, 2005 1:50 pm
Location: Columbia, South Carolina USA

Re: Intermittant xBrowse ADO error ( again )

Postby nageswaragunupudi » Mon Aug 10, 2009 1:15 am

Mr James

>>
Can we kill it if we drive a wooden stake in it's heart?
>>
Cat has 9 lives. Let us not attempt this.
The object stays till *all* references to it go out of scope. oBrw variable in our program is only one such reference. If we make it nil, we are not killing it but we are only depriving ourselves the ability to refer to the object any more in our code. The object still lives because there are other references to it.
That is the reason I suggested that this is not the right approach to solve our problem.

>>
Is there no way to actually kill the browse object? Doesn't windows destroy it at some point?
>>
When the window is closed, oBrw also is destroyed. ( Test: Modify the Method Destroy in xbrowse.prg by including MsgInfo( 'destroyed' ) at the end and observe the behavior )

Theoritically it should be possible to kill the object, if we make all the references ( oDlg:aControls[n] := nil or aWindows[nAt] := nil .. this is a static array in window.prg ) ..etc.
That means we are talking about disturbing the internal structure of the core fwh library with all undesirable consequences. Another fallout is that the object's destroy method is not called and the resources will not be released.
An object should be killed the way it is designed to be killed. Shortcutting by setting it to nil is not desirable.


>>
If we modify the paint method, don't we also need to do it for other data sources, like DBFs? Wouldn't we need at least two flags for each datasource type--one to flag which datatype the browse is using and another to determine if it still exists?
>>
My proposal is not to modify the paint method.

Please see the Display method of xbrowse
Code: Select all  Expand view
METHOD Display() CLASS TXBrowse

   if !::lCreated
      return nil
   endif

   ::BeginPaint()
   ::Paint()
   ::EndPaint()

return 0
 

WinRun and _fwh functions invoke Browse's HandleEvent method, which in turns invokes the Display method.
Right now, Display method checks if the object is created and if created calls Paint method.

As a work around, if we set oBrw:lCreated := .f., the Display method does not call Paint method and all our problems are solved. I am confident this works.

If this is tested, I would propose revised code something like this:
Code: Select all  Expand view

DATA lDataClosed INIT .f.

METHOD End() INLINE ::lDataClosed := Super:End()

METHOD Display()

   if ::lCreated .and. ! ::lDataClosed
       ::BeginPaint()
       ::Paint()
       ::EndPaint()
   endif
return nil
 


Now the programmer may call oBrw:End() and then close the recordset or dbf file whatever in the valid clause of the container window.

Alternatively, XBrowse can provide us with a DATA bOnClose, which is evaluated at the end of its Destroy method.

Then we would write oBrw:bOnClose := { || oRs:Close() } or ( | oBrw | ( oBrw:cAlias )->( DbCloseArea() ) } or { || oDbf:Close() } depending on what we are browsing.


XBrowse Code:
Code: Select all  Expand view

METHOD Destroy()
  < all other present code >
  If ::bOnClose != nil
     Eval( ::bOnClose, Self )
  endif
return Super:Destroy()
 


In this case the data source is closed only while the browse is also being destroyed.
Which approach is better, experts like Mr Antonio should decide. Personally I prefer the second method. Our browse definition is clear and no event occurs after our data source is closed and we need not clutter the valid clause of the window.
Last edited by nageswaragunupudi on Mon Aug 10, 2009 3:28 am, edited 1 time in total.
Regards

G. N. Rao.
Hyderabad, India
User avatar
nageswaragunupudi
 
Posts: 10248
Joined: Sun Nov 19, 2006 5:22 am
Location: India

Re: Intermittant xBrowse ADO error ( again )

Postby James Bott » Mon Aug 10, 2009 1:59 am

Rao,

In this case the data source is closed only while the browse is also being destroyed.
Which approach is better, experts like Mr Antonio should decide. Personally I prefer the second method. Our browse definition is clear and no event occurs after our data source is closed and we need not clutter the valid clause of the window.


I agree, I like this solution too. Antonio, what do you think?

James
User avatar
James Bott
 
Posts: 4840
Joined: Fri Nov 18, 2005 4:52 pm
Location: San Diego, California, USA

Re: Intermittant xBrowse ADO error ( again )

Postby nageswaragunupudi » Mon Aug 10, 2009 3:25 am

I would like to take this opportunity to clarify some other issues raised in this thread.

Mr Rick questioned:
why do I seem to be having these problems with xbrowse and ADO

You found this error because you tested with xbrowse and ADO. In similar circumstances, we should get errors even on DBF/TData or any other browse. This problem is not specific to ADO or XBrowse.

Mr Rick again
I am at a loss to understand why I can not get this to fail every time

This problem arises only when the browse object is required to be repainted within the very short time gap after closing the datasource and before the window is closed. Probability of such a situation is extremely rare but can not be ruled out. This is what you are experiencing.

Mr James
I also wonder, if we can't kill the oBrw object, then why can we kill the recordset object? Or is the recordset object not killed either, just erroring out?

In fact the recordset object also is not killed / destroyed even after setting oRs := nil. This recordset object still lives because the Browse Object's data oRs refers to this recordset object.
What is creating problem for Paint method of XBrowse is that the recordset is "closed".
You will agree that closing the datasource while the browse object is still alive, is like pulling out the carpet from under its feet. Isn't it unfair to the browse object?
Regards

G. N. Rao.
Hyderabad, India
User avatar
nageswaragunupudi
 
Posts: 10248
Joined: Sun Nov 19, 2006 5:22 am
Location: India

Re: Intermittant xBrowse ADO error ( again )

Postby nageswaragunupudi » Mon Aug 10, 2009 3:50 am

It may be better if we test one of the solutions and see if the problem is resolved. If Mr Rick is using 9.06, I suggest using the following code:

Separate module for derived class:
Code: Select all  Expand view

#include 'fivewin.ch'

CLASS TXbrwTest FROM TXBrowse

   DATA bOnClose

   METHOD Destroy()

ENDCLASS

METHOD Destroy() CLASS TXBrwTest

   Super:Destroy()
   if ::bOnClose != nil
      Eval( ::bOnClose, Self )
   endif

return nil
 


At the beginning of the Function Main(), insert
Code: Select all  Expand view
TXBrows( { || TXBrwTest() } )  // signals the browse system to use this child class instead of the original  txbrowse throughout the application
// Pl note .. TXBrows( .. ) not TXBrowse( .. )


Then while defining the browse
Insert this line
Code: Select all  Expand view

oBrw:bOnClose := { || _VehClose( .t. ) }
 

Please also remove all code like oBrw:End(), oBrw := nil .... etc. They just do not serve any purpose.

This can be tested for a week or so and let us observe the results. I am assuming that Mr Rick is using FWH 9.06. The code I suggested suits that version.

Best way to test any program for intermittent or very rare bugs is to let a child play with the software. Children operate the software in so many unexpected ways that we get to find bugs in the most unexpected places. I used to produce total bug-free code when my son was very young.
Regards

G. N. Rao.
Hyderabad, India
User avatar
nageswaragunupudi
 
Posts: 10248
Joined: Sun Nov 19, 2006 5:22 am
Location: India

Re: Intermittant xBrowse ADO error ( again )

Postby Enrico Maria Giordano » Mon Aug 10, 2009 12:01 pm

I completely lost the track. Any chance to see a reduced and self-contained sample showing the problem in a crystal clear way?

EMG
User avatar
Enrico Maria Giordano
 
Posts: 8315
Joined: Thu Oct 06, 2005 8:17 pm
Location: Roma - Italia

PreviousNext

Return to FiveWin for Harbour/xHarbour

Who is online

Users browsing this forum: Horizon and 87 guests