Is this a Vista bug?

A customer reported a crash of a VFP application while running under the new version of Windows: Vista. The customer code attempted to automate the configuration of the web server (See Automating Web Site Administration Using IIS).

Specifically, there was a scenario under which the user code calling GETOBJECT("IIS://LOCALHOST/W3SVC/1/ROOT") would crash. At the time, the app was displaying a wizard which was a Fox form with some controls on it.

So I wrote some sample code (requires IIS to be installed on WinXP, and IIS with IIS 6 Metabase compatibility on Vista, as well as “Run As Administrator”) to try to reproduce the bug on Vista.

CLEAR

FOR i = 1 TO 10000

      x=GETOBJECT("IIS://LOCALHOST/W3SVC/1/ROOT")

      ?i,x.defaultdoc

      x=0

ENDFOR

 

Of course this worked flawlessly, so the customer sent me a zip file with a VFP runtime EXE that I could run on Vista to reproduce the error. Sure enough I could reproduce the crash. However, no crash occurred when the GETOBJECT line was single stepped under the VFP debugger.

Vista is shipping soon, so I need to figure out fast if this was a Vista bug.

After some debugging, I found why it was crashing. A ListBox on the form had the ColumnCount property set to 2, indicating to VFP that there should be 2 columns displayed. However, there was only one column defined in the RowSource property. In this case VFP wasn’t doing the right thing. Simple fix on the customer side: only one column was desired, so set the ColumnCount to 1. Simple fix on the VFP side: handle the case when the ColumnCount doesn’t match the number of items in the RowSource.

I came up with some code that repros the scenario in WinXP below. If you have 2 items in the RowSource, then it works fine. If not, it causes random behavior. In my case, I get “Variable '' is not found.”

Details: internally VFP maintains an Instruction Pointer (IP) that points to the next user code to execute. When executing the GETOBJECT line, it points to the code for that line. When evaluating a RowSource, the RowSource is compiled and the IP is pointed to the compiled code temporarily, then restored. The restoration was not happening if the number of items in the RowSource didn’t match the ColumnCount.

The reason it occurred on Vista was probably because WM_PAINT messages were sent to VFP to repaint the listbox while the GETOBJECT was executing. Perhaps the GETOBJECT was doing some waiting, which would cause some thread switches and idle time, allowing apps to paint. The IP was pointing at GETOBJECT, then the WM_PAINT caused the IP to point to some temporary place, then when the GETOBJECT was done, the IP was pointing at some random place to execute.

CLOSE DATABASES all

PUBLIC ox

ox=CREATEOBJECT("test")

 

 

DEFINE CLASS test as Form

          left=200

          AllowOutput=.f.

          ADD OBJECT lst as ListBox

          ADD OBJECT tmr as timer WITH interval=1000,enabled=.t.

          PROCEDURE Load

                   CREATE CURSOR customer (Company c(10))

                   INSERT INTO customer VALUES ("One")

                   INSERT INTO customer VALUES ("Two")

          PROCEDURE init

                   WITH this.lst as ListBox

                             .Width=300

                             .RowSourceType=2 && Alias

                             .RowSource="Customer.company+' asdf ' " && notice only 1 expression

* .RowSource="Customer.company+' asdf ',4 " && This is valid

                             .columncount=2

                   ENDWITH

                   this.show

          PROCEDURE tmr.timer

                   ?PROGRAM()

                   y=NEWOBJECT("form")

                   y.visible=1

                   y.windowstate=2

                   y=0

                   x=GETOBJECT("IIS://LOCALHOST/W3SVC/1/ROOT")

                   INKEY(.1)

ENDDEFINE