Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

VFP6 - How Do You Set a NOWAIT Condition on the Default VFP _SYSMENU 3

Status
Not open for further replies.

Booleanist59

Programmer
Apr 8, 2014
15
US
I've inherited a few old applications written in VFP6 from the 90's. The _screen is referenced with no base level form in place, A bitmap background is displayed, and it uses the VFP _SYSMENU by replacing its own menu for _SYSMENU. We noticed a problem with the CPU Utilization rate - there are a lot of these apps running and drawing 25% constant CPU utilization rates even while just sitting idle which is interfering with the users' printer ques. I threw a form together with a pirated hand written menu with a NOWAIT flag and the utilization rate drops to zero.

Is there a way to just set the NOWAIT option on the default VFP _SYSMENU ?

I've been using the menu generator and copying the code from a screen based version into the a form based version with its own menu to ensure that was the reason for the CPU utilization (and it was) but these menus are complicated and tedious to adjust manually once in the form version when changes are required.

Any suggestions or info would be greatly appreciated.

Booleanist59
 
Can you share some of the code used to create the sysmenu?

I do the same in my apps and don't have such high CPU utilization. 24%. Let me guess, a 4 core processor, you utilize one core almost 100%, then.

Is any READ EVENTS in the app code?

Oh, and you may find out what really runs all the time, by using COVERAGE and log what is running for a few seconds.

Bye, Olaf.
 
Hello Olaf,

You hit the proverbial nail on the head - 25% usage on every scheduling system and they're running on a quad-core.

The original developer probably learned on FoxPro or VFP 3 in the early 90's - He uses all of the _SCREEN settings:

set sysm off
*
_screen.windowstate = 2
_screen.caption = 'Food Stamp Recertifications FSO'
_screen.closable = .f.
_screen.minbutton = .t.
_screen.maxbutton = .t.
_screen.picture = 'recertFSO.bmp'
_screen.icon = 'recertFSO.ico'
*
do MAIN.MPR

He never uses READ EVENTS.

I have a prg that sets up the environment and launches the form followed by a READ Events with all of the usual ON ERROR and ON SHUTDOWN safeguards thrown in. I built a form which uses the menu code I pirated from the Main.mpr and used it to define a rough menu in the INIT Event. I used the ACTIVATE Event of the form to SET MENU TO MYmenu NOWAIT and the utilization went to 0. The menu is pretty ugly and it's going to be impossible to maintain manually - and I've got 5 of these similar systems gumming up their printing abilities from MS Office applications. Any ideas on how to drop the CPU Utilization Rate? Have to lockdown the building in a few minutes but will try running Coverage tomorrow and see what It logs.

Any feedback always appreciated.

Thanks for your time and knowledge, my VFP is still a little rusty,

Dave
 
If there is no READ EVENTS in the original code, there are perhaps Screens with READ / READ CYCLE? Or is the app using modal forms?

What would utilize a core 100% is a DO WHILE .T. ENDDO loop, for example. And that should rather be READ EVENTS.

Andfinally MPRsaregenerated bygenmenu, you should also find a menu defined in the Other section of the project manager. An mnx stores the menu definition and you edit and maintain that, not the MPR code, the MPR code is what you should then generate when editingthe menu. In that mode VFP will have a menu menu and that'll have the genmenu item to generate the MPR.

Bye, Olaf.
 
And to be more clear about one thing: The app would immediately end, if there is nothing after DO MENU.MPR and no modal form is run. So I'd search for something inside the mpr causing a wait loop. The coverage idea would log whatever runs endless and hogs the cpu anyway, but I'd guess it's within the menu initialization code. Genmenu wouldn't generate things like that, though.

Bye, Olaf.
 
We've got over 500 employees spread out over various systems here. Given the lack of structure and design in his programs and reports, I've got my work cut out for me taming these systems and getting them to perform reliably. They've been crashing for years here and I spent the last three years straightening his systems out. I'm all for taking shortcuts or cutting corners but it's got to be bullet proof for all the users. I've never seen programs designed this poorly. I don't think the original developer ever took the course on structured design or data validation - he came from the DOS world and single user systems.

