A little program is doing some mass updates and have, for monitoring, a short display procedure that is called at the end of a repeat block.
After running a short time and displaying 1500 it hungs, cpu uses 25 tp 29% (4 cores) and the attached debugger can't interrupt.
The workaround: Change 1500 to 100 so that this display with Process Events is called more often.
proc:
counter = counter + 1.
if counter mod 1500 = 0 then do:
pause 0 before-hide.
disp "Counter: " counter.
process events.
end.
end proc.
Win 2008R2 64bit, OE 10.2B08 32bit
Any ideas except open a WR at PSC?
Is this running in a GUI client?
It's running within the OE GUI: Standard Procedure Editor
Ah found what I was looking for.
PROCEDURE DisableProcessWindowsGhosting EXTERNAL "USER32.dll": END PROCEDURE.
Pop that in your code and run it before you run anything else. Does that help at all? Not sure if it's a problem in your OS, but it certainly helps this sort of issue in Win7.
http://knowledgebase.progress.com/articles/Article/000035144
I will try it, i forgot to write that it really hungs, not only thr gui.
promon shows one active transaction that doesn't changes, a few limbo locks and 0% I/O ...
The first test: It seems to work.
I don't understand why this helps.
I i run progetstack the last call is the procedure with the display ...
Is the gui blocking the application?
BTW: Nice KB, this function should be avoided :-)
Subsequently if a process is truly hung then this will affect application usability. In general, in ABL applications the DisableProcessWindowsGhosting function should be avoided if at all possible
[quote user="Stefan Marquardt"]BTW: Nice KB, this function should be avoided :-)[/quote]
We don't recommend using DisableProcessWindowsGhosting because it makes it more difficult for users to kill the process if it hangs. Since you said that calling PROCESS EVENTS more often avoids the problem I would recommend that you take that approach rather than using DisableProcessWindowsGhosting.
Interesting you don't recommend it. What is your solution to ghosting then? If a query takes a certain amount of time to run then you get ghosting, and the users kill the process.
[quote user="James Palmer"]
Interesting you don't recommend it. What is your solution to ghosting then? If a query takes a certain amount of time to run then you get ghosting, and the users kill the process.
[/quote]
Asynchronous AppServer calls.
Easier said than done in an application that's been around for 20 years. Yes I agree that's the ideal solution, but that takes time to implement. Time that certain managers see as unnecessary.
[quote user="James Palmer"]Interesting you don't recommend it. What is your solution to ghosting then? If a query takes a certain amount of time to run then you get ghosting, and the users kill the process.[/quote]
Ghosting occurs when a process doesn't look at the message queue for more than five seconds. PROCESS EVENTS looks at the message queue, so calling it often enough avoids ghosting. I would only use DisableProcessWindowsGhosting if I couldn't use PROCESS EVENTS.
If you want to avoid PROCESS EVENTS you could call the PeekMessage API instead to let WIndows know that you're still alive. Here's an article about that approach (not OpenEdge-centric, but still applicable):
Telling Windows we’re not really “Not Responding”
DisableProcessWindowsGhosting would be my last resort.
Matt, do you volunteer to provide the ABL syntax to declare that API call?
You successfully volunteered me. Here it is:
&SCOPED-DEFINE PM_NOREMOVE 0
PROCEDURE PeekMessageA EXTERNAL "user32.dll" :
DEFINE INPUT PARAMETER lpmsg AS MEMPTR.
DEFINE INPUT PARAMETER hWnd AS LONG.
DEFINE INPUT PARAMETER wMsgFilterMin AS LONG.
DEFINE INPUT PARAMETER wMsgFilterMax AS LONG.
DEFINE INPUT PARAMETER wRemoveMsg AS LONG.
DEFINE RETURN PARAMETER lResult AS LONG.
END PROCEDURE.
DEFINE VARIABLE Msg AS MEMPTR.
DEFINE VARIABLE lResult AS INTEGER.
SET-SIZE(Msg) = 48. /* big enough for 64-bit */
/* This call to PeekMessage will not remove posted messages from the
** message queue. The one side effect I am aware of is that messages
** that were sent from another process using SendMessage will be
** dispatched before PeekMessage returns, but the same is true of
** any code that processes messages. Since you have to process
** messages to tell Windows you're awake, this seems like an
** unavoidable (and relatively rare) side effect.
*/
RUN PeekMessageA(Msg, 0, 0, 0, {&PM_NOREMOVE}, OUTPUT lResult).
You successfully volunteered me. Here it is:
&SCOPED-DEFINE PM_NOREMOVE 0
PROCEDURE PeekMessageA EXTERNAL "user32.dll" :
DEFINE INPUT PARAMETER lpmsg AS MEMPTR.
DEFINE INPUT PARAMETER hWnd AS LONG.
DEFINE INPUT PARAMETER wMsgFilterMin AS LONG.
DEFINE INPUT PARAMETER wMsgFilterMax AS LONG.
DEFINE INPUT PARAMETER wRemoveMsg AS LONG.
DEFINE RETURN PARAMETER lResult AS LONG.
END PROCEDURE.
DEFINE VARIABLE Msg AS MEMPTR.
DEFINE VARIABLE lResult AS INTEGER.
SET-SIZE(Msg) = 48. /* big enough for 64-bit */
/* This call to PeekMessage will not remove posted messages from the
** message queue. The one side effect I am aware of is that messages
** that were sent from another process using SendMessage will be
** dispatched before PeekMessage returns, but the same is true of
** any code that processes messages. Since you have to process
** messages to tell Windows you're awake, this seems like an
** unavoidable (and relatively rare) side effect.
*/
RUN PeekMessageA(Msg, 0, 0, 0, {&PM_NOREMOVE}, OUTPUT lResult).
Flag this post as spam/abuse.
Hi Matt,
please notice that the application not only looks like that it's not responding, it really(!) never finish without using DisableProcessWindowsGhosting or more times PROCESS EVENTS.
[quote user="Stefan Marquardt"]please notice that the application not only looks like that it's not responding, it really(!) never finish without using DisableProcessWindowsGhosting or more times PROCESS EVENTS.[/quote]PeekMessage(PM_NOREMOVE) is like a very lightweight version of PROCESS EVENTS. It checks whether there's anything in the message queue but doesn't have all of the overhead of PROCESS EVENTS and doesn't dispatch any messages like PROCESS EVENTS does. You can use PeekMessage in place of PROCESS EVENTS if all you need to do is keep the application responsive.
Matt, this is really good stuff, thanks!
The issue I see with both solutions is that the now 'alive but busy' window can't regain focus once it's lost, and so becomes 'trapped' behind other windows until the window finishes up. Is there anything that can be done here? I guess it may mean processing *some* of the messages, but not others? Thoughts?
[quote user="RWEBSTER"]The issue I see with both solutions is that the now 'alive but busy' window can't regain focus once it's lost, and so becomes 'trapped' behind other windows until the window finishes up. Is there anything that can be done here? I guess it may mean processing *some* of the messages, but not others? Thoughts?[/quote]
You may be able to keep the busy window from becoming completely inactive by handling messages like WM_NCACTIVATE and the other WM_NC* messages. This is basically what SESSION:MULTITASKING-INTERVAL does. You can run the following code to see the difference. If you set MULTITASKING-INTERVAL to 0, you won't be able to bring the window to the foreground if you activate another application. If you set MULTITASKING-INTERVAL to 100 you will be able to bring the window to the foreground even though it's busy running the loop.
/* Set MULTITASKING-INTERVAL to a positive non-zero value to make
** the window respond to activation messages while it's busy. Set
** it to 0 to make the window not respond to activation messages.
*/
SESSION:MULTITASKING-INTERVAL = 100.
DEFINE VARIABLE i AS INTEGER.
DO i = 1 TO 1000000:
DISPLAY i.
END.
MULTITASKING-INTERVAL uses PeekMessage to check the message queue but, unlike the PeekMessage code I posted earlier, it actually removes some messages and handles them. MULTITASKING-INTERVAL is sort of a lighter weight, automatic version of PROCESS EVENTS.
I realize that the more I say the more confusing this is probably becoming. I'm planning to create a page on the wiki to go into this subject in more depth. Each of the methods I've talked about (PROCESS EVENTS, MULTITASKING-INTERVAL, and PeekMessage) has its place and this topic deserves a fuller treatment than I have given in this piecemeal fashion.
[quote user="Brian K. Maher"]What about using SESSION:SET-WAIT-STATE("GENERAL") ??[/quote]
SET-WAIT-STATE does two things - it disables an application's ABL windows and .NET forms so they don't accept input and it sets the mouse cursor to the hourglass. Other than those things, your program runs just as it would without SET-WAIT-STATE. It you go into the wait state and do something that takes a long time (a long loop, for example), you'll have the same issues as you would without entering the wait state.
I wonder if you good people on this thread have a solution for a somewhat related (or opposite?) issue: how to prevent the flickering when creating a dynamic dialog. Ever since the beginning of dynamic programming we have the problem that dynamic dialogs become visible too soon and therefore there is a lot of flickering going on when they are drawn. This problem has become bigger now with OpenEdge GUI for .NET when the ABL dialog is drawn over an embedded GUI window.
With windows we have no problem as we can control when they become visible. With dialogs however we have never been able to solve this.
I have tried LockWindowUpdate in the past, but that has the nasty side-effect of making the entire desktop flicker.
Are there similar solutions for this as discussed in this thread?
I guess my question is: is there an API call to temporarily prevent screen painting in an OpenEdge session?
Or any other trick to postpone realization of a dialog in OpenEdge?
When we start creating widgets we disable redrawing on the window / dialog (SendMessageA, WM_SETREDRAW, value 0).
When finished we enable redrawing and force windows to RedrawWindow (RDW_INVALIDATE + RDW_ALLCHILDREN).
That sounds what I am looking for, I will give it a try. I wonder: don't you need to pass in the HWND or something like that?
Could you post some ABL sample code?
Yes the first parameter of both APIs is HWND.
/* windows constants */ &GLOBAL-DEFINE WM_SETREDRAW 11 &GLOBAL-DEFINE RDW_INVALIDATE 0x0001 &GLOBAL-DEFINE RDW_ALLCHILDREN 0x0080 /* disable drawing */ RUN SendMessageA ( hw:HWND, {&WM_SETREDRAW}, 0, 0, OUTPUT idummy ). /* enable drawing and force redraw */ RUN SendMessageA ( hw:HWND, {&WM_SETREDRAW}, 1, 0, OUTPUT idummy ). RUN RedrawWindow ( hw:HWND, 0, 0, {&RDW_INVALIDATE} + {&RDW_ALLCHILDREN}, OUTPUT idummy ) /* external procedures */ PROCEDURE SendMessageA EXTERNAL "user32":u: DEFINE INPUT PARAMETER i_hwnd AS LONG NO-UNDO. DEFINE INPUT PARAMETER i_imsg AS LONG NO-UNDO. DEFINE INPUT PARAMETER i_lwParam AS LONG NO-UNDO. DEFINE INPUT PARAMETER i_lParam AS LONG NO-UNDO. DEFINE RETURN PARAMETER lResult AS LONG NO-UNDO. END PROCEDURE. PROCEDURE RedrawWindow EXTERNAL "user32":u: DEFINE INPUT PARAMETER i_hwnd AS LONG NO-UNDO. DEFINE INPUT PARAMETER i_lprcUpdate AS LONG NO-UNDO. DEFINE INPUT PARAMETER i_hrgnUpdate AS LONG NO-UNDO. DEFINE INPUT PARAMETER i_flags AS LONG NO-UNDO. DEFINE RETURN PARAMETER o_lsuccess AS LONG NO-UNDO. END PROCEDURE.
Thank you very much Stefan, I going to dive into this!
-peter
Hi Stefan,
Unfortunately this does not work. The problem is that in the case of a dialog, the HWND property only becomes valid AFTER the dialog becomes visible :-(.
Or am I missing something?
-peter
We create the window / dialog with visible, lock it, draw all widgets and then force the redraw. This does result in an empty window being shown, but prevents the flickerfest of widgets being added.
And since this is an Advanced Business Language you can always play around with this nonsense:
DEF VAR hw AS HANDLE NO-UNDO. DEF VAR ihwnd AS INT64 NO-UNDO. CREATE WINDOW hw ASSIGN X = -10000 Y = -10000 WIDTH-PIXELS = 1 HEIGHT-PIXELS = 1 VISIBLE = TRUE . ASSIGN ihwnd = hw:HWND hw:VISIBLE = FALSE . MESSAGE ihwnd VIEW-AS ALERT-BOX. ASSIGN hw:X = 100 hw:Y = 100 hw:WIDTH-PIXELS = 200 hw:HEIGHT-PIXELS = 200 hw:VISIBLE = TRUE . WAIT-FOR CLOSE OF hw.
That is a jolly good idea. Let me play around with it.
And since this is an Advanced Business Language you can always play around with this nonsense:
DEF VAR hw AS HANDLE NO-UNDO.
DEF VAR ihwnd AS INT64 NO-UNDO.
CREATE WINDOW hw ASSIGN
X = -10000
Y = -10000
WIDTH-PIXELS = 1
HEIGHT-PIXELS = 1
VISIBLE = TRUE
.
ASSIGN
ihwnd = hw:HWND
hw:VISIBLE = FALSE
.
MESSAGE ihwnd VIEW-AS ALERT-BOX.
ASSIGN
hw:X = 100
hw:Y = 100
hw:WIDTH-PIXELS = 200
hw:HEIGHT-PIXELS = 200
hw:VISIBLE = TRUE
.
WAIT-FOR CLOSE OF hw.
Flag this post as spam/abuse.
I stlil have the unsolved problem and more and more users complains.
This problem was introduced after moving from 10.2A to 10.2B08 and Win2003 to Win2008R2 on the citrix farm.
I have this problem running a little code in the GUI editor which updates in a loop a bunch of records. (direct on the server where the database reside)
I press "run", don't "touch" the window and it never comes back and hangup with 100% cpu use.
We can't change every line of code to avoid mass updates without calling Process events or other proposals.
It should work without a code change like before.
Stefan: Does this only happen on Citrix or does it happen on local desktops as well?
Hi, it happens on the server (local via rdp) too.
Have you tried just using Progress locally and connecting to the DB client server and see if it still happens? The reason I ask is there are several issues with Citrix/Terminal Server.
But if it happens on a normal windows desktop it could be other issues.