Well... so did I but most developers keep retooling every couple of years - I started programming in the '70's on a graphite card deck readers. I was a big FoxPro fan in the 80's and migrated from VFP to VB in the 90's. It looks like I'm going to have to get away from his design using a bitmap background for the screen and running the SYSMENU from it. Since the menus are so complex and the maintenance too time consuming to launch one from the form's INIT Event. I'm better off launching the standard prg to set up the environment and then launching my main form followed by the READ EVENTS in the main prg.

If you can't use a NOWAIT flag on the SYSMENU, is there any way to use the SYSMENU as the form's menu rather than just in the screen itself?

The form menu I threw together isn't as smooth as the SYSMENU in appearance or behavior. Since maintenance is the primary concern, I'm probably going to be better off scrapping the menu idea and using command buttons on the form after the user logs on. That way, the idle form maintains a 0% CPU Utilization Rate whether it's open or minimized.

Thanks for your help Olaf. If you or anyone else has any further suggestions on menus, please feel free to post them,

Dave
 
Hi Olaf,

Just noticed the second of your two reply postings. That's what I was wondering - how can SYSMENU peg the CPU Utilization when it's just a screen with a menu hanging from it after the logon form loads and disappears after logon. Think I'll backtrack into the logon form load/unload and events and/or the menu code and see if there's anything hidden in there I'm missing. The Coverage log didn't show anything unexpected but your post gave me additional "food for thought". Let you know if I find anything I really don't have time to rewrite the front end of these 5 applications unless i have to.

Thanks again for your help,

Dave
 
Just to further clarify what Olaf is saying:

You cannot add a NOWAIT to _sysmenu because it does not have any sort of wait to begin with. Something beside the menu is causing your wait state.

It may be *in* the menu's generated code, which you'll find by opening the menu in the menu designer and opening the menu menu. (Yes, it's a menu labeled menu.)
 
Thanks Dan, I'll look into that but I stumbled across something unexpected.

If I launch the app with the task manager open, it starts up and goes to 0% CPU Utilization. If I minimize the app or put the focus onto another panel, the CPU Utilization goes through the roof. Why would it spike only when the app is not currently selected.

Even opening up the Task Manager in front of it makes the app's CPU Usage spike but click on the app's screen again and the Task Manager shows it drops to 0 again. Since the Task manager stays on top of any open screen - i found i can do the same thing with the CPU Utilization Rate with all of these scheduling systems.

Any Ideas what the heck that's about?
 
Try Coverage again now that you know what action it takes to make it happen.

Tamar
 
Okay, realized that Coverage probably won't catch this.

Try using the Event Tracker so you can see what events are firing when this happens. Also consider scattering lots of DEBUGOUT commands through the application to track it down.

Tamar
 
It sounds to me like VFP is in hard-charging execution. Windows may not report that it's in hard-charging execution while the app has focus, but when the app doesn't have focus the app is in hard-charging execution and trying really hard to get focus back.

But that's just a WAG.

You need to discover where the wait state comes from. It absolutely cannot ever come from _sysmenu. (In the early days of VFP one our FAQs was how to make _sysmenu have a wait state.) It isn't in _sysmenu.

Where does it come from? Is it from an old-style read loop? Is it a DO WHILE that just won't give up? We can't answer that. But given JUST a _sysmenu, an application will start up and immediately exit. Yours isn't.

What's going on in there?
 
I'm thinking about this scenario:

As DO menu.mpr is the last line of the main.prg of the app, the wait state has to be in that mpr. It may be generated by genmenu and then modified afterwards. What if you debug the code within the IDE.
Also what you mention makes me think: There is a logon form. But where is that started? in the menu.mpr, too?

You have to find something keeping the exe running, if there is no READ, READ CYCLE, READ EVENTS, no modal form, no endless loop, the app would quit.

Bye, Olaf.
 
Appreciate all of the input - I know it's not the SYSMENU but I'm still trying to isolate it to figure out what's running. This does not happen on anu of the apps I've written with the standard base form launched from a prg and a read events statement. This is only occurring with a few apps I inherited starting with a plain screen (reference above with _screeen statements) and then the Main.mpr replaced SysMenu calling forms. No luck yet but not throwing in the towel on this one.

Not sure if this is relevant or not. This was really brought to my attention after printer que issues started cropping up in MS Office apps after we switched to a Windows 2012 64 bit R2 virtual server. I'm compiling from a WinXP work station and users are running Win7 work stations. I get the same results whether from a Win7 quad core with 25% Utilization Rate for every scheduling system I minimize that has no base form - just a screen launch. The utilization rate jumps to 99% on the WinXP workstation for the same apps - they're older Dell Optiplex 745's. Just some environment perspective to share.

I have to take a moment to ask... Hey TamarGranor, are you the Tamar E. Granor that wrote the VFP Hacker's Guide books with Ted Roche? I'm pretty sure I owe you and Ted a big thank you for helping keep my head screwed on straight (and what's left of my sanity) after this many years and versions of Foxbase - VFP7.

Again - thanks to everyone responding with suggestions - I truly appreciate your time.

Dave
 
Olaf & company,

If it weren't for the understaffing and backlog of work I'm facing, I think it might be better to redesign this system launch via a proper prg and DO FORM followed bythe READ EVENTS and closing cleanup code I usually use. I'm going to throw this whole mess out there in case somebody has the time and sees something i'm not seeing. The timer is one thing I can't fathom in a screen as opposed to a form.

I gave him an AutoUpdate framework which i use everywhere. He cut it down to about 1/10th the original size and I've included his version here. My other systems have many of the usual features you'd expect including the RUN /N WHATEVER.EXE name line so the auto updater can unload once the real exe is downloaded and launched. This is also missing all the code i use to compare and check for newer versions before launching the app. I also use version checker code for time and date comparisons to keep people from launching outdated EXEs in both the autoupdaters launching them and the main EXE repeats the code in case any wise guys make their own shortcuts or try and run from the server.

This is what I found in his auto-updater.


set talk off
set safety off

m.nFileHnd = fopen("c:\fsrecertfso\fsrecertfso.exe" , 12)

if m.nFileHnd > 0
m.lSuccess = fclose(m.nFileHnd)

if dtoc(fdate("j:\fsrecertfso\fsrecertfso.exe")) + ftime("j:\fsrecertfso\fsrecertfso.exe") <> dtoc(fdate("c:\fsrecertfso\fsrecertfso.exe")) + ftime("c:\fsrecertfso\fsrecertfso.exe")
copy file j:\fsrecertfso\fsrecertfso.exe to c:\fsrecertfso\fsrecertfso.exe
endif

do c:\fsrecertfso\fsrecertfso.exe
endif


Back to thwe opening of the actual application's prg, I repeat those lines here.
set sysm off
*
_screen.windowstate = 2
_screen.caption = 'Food Stamp Recertifications FSO'
_screen.closable = .f.
_screen.minbutton = .t.
_screen.maxbutton = .t.
_screen.picture = 'recertFSO.bmp'
_screen.icon = 'recertFSO.ico'
m.recNumber = 0
m.cSearchStr = ''
m.retvalue= ''
m.insystem = .t.
m.rdate = {}
m.cunit = ''
m.unit = ''
m.casenum = ''
m.reschDate = datetime()
m.from = datetime()
m.to = datetime()
m.name = ''


Then his timer object checks for the absence of a file I use to clear all users in case of no IT staff in the office - which is where my problem may be...

otimer = createobject("mytimer")


That timer Event uses the following code in the user event:
if file("keepthem.out")
if _screen.formcount = 0
ACTIVATE POPUP file
keyboard "{ALT+X}"
endif
for i = 1 to _screen.formcount
_screen.activeform.release
endfor
m.insystem = .f.
endif


This same timer control is used on one from from the menu, the schedule display) at 1200000 milliseconds if the user opens the schedule which should not even come into play as we're not even using the menu

The screen params fire first then the logon form is opened by the prg and if the user gets their userID and Password coerrect, it set a variable (m.insystem) and unloads. The m.insystem condition is used by the prg again after the form unloads. If it's true, you remain in the prg. It also used as a set condition to to exit the menu.ppr. Anyway -

A table is selected, a record appended, and it records a time stamp, username and time logged in.
if m.insystem
use schedule in 0 order dateunit
use resch in 0
use monthly in 0
use noshows in 0
use user in 0

sele user
append blank
* repl username with "JOHN MOJTA"
repl username with subs(sys(0), at("#", sys(0)) + 2)
repl machine with subs(sys(0), 1, at("#", sys(0)) - 2)
repl login with datetime()
repl loginid with users.username



The next exec statement in the prg is to do Main.mpr. The m.insystem value is used in decisions as you navigate through the program and is can also be reset by the Exit option of the menu to .f.

DO Main.mor
endif


After the menu exit, the prg will select the same table and record that you time stamped on your way in time stamp your time you log off. End of prg is:
close all
clear all
release all
set sysm to defa
_.screen.picture = ''
_screen.caption = 'Microsoft Visual FoxPro'


so the original developer can go back to the VP platform .


I do not see where a loop is running except the timer cycling(?) every 60 seconds.
The unsettling part of this is that there is no Read Events line anywhere.
I know from experience the original developer couldn't really use a report generator either so I can't say where his FoxPro experience left off - he was especially co-operative and was let go years ago.

FYI - I've included the following which is the entire MPR code below this point in case you're curious. Thank you for your time and any ideas greatly appreciated
:

* *********************************************************
* *
* * 04/11/14 MAIN.MPR 15:36:25
* *
* *********************************************************
* *
* * Author's Name
* *
* * Copyright (C) 2014 Company Name
* * Address
* * City, Zip
* *
* * Description:
* * This PROGRAM was automatically generated BY GENMENU.
* *
* *********************************************************


* *********************************************************
* *
* * Menu Definition
* *
* *********************************************************
*

SET SYSMENU TO
SET SYSMENU AUTOMATIC

DEFINE PAD _41j0xg8s3 OF _MSYSMENU PROMPT "\<File" COLOR SCHEME 3 ;
KEY ALT+F, ""
DEFINE PAD _41j0xg8s4 OF _MSYSMENU PROMPT "\<Utility" COLOR SCHEME 3 ;
KEY ALT+U, ""
DEFINE PAD _41j0xg8s5 OF _MSYSMENU PROMPT "\<Reports" COLOR SCHEME 3 ;
KEY ALT+R, ""
DEFINE PAD _41j0xg8s6 OF _MSYSMENU PROMPT "Repro-\<Graphics" COLOR SCHEME 3 ;
KEY ALT+G, "" ;
SKIP FOR GL_RUNLETTERS = .F.
ON PAD _41j0xg8s3 OF _MSYSMENU ACTIVATE POPUP file
ON PAD _41j0xg8s4 OF _MSYSMENU ACTIVATE POPUP utility
ON PAD _41j0xg8s5 OF _MSYSMENU ACTIVATE POPUP reports
ON PAD _41j0xg8s6 OF _MSYSMENU ACTIVATE POPUP reprograph

DEFINE POPUP file MARGIN RELATIVE SHADOW COLOR SCHEME 4
DEFINE BAR 1 OF file PROMPT "\<View Sch"
DEFINE BAR 2 OF file PROMPT "E\<xit"
ON BAR 1 OF file ACTIVATE POPUP viewschedu
ON SELECTION BAR 2 OF file m.insystem = .f.

DEFINE POPUP viewschedu MARGIN RELATIVE SHADOW COLOR SCHEME 4
DEFINE BAR 1 OF viewschedu PROMPT "Unit B\<C F/S Dept"
DEFINE BAR 2 OF viewschedu PROMPT "Unit B\<E"
DEFINE BAR 3 OF viewschedu PROMPT "Unit B\<G"
ON SELECTION BAR 1 OF viewschedu ;
DO _41j0xg8s7 ;
IN LOCFILE("FSRECERTSFSO\MAIN" ,"MPX;MPR|FXP;PRG" ,"WHERE is MAIN?")
ON SELECTION BAR 2 OF viewschedu ;
DO _41j0xg8s8 ;
IN LOCFILE("FSRECERTSFSO\MAIN" ,"MPX;MPR|FXP;PRG" ,"WHERE is MAIN?")
ON SELECTION BAR 3 OF viewschedu ;
DO _41j0xg8sj ;
IN LOCFILE("FSRECERTSFSO\MAIN" ,"MPX;MPR|FXP;PRG" ,"WHERE is MAIN?")

DEFINE POPUP utility MARGIN RELATIVE SHADOW COLOR SCHEME 4
DEFINE BAR 1 OF utility PROMPT "\<Make Schedule" ;
SKIP FOR GL_MAKESCHED = .F.
DEFINE BAR 2 OF utility PROMPT "Select \<Printer"
DEFINE BAR 3 OF utility PROMPT "Re\<index"
ON SELECTION BAR 1 OF utility do form makesch
ON SELECTION BAR 2 OF utility set printer to name getprinter()
ON SELECTION BAR 3 OF utility do form reindx

DEFINE POPUP reports MARGIN RELATIVE SHADOW COLOR SCHEME 4
DEFINE BAR 1 OF reports PROMPT "\<Schedules"
DEFINE BAR 2 OF reports PROMPT "\<Reschedules"
DEFINE BAR 3 OF reports PROMPT "\<No Shows"
DEFINE BAR 4 OF reports PROMPT "\<Case Numbers"
DEFINE BAR 5 OF reports PROMPT "\<Homeless List"
ON SELECTION BAR 1 OF reports do form prtsch
ON SELECTION BAR 2 OF reports do form prtresch
ON SELECTION BAR 3 OF reports do form prtnshow
ON SELECTION BAR 4 OF reports do form numsumm
ON SELECTION BAR 5 OF reports do form homelist

DEFINE POPUP reprograph MARGIN RELATIVE SHADOW COLOR SCHEME 4
DEFINE BAR 1 OF reprograph PROMPT "\<Letters"
DEFINE BAR 2 OF reprograph PROMPT "\<Archive Addresses"
DEFINE BAR 3 OF reprograph PROMPT "\<Reprint Letters"
ON SELECTION BAR 1 OF reprograph do form prtlet
ON SELECTION BAR 2 OF reprograph do prg_archiveaddrs
ON SELECTION BAR 3 OF reprograph do form prtltr


* *********************************************************
* *
* * Cleanup Code & Procedures
* *
* *********************************************************
*

do while m.insystem
activate menu _msysmenu
enddo

* *********************************************************
* *
* * _41J0XG8S7 ON SELECTION BAR 1 OF POPUP viewschedu
* *
* * Procedure Origin:
* *
* * From Menu: MAIN.MPR, Record: 7
* * Called By: ON SELECTION BAR 1 OF POPUP viewschedu
* * Prompt: Unit BC F/S Dept
* * Snippet: 1
* *
* *********************************************************
*
PROCEDURE _41j0xg8s7
sele schedule
set order to tag dateunit
m.cUnit = "BC"
set filter to unit = m.cUnit
go top
if eof()
messageBox("No appointments scheduled for this unit yet", 16, "Error Message")
set filter to
return
endif

m.rdate = dateX
do form sch


* *********************************************************
* *
* * _41J0XG8S8 ON SELECTION BAR 2 OF POPUP viewschedu
* *
* * Procedure Origin:
* *
* * From Menu: MAIN.MPR, Record: 8
* * Called By: ON SELECTION BAR 2 OF POPUP viewschedu
* * Prompt: Unit BE
* * Snippet: 2
* *
* *********************************************************
*
PROCEDURE _41j0xg8s8
sele schedule
set order to tag dateunit
m.cUnit = "BE"
set filter to unit = m.cUnit
go top
if eof()
messageBox("No appointments scheduled for this unit yet", 16, "Error Message")
set filter to
return
endif

m.rdate = dateX
do form sch


* *********************************************************
* *
* * _41J0XG8SJ ON SELECTION BAR 3 OF POPUP viewschedu
* *
* * Procedure Origin:
* *
* * From Menu: MAIN.MPR, Record: 9
* * Called By: ON SELECTION BAR 3 OF POPUP viewschedu
* * Prompt: Unit BG
* * Snippet: 3
* *
* *********************************************************
*
PROCEDURE _41j0xg8sj
sele schedule
set order to tag dateunit
m.cUnit = "BG"
set filter to unit = m.cUnit
go top
if eof()
messageBox("No appointments scheduled for this unit yet", 16, "Error Message")
set filter to
return
endif

m.rdate = dateX
do form sch
 
Here's your problem. In the menu cleanup code:

Code:
do while m.insystem
activate menu _msysmenu
enddo

That's what the programmer used instead of READ EVENTS. Get rid of it and add READ EVENTS.

Tamar

P.S. Yes, I'm that Tamar Granor. (In fact, to the best of my knowledge, I'm the only Tamar Granor in the world.) Glad we were able to help you.
 
Ok,

suspicion confirmed. A tight DO WHILE ENDDO loop running endless does utilize a CPU core to 100%. This is simply nonsense programming.
Activate menu _msysmenu isn't even needed once.

This is not what genmenu generates, it's been manually added, but there is feature of the menu designer to do so and also to remove that nonsense again:

Modify the menu of the project, then choose "General Options" from the View menu. In menu code select Cleanup, this will display that cleanup code. This is not intended for an endless loop. It's intended for code to run at the end of the menu generation, obviously, eg to release some objects you created in the Setup code you can also add at the General Options.

So after mending that you can stay with the menu definition and designer. Just don't add such nonsense again.
Put a READ EVENTS after the DO Menu.mpr and that's mainly it. Obviously you need some menu item causing CLEAR EVENTS or at least QUIT.

Bye, Olaf.
 
And one more question: The coverage log did not show these three lines running over and over again all the time?

Bye, Olaf.

 
Activate Menu _Msysmenu is actually a wait state (which is why there's also a NOWAIT clause), but the only time it should be used is when you want to force re-evaluation of menu Skip For clauses. Activating the menu prevents timers from firing.

As others have said, replace that code with Read Events (and, of course, Clear Events where needed).

You have my sympathy dealing with code like this. I recently started a gig where they had a self-taught programmer who wasn't much of a teacher. She was incredibly prolific but closed to suggestion. She was let go around 2004 and in the intervening years the prevailing sentiment was "don't touch it, it might break".

One process, which runs at night by button-pusher operators, was ~8,000 lines of code, no error handler, weak organization, not a single UDF and not a RETURN statement in the lot. She didn't understand loops, arrays, or date math. This process ran for four hours every night and was error prone (with no error handler so somebody's phone would ring).

I've reorganized the code, fixed the glaring deficiencies, and it does everything it used to do. It runs in 12 minutes and hasn't seen an error in over a month.

It's gratifying work, but boy is it draining!
 
>Activate Menu _Msysmenu is actually a wait state

I didn't know that, but of course the purpose of the genmenu code is to start the menu and also show it, so Activate Menu _Msysmenu is not needed at all and surely not as "cleanup code".
If it's a wait state, then why does it utilize a cpu core to 100%, though? It's used there without the nowait clause. Without testing, I'd rather say the while loop keeps it alive.

@Booleanist59: One thing you obviously also need to do after removing the while loop is using genmenu to recreate the menu.mpr without these lines. You can also modify the mpr itself, but I think you should maintain the menu and gain being able to modify it easier then editing the mpr code.

And as Dan said, you have our sympathy for such a hard to find fault.

One thing to notice:
Code:
...
ON SELECTION BAR 2 OF reprograph do prg_archiveaddrs
ON SELECTION BAR 3 OF reprograph do form prtltr


* *********************************************************
* * 
* * Cleanup Code & Procedures 
* * 
* *********************************************************
*

do while m.insystem
activate menu _msysmenu
enddo

The comment header makes it seem like the "Cleanup Code" is it's own procedure, but it runs right after the last ON SELECTION is defined. It's not code running at a destroy event of the menu, there is actually no such thing for menus.

Bye, Olaf.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top