Screen freezes while my application keeps running

Hi there,

I have a rather wierd problem on my hands:

My program should do some simulation for one virtual year. Therefore for each hour of the year it runs through the same cycle with new data that apply to that point in time. I have output to my screen just to check what is happening and to disk for further analysis. While debugging I have set the output rate to one set per minute, so ample of time for the application to perform data processing.
It runs fine for more than 1200 cycles - and then the output to screen freezes but data processing and output to disk continues as if nothing had happened.

This is a windows application I buildt with Visual Studio 2017 in a windows 11 environment using the intel Fortran compiler.

I am at a loss. I do not even know which keywords to search for in this forum or in the docs / manuals.

AnyIdea anybody?

Thanks in advance

Maybe the console where you print results has a limit concerning the number of lines that can be output? Just a naive hypothesis, but testable: if you reduce the number of lines for each cycle, does the number of cycles without problem increase?

No, it is not the console that freezes, it is the windows-output. In fact, for debugging I had the console to print one line of text that was going to the screen via windws ‘TextOut’ function. The console counts on and on. The windows-screen does not.

OK, so you use a Windows GUI. Is the windows-screen scrollable?

No, it is just one screen without scrollbars, one main window with several child windows.
When my app is in the frozen mode I can minimize and maximize my main window, however after it is maximised again, it does not show any content, just a uniform background color.

You may find more help in a Windows forum. Personally, I am familiar with GTK GUI but not Windows GUI. I have just found these links about TextOut:

But if you post here a few lines of your code, maybe we will have more ideas.

Maybe have a look in Windows’ Process Explorer (Ctrl+Maj+Escape) to ensure your process does not hog all your system’s memory and/or CPU. The program may be out or ressource to update the display.

It’s difficult to give clue without knowing anything about the program. Is it an open-source code ?

Following up on other suggestions, can you cut your code down to a small, self-contained program that reproduces the problem? Doing this will let other people see if they get the same results, and it might also give you some insight into the issue.

In the meantime, my own naive questions:

What happens when you run a program that simply outputs more than 1200 sets to the screen? What if you just output the first set more than 1200 times?

How do you test to see when a minute has passed and it’s time to output another set?

Thanks for all your suggestions, I will respond to them one at a time.

@vmagnin
the windows-documentation did not provide any clue to me (of course, these were the first that I referred to).
As to the code: I do not know which one to produce. I doubt if the problem is in the paint-procedure but here is one of one of the child windows. This code just sets the background color and provides date and time as a headline. You see some printouts to console:

    case (WM_PAINT)
        if (llDiagnose) print*,'DN_StatusWndProc - WM_PAINT'    
        hDC = BeginPaint (hWnd, tPS)
        
            !**** Hintergrund einfÀrben

            irslt = SetMapMode (hDC, MM_TEXT)
            lRslt = GetClientRect (hWnd, tRect)
            hBrush = CreateSolidBrush (iiCLRNormal)
            iRslt = SelectObject (hDC, hBrush)
            lRslt = MSFWIN$RectAngle (hDC, 0, 0, tRect.Right, tRect.Bottom)                   

            !**** Datum und Uhrzeit

            iRslt = SetTextAlign (hDC, ior(TA_BOTTOM, TA_LEFT))
            iRslt = SetBkMode (hDC, TRANSPARENT)
            iRslt = MSFWIN$SetTextColor (hDC, rgb(230,230,230))
            write (cRslt,'(i6)') iiSimZeit
            if (iTag .eq. 0) then
                cRslt = ccDatumIst//'  '//ccUhrzeitIst//'   SimZeit:'//cRslt
            else
                cRslt = cTag(iTag)//'  '//ccDatumIst//'  '//ccUhrzeitIst//'   SimZeit:'//cRslt
            endif
            print *,'Zeitpunkt = ', trim (cRslt)
            lRslt = SelectObject (hDC, hhBigFont)
            iRslt = GetTextExtentPoint32 (hDC, cRslt, sizeof (trim(cRslt)),tSize)
            iX = (iWinWidth - tSize.cx) / 2
            iY = iiBigLine
            tDateRect.Left = iX
            tDateRect.Top = 0 
            tDateRect.Right = iX + tSize.cx
            tDateRect.Bottom = iY
            lRslt = TextOut (hDC, iX, iY, cRslt, sizeof (cRslt))

        lrslt = EndPaint(hDC, tPS)
        lRslt = DeleteObject (hBrush)
        DN_StatusWndProc = 0
        return

You will find two printouts to console: The first one (in the second line) was inserted to check the program flow on my console. The second one (print*,‘Zeitpunkt 
’ prints date and time to console and proves that the program is actually running and progressing through the data, even after my screen has frozen.

By the way, this is a single thread application, I split the output to screen into some childwindows just to keep the windows procedures in reasonable size.

1 Like

@ nja

This program is not a very big one. I checked with taskmanager while it was running, in normal mode and frozen mode, and found it uses about 14 MB of memory and has a CPU load of 5 % at most. This is corroborated by my process explorer. So I do not think it is short of resources.

The code is my own, so I fear I made this mistake and have to find it and cannot blame the issue on somebody else ;-).

If you print in console that lRslt, does it change after your ~1200 cycles?
The TextOut doc says:

If the function fails, the return value is zero. To get extended error information, call GetLastError.

Or the error could occur with BeginPaint:

If the function fails, the return value is NULL, indicating that no display device context is available.

@RoboNerd
Instead of cutting down my prog I tried about the same thing in a different manner. Here is the windows procedure reacting to the timer message which controls the interval of my cycles:

    case (WM_TIMER)                             ! Mit dieser Nachricht wird ein neuer Simulationsschritt angestoßen.
        if (llDiagnose) print *,'DN_DispatchwndProc: WM_TIMER'

        iiSimZeit = iiSimZeit + iiTimeStep
        if (iiSimZeit .gt. 0) iRslt = DN_Update()       ! t = 0 wird von InitSim gefĂŒllt, daher 0 ĂŒberspringen!  
 
        !**** Simulationsschritt ausfĂŒhren

        iRslt = DN_SetLoad ()                   ! EEG-Erzeugungen bestimmen
        iRslt = DN_AutoDispatch()               ! Residuallast ausgleichen
!        irslt = DN_Eval()                      ! Netzströme errechnen   
        iRslt = DN_SumUp ()                     ! Daten zusammenstellen
        nRun = nRun + 1
        iRslt = DN_OutComes(nRun)               ! Ergebnisse ausgeben                  
            
        !**** Diagramme aktualisieren
        !**** Restlast

        lRslt = InvalidateRect (hhDiaResLast, NULL, .true.)
        lRslt = UpdateWindow (hhDiaResLast)

        !**** (noch) verfĂŒgbare Leistung
  
        lRslt = InvalidateRect (hhDiaOptionen, NULL, .true.)
        lRslt = UpdateWindow (hhDiaOptionen)

        !**** SpeicherfĂŒllstĂ€nde

        lRslt = InvalidateRect (hhDiaSpeicher, NULL, .true.)
        lRslt = UpdateWindow (hhDiaSpeicher)

                   
        iRslt = SendMessage (hhStatus, WM_TIMER, wParam, lParam)              ! Weitergabe an das Status-Fenster  
        DN_DispatchWndProc = 0
        if (llDiagnose) print *,'Exit 11 DN_DispatchwndProc'
        return

The procedures doing the job are from DN_Update, which loads a new set of data from file, down to DN_Oucomes which saves the output to file, updating the windows that show some graphs on screen and forwarding the timer message to another child window, that updeates the status-information on my screen. I out-commented each and every of these procedures one after the other - and the same thing happens in all cases. While in any one setting the number of cycles performed before the screen freezes is pretty reproducible, the number of okay cycles changes when I cancel diverse procedures, but the problem occurs everytime.

So when I skipped the update function, it operates on the same data every time - and freezes my screen-output not after about 1200 cyles but after about 1500.

Oh, in fact I did not try this as yet.

Seems a good Idea. I will come back once i Have some results.

Thanks

1 Like

Here we are: It is BeginPaint() that fails with errorcode 1425. (Invalid device context (DC) handle).
But as it is quite often in life: One answer gives rise to a new question: How on earth can the DC-handle go corrupt?


        hDC = BeginPaint (hWnd, tPS)
            if (hdc .eq. 0) then
                iRslt = GetLastError()
                print *,'DiagrammWndProc: Error in Begin Paint. Code: ',iRslt
            endif


I mean, i check immediately after it was created?

Could we imagine that for some reason an EndPaint() did not occur as expected?

EndPaint releases the display device context that BeginPaint retrieved.

As far as I can see, the device context handle (hDC) isn’t corrupt; it’s zero, indicating that BeginPaint couldn’t create a device context. As to the reason for that, the window context (hWnd) might have been corrupted, or you could be allocating memory somewhere and not deleting it (although that’s unlikely).

Checking the result of BeginPaint was, of course, the right thing to do; could there be other, similar places where you should be checking returned results?

Out of curiosity, how many child windows do you have? If you use fewer child windows, does anything change? Have you tried using just one window?

You could try building a debug version and setting two breakpoints, one to stop every time BeginPaint returns anything, and another to stop when BeginPaint returns zero. Let the program hit the first breakpoint a few times, and see what the window context handle looks like. Then disable the first breakpoint, continue execution, and see what the window context handle looks like when BeginPaint fails. If the window context handle is changing when you don’t expect it to, set a watchpoint and see where that’s happening. If the handle isn’t changing, the underlying window context might be corrupted by 
 something.

A remote possibility is a timing race of some sort, but that seems unlikely given how consistent this behavior is. Still, it might be interesting to try inserting a one-second delay before calling SendMessage.

I still think it would be good to have a small program that reproduced the problem. As it is, we’re seeing code, but we’re not seeing declarations, and we’re also not seeing compiler flags. I assume you’re using “implicit none”; do you have warnings enabled and elevated to errors? Fixing warnings can be a giant nuisance, but it can also save you debugging time. Please accept my apologies if you’re already doing this.

(If an optimized build fails and a debug build runs successfully, it might be a clue that despite your best efforts, you have an uninitialized variable somewhere.)

@vmagnin
here is the windows procedure of the child window that first gets corrupted:

!--------------------------------------------------------------------------------------------------
!
!   Darstellung von Parametern als SĂ€ulendiagramm
!
!   Achtung: ttDIagramm ist fest dimensioniert. 
!   Wenn neue Digramme vorgesehen werden, dann ist die Dimension in WinData anzupassen.
!
!--------------------------------------------------------------------------------------------------

integer function DN_DiagrammWndProc (hWnd, iMessage, wParam, lParam)

    !DEC$ IF DEFINED(_X86_)
    !DEC$ ATTRIBUTES STDCALL, ALIAS : '_DN_DiagrammWndProc@16' :: DN_DiagrammWndProc
    !DEC$ ELSE
    !DEC$ ATTRIBUTES STDCALL, ALIAS : 'DN_DiagrammWndProc' :: DN_DiagrammWndProc
    !DEC$ ENDIF

use IFWIN
use DN_WinData
use DN_Data
implicit none

!**** Windows-Fenster

integer hWnd, iMessage, wParam, lParam              ! Windows-Parameter
integer hDC                                         ! Device Context


!**** Diagrammeigenschaften

integer, save :: iWinWidth, iWinHeight              ! Dimensions of the Window in pixels
integer iHSmallLine                                 ! Höhe der kleinen Schrift in Pixel
integer iHBigLine                                   ! Höhe der großen Schrift in Pixel
integer iDTop, iDBottom, iDLeft, iDRight            ! RÀnder der DiagrammflÀche
integer iDWidth, iDHeight                           ! Abmessungen der DiagrammflÀche
integer iYDist                                      ! Abstand zwischen den y-Gitterlinien
integer iXDist                                      ! Mittenabstand zwischen den SĂ€ulen; am Anfang und Ende jeweils eine halbe Distanz
integer iX, iY                                      ! allgemeine Koordinaten
integer iXLeft, iXRight, iYBottom, iYTop            ! Abmessingen eines zu zeichnenden Rechtecks
integer iY0                                         ! Position der Nullinie
integer iSWidth                                     ! Breite einer DatensÀule
integer iXSize                                      ! Breite eines Schriftzuges

integer :: iRand = 5                                ! Rand zum Diagramm (Anordnung des Steuerbuttons
integer iBtnWidth, iBtnHeight                       ! Abmessungen des Steuerknopfs
integer hBtn                                        ! Handle des Umschaltknopfs

integer, parameter :: IP_PROZENT = 2                ! Y-Achse wird in % skaliert
integer, parameter :: IP_UNIT = 1                   ! Y-Achse wird in realwert skaliert

integer, dimension (3,24) :: iColor = &           ! die Automatisch vergebenen Farben
    reshape ((/   0, 69,134,    &
                    255,066,014,    &
                    255,211,032,    &
                    087,157,028,    &
                    126,000,033,    &
                    131,202,255,    &
                    049,064,004,    &
                    174,207,000,    &
                    075,031,111,    &                                                      
                    255,149,014,    &
                    197,000,011,    &
                    000,132,209,    &
                    255,255,000,    &
                    000,255,000,    &
                    000,128,128,    &
                    255,000,255,    &
                    000,000,255,    &
                    255,000,000,    &
                    000,000,128,    &
                    000,128,000,    &
                    128,000,128,    &
                    128,000,000,    &
                    128,128,000,    &
                    128,128,128/), (/3,24/))

integer, dimension (24) :: hBrush                   ! Die Pinsel zum ausfĂŒllen der SĂ€ulen
integer hNewBrush                                   ! Pinsel mit benutzerdefinierter Farbe
logical lNewBrush                                   ! .true. - ein benutzerdefinierter Pinsel wurde definiert
integer hOldBrush                                   ! Handle zum originalen Pinsel

integer, save :: hBlackPen                                   ! Handle zu einem schwarzen Stift
integer, save :: hGreyPen                                    ! Handle zu einem grauen Stift
integer, dimension (5), save :: hUserPen                    ! Stift fĂŒr die Benutzerdefinierte Linien
integer hOldPen                                     ! Handle 7zum originalen Stift

real rScal                                          ! Skalierungsfaktor in Pixel pro Einheit

!**** Sonstiges

character*60 cRslt                                  ! Multifunktionsstring
integer iLen                                        ! LĂ€nge eines Strings
integer iPos                                        ! Position des Dezimalpunkts in cRslt

integer iRslt                                       ! multipurpose integer
integer iStyle                                      ! Windows-Style fĂŒr Steuerknopf
integer, save, dimension (4) :: iSwitch             ! Anzeiger : Darstellung in absoluten GrĂ¶ĂŸen (iProzent) oder in Prozent (iAbsolut), einer pro Diagramm
integer, parameter :: iPROZENT = 1
integer, parameter :: iABSOLUT = -1
integer iIndex                                      ! Index des Diagramms (iiDiaEEG ...)

integer j, jj                                       ! Schleifenparameter
integer, save :: jDCount                            ! DurchlaufzÀhler

logical lRslt                                       ! multipurpose integer
logical :: lIsPaint = .false.                       ! Zeigt an, dass es ein Diagramm zum Zeichnen gibt.

real rRslt                                          ! Vielzweck-Real

type (DiagramSpec) :: tDia

type (T_RECT) tRect                                 ! Rechteck-STruktur
type (T_TEXTMETRIC) tTM                             ! Struktur fĂŒr Textabmessungen
type (T_PAINTSTRUCT) tPS                            ! Paintstruct-Struktur 
type (T_SIZE) TS                                    ! Size-Struktur  

include 'resource.fd'

!--------------------------------------------------------------------------------------------------
!   hier geht es los
!--------------------------------------------------------------------------------------------------

if (llDiagnose) then
    if (llFirstRun) then
        jDCount = 1
    else
        jDCount = jDCount + 1
    endif
    print *
    print *,'Start DN_DiagrammWndProc, Aufruf Nr.:', jDCount, '  Simzeit = ', iiSimZeit
endif


select case (iMessage) 
    case (WM_CREATE)

        !**** Abmessungen des Fensters ermitteln

        lrslt = GetWindowRect (hWnd, tRect)                 
        iWinWidth = tRect.right - tRect.Left
        iWinHeight = tRect.bottom - tRect.top
        
        !**** Umschalter Prozent/Absolut einrichten
        
        iStyle = ior (WS_CHILD, WS_VISIBLE)
        iStyle = ior (iStyle, BS_PUSHBUTTON)
        hDC = CreateCompatibleDC (NULL)
        lRslt = SelectObject (hDC, tDia.hSmallFont)
        lRSlt = GetTextMetrics (hDC, tTM)
        lRslt = DeleteDC(hDC)
        iBtnHeight = tTM.tmHeight * 6 / 4
        iBtnWidth = iBtnHeight
        iX = iWinWidth - iRand - iBtnWidth
        iY = iRand
        hBtn = CreateWindow ('BUTTON','%'C, iStyle, iX, iY, iBtnWidth, iBtnHeight, hWnd,1,hhInstance,NULL)
        iRslt = SendMessage (hBtn, WM_SETFONT, tDia.hSmallFont, .true.)

        iSwitch = iABSOLUT            ! AbsolutgrĂ¶ĂŸen als Default

        !**** Pinsel vorbereiten

        do j = 1,24
            hBrush(j) = CreateSolidBrush (RGB (iColor (1,j), iColor (2,j), iColor (3,j)))
        enddo 
        hBlackPen = CreatePen (PS_SOLID, 2, RGB (0,0,0))
        hGreyPen = CreatePen (PS_SOLID, 1, RGB (180,180,180))
        if (llDIagnose) print *,'Exit 1 DN_DIagrammWndProc'
        DN_DiagrammWndProc = 0
        return 

    case (WM_COMMAND)
        if (llDiagnose) print *,'DN_DiagrammWndProc WM_COMMAND'
        select case (hiword(wParam))                        ! Aktionscode
            case (BN_CLICKED)
                iIndex = loword(wParam)
                if (hWnd .eq. hhDiaEEG) iRslt = iiDIaEEG
                if (hWnd .eq. hhDiaSpeicher) iRslt = iiDiaSpeicher
                if (hWnd .eq. hhDiaResLast) iRslt = iiDiaResLast
                if (hWnd .eq. hhDiaOptionen) iRslt = iiDiaOptionen
                iSwitch (iRslt) = iSwitch (iRslt) * (-1)
                lRslt = InvalidateRect (hWnd, NULL, .true.)
                lRslt = UpdateWindow (hWnd)
        end select
        if (llDIagnose) print *,'Exit 2 DN_DIagrammWndProc'
        DN_DiagrammWndProc = 0
        return 

    case (WM_PAINT)
        if (llDiagnose) print *,'DN_DiagrammWndProc WM_PAINT'

        hOldPen = 0
        hOldBrush = 0

        !**** FenstergrĂ¶ĂŸe ermitteln

        lrslt = GetWindowRect (hWnd, tRect)                 
        iWinWidth = tRect.right - tRect.Left
        iWinHeight = tRect.bottom - tRect.top

        !**** Diagrammdaten auswÀhlen

        if (hWnd .eq. hhDiaEEG) iIndex = iiDIaEEG
        if (hWnd .eq. hhDiaSpeicher) iIndex = iiDiaSpeicher
        if (hWnd .eq. hhDiaResLast) iIndex = iiDiaResLast
        if (hWnd .eq. hhDiaOptionen) iIndex = iiDiaOptionen

        tDia = ttDia(iIndex)
 
       !**** Schriften und Stifte auswÀhlen        

        do j = 1,tDia.iNLine
            hUserPen(j) = CreatePen (tDia.iLineStyle(j),tDia.iLineWidth(j),tDia.iLineColor(j))
        enddo

        hDC = BeginPaint (hWnd, tPS)
            if (hDC .eq. 0) then
                iRslt = GetLastError()
                print *,'DiagrammWndProc: Error in Begin Paint. Code: ',iRslt
            endif


            !**** SchriftgrĂ¶ĂŸen
 
            iRslt = SelectObject (hDC, tDia.hSmallFont)
            lrslt = GetTextMetrics (hDC, tTM)
            iHSmallLine = tTM.tmHeight
            iRslt = SelectObject (hDC, tDia.hBigFont)
            lrslt = GetTextMetrics (hDC, tTM)
            iHBigLine = tTM.tmHeight

            ! **** Diagrammabmessungen

            iDBottom = iWinHeight - iHSmallLine * 3 / 2
            iDTop = (iHBigLine + iHSmallLine) * 3 / 2
            iDLeft = iHSmallLine * 3 / 2                ! gleicher Rand wie unten
            iDRight = iWinWidth - iHSmallLine * 3 / 2   ! gleicher Rand wie unten
            iDWidth = iDRight - iDLeft
            iDHeight = iDBottom - iDTop

            !**** SĂ€ulen: Mittenabstand und Breiten 

            iXDist = nint (float (iDWidth) / float (tDia.iNValue))
            iSWidth = iXDist * 7 / 10

            !****  SĂ€ulen als Rechecke einzeichnen

            if (iSwitch (iIndex) .eq. iPROZENT) rScal = float (iDheight) / 100.0
            if (iSwitch (iIndex) .eq. iABSOLUT) rScal = float (iDheight) / (tDia.rYMax - tDia.rYMin)
!            iYBottom = iDBottom
            do j = 1, tDia.iNValue
                if (tDIa.iColor(j) .lt. 0) then                       !Verwenden der vordefinierten Pinsel, wenn der User keinen ausgewÀhlt hat.
                    iRslt = SelectObject (hDC, hBrush(j))
                    lNewBrush = .false.
                else
                    hNewBrush = CreateSolidBrush (tDia.iColor(j))
                    iRslt = SelectObject (hDC, hNewBrush)
                    if (hOldBrush .eq. 0) then
                        hOldBrush = iRslt
                    else
                        lRslt = DeleteObject (iRslt)
                    endif
                    lNewBrush = .true.
                endif
                iXLeft = iDLeft + iXDist/2 + (j - 1) * iXDist - iSWidth / 2
                iXRight = iXLeft + iSWidth
                iY0 = iDBottom - (0.0 - tDia.rYMin) * rScal
                if (iSwitch (iIndex) .eq. iABSOLUT) then
                    if (tDia.rVal(j) .ge. 0.0) then
                        iYTop = iY0
                        iYBottom = iY0 - nint (tDia.rVal(j) * rScal)
                    else
                        iYTop = iY0 - nint (tDia.rVal(j) * rScal)
                        iYBottom = iY0
                    endif
                endif
                if (iSwitch (iIndex) .eq. iPROZENT) then                    ! Vorerst nur fĂŒr positive Werte definiert
                    rRslt = tDIa.rVal(j) / tDia.rMaxVal(j) * 100.0
                    iYTop = iDBottom - nint (rRslt * rScal)
                    iYBottom = iDBottom
                endif
                lRslt = MSFWIN$RectAngle (hDC, iXLeft, iYTop, iXRight, iYBottom)
                if (lNewBrush) lRslt = DeleteObject (hNewBrush)
            enddo 

            !**** Beschriftungen
            !**** Titel

            lRslt = SelectObject (hDC, tDia.hBigFont)
            iRslt = SetTextAlign (hDC, ior(TA_BASELINE, TA_CENTER))
            iX = iWinWidth / 2
            iY = iHBigLine
            iLen = Len_Trim (tDia.cTitle)
            lRslt = Textout (hDC, iX, iY, tDia.cTitle, iLen)

            !**** Zahlenwerte

            lRslt = SelectObject (hDC, tDia.hSmallFont)
            iY = iHBigline * 3 / 2 + iHSmallLine
            do j = 1, tDia.iNValue
                cRslt = ''
                write (cRslt,'(f8.1)') tDia.rVal(j)
                cRslt = adjustl(cRslt)
                iPos = index (cRslt, '.')
                if (iPos .ne. 0) cRslt (iPos:iPos) = ','
                iLen = Len_Trim (cRslt)
                iX = iDLeft + iXDist/2 + (j - 1) * iXDist
                iRslt = TextOut(hDC, iX, iY, cRslt, iLen)
            enddo

            !**** Bezeichnungen der GrĂ¶ĂŸen

            iY = iWinHeight - iHSmallLine / 2
            do j = 1, tDia.iNValue
                iLen = Len_Trim (tDia.cValName(j))
                iX = iDLeft + iXDist/2 + (j - 1) * iXDist
                iRslt = TextOut(hDC, iX, iY, tDia.cValName(j), iLen)
            enddo

            !**** Skalierung Gitternetz

            iXSize = 0
            iYDist = nint (float (iDHeight) / float (tDia.iNYintervals))
            iRslt = SetTextAlign (hDC, ior(TA_BASELINE, TA_RIGHT))
            iX = iWinWidth - iHSmallLine / 2                                 ! ein Wenig Abstand zum Rand, etwa wie unten
            if (iSwitch(iIndex) .eq. iABSOLUT) then
                do j = 1, tDia.iNYIntervals + 1
                    rRslt = tDia.rYMin + (j - 1) * (tDia.rYMax - tDia.rYMin) / tDia.iNYIntervals 
                    cRslt = ''
                    write (cRslt,'(f8.0)') rRslt
                    cRslt = adjustl(cRslt)
                    iPos = index (cRslt, '.')
                    if (iPos .ne. 0) cRslt (iPos:iPos) = ','
                    iLen = Len_Trim (cRslt)
                    iY = iDBottom - (j - 1) * iYDist + iHSmallLine / 3 
                    iRslt = TextOut(hDC, iX, iY, cRslt, iLen)
                    lRslt = GetTextExtentPoint32 (hDC, cRslt, iLen, TS)
                    iXSize = max (iXSize, TS.cx)
                enddo
            else if (iSwitch(iIndex) .eq. iPROZENT) then
                do j = 1, 6
                    rRslt = (j - 1) * 20.0
                    cRslt = ''
                    write (cRslt,'(f8.0)') rRslt
                    cRslt = adjustl(cRslt)
                    iPos = index (cRslt, '.')
                    if (iPos .ne. 0) cRslt (iPos:iPos) = ','
                    iLen = Len_Trim (cRslt)
                    iYDist = nint (float (iDHeight) / 5.0)
                    iY = iDBottom - (j - 1) * iYDist + iHSmallLine / 3 
                    iRslt = TextOut(hDC, iX, iY, cRslt, iLen)
                    lRslt = GetTextExtentPoint32 (hDC, cRslt, iLen, TS)
                    iXSize = max (iXSize, TS.cx)
                enddo
            endif
                                    
            !**** Koordinatengitter

            jj =  tDia.iNYintervals + 1
            if (iSwitch(iIndex) .eq. iPROZENT) jj = 6
            do j = 1, jj
                iRslt = SelectObject (hDC, hGreyPen)
                if (hOldPen .eq. 0) hOldPen = iRslt
                iY = iDBottom - (j - 1) * iYDist
                lRslt = MoveToEx (hDC, iDLeft, iY, NULL)                
                lRSlt = MSFWIN$LineTo (hDC, iWinWidth - iXSize - iHSmallLine, iY)
            enddo

            !**** X-Achse

            iRslt = SelectObject (hDC, hBlackPen)
            if (hOldPen .eq. 0) hOldPen = iRslt
            lRslt = MoveToEx (hDC, iDLeft, iY0, NULL)                
            lRSlt = MSFWIN$LineTo (hDC, iWinWidth - iXSize - iHSmallLine, iY0)
        
            !**** Y-Achse

            iRslt = SelectObject (hDC, hBlackPen)
            iDTop = iDBottom - tDia.iNYIntervals * iYDist
            lRslt = MoveToEx (hDC, iDLeft, iDBottom, NULL)
            lrslt = MSFWIN$LineTo (hDC, iDLeft, iDTop) 

            !**** Benutzerdefinierte Linien - machen keinen Sinn in Prozentdarstellung

            do j = 1, tDia.iNLine
                iRslt = MSFWIN$SetTextColor (hDC, tDia.iLineColor(j))
                iLen = Len_Trim (tDia.cLineText(j))
                iY = iY0 - nint (tDia.rLineVal(j) * rScal) + iHSmallLine / 3                   
                iX = iWinWidth - iHSmallLine / 2                                 ! ein Wenig Abstand zum Rand, etwa wie unten
                iRslt = TextOut(hDC, iX, iY, tDia.cLineText(j), iLen)
                lRslt = GetTextExtentPoint32 (hDC, tDia.cLineText(j), iLen, TS)
                iXSize = TS.cx
                iRslt = SelectObject (hDC, hUserPen(j))
                if (hOldPen .eq. 0) hOldPen = iRslt
                iY = iY - iHSmallLine / 3 
                lRslt = MoveToEx (hDC, iDLeft, iY, NULL) 
                iX = iWinWidth - iXSize - iHSmallLine              
                lRSlt = MSFWIN$LineTo (hDC, iX , iY)
            enddo
        if (holdBrush .ne. 0) then 
            iRslt = SelectObject (hDC, hOldBrush)
            lrslt = DeleteObject (iRslt)
        endif
        if (hOldPen .ne. 0) then
            iRslt = SelectObject (hDC,hOldPen)
        endif
        lrslt = EndPaint(hDC, tPS)
        do j = 1,tDia.iNLine
            lRslt = DeleteObject (hUserPen(j)) 
        enddo
        if (llDIagnose) print *,'Exit 3 DN_DIagrammWndProc'
        DN_DiagrammWndProc = 0
        return 

    case (WM_DESTROY)
        if (llDiagnose) print *,'DN_DiagrammWndProc WM_DESTROY'
        do j = 1, 24
            lRslt = DeleteObject (hBrush(j))
        enddo
        lRslt = DeleteObject (hBlackPen)
        lRslt = DeleteObject (hGreyPen)
        if (llDIagnose) print *,'Exit 4 DN_DIagrammWndProc'
        DN_DiagrammWndProc = 0
        return
    
end select


DN_DiagrammWndProc = DefWindowProc (hWnd, iMessage, wParam, lParam)
if (llDiagnose) print *,'Exit 5 DN_DiagrammWndProc'

return
end

I tried to leave hDC just like when this procedure was started.

1 Like

Well I have a slight issue with some memory leak. Its four byte per cycle, so I guess it is a pointer that is not properly deallocated. I will check into this, but this seems not to be connected to my problem. After all this piece of memory was allocated prior to being used.

I have of course my main window, which holds 3 child windows left, center and right side of the screen. Within these child windows there are the grandchildren of varying numbers, 3 at most as yet not counting the controls. See my screenshot here.

During my tests i turned the child windows and my procedures off, one at a time. I wanted to see, which function / procedure would produce this issue. But the point in time varies, say from 1230 to 1500 or even about 2000 cycles but eventually in all settings output to screen happens to stop. The big window in the center - this is the German power grid - is not operational, that is, it is not updated nor handled in any way as yet. You see, this is work in progress.

For debugging I have a console window open while this app is running. and for each cycle I have date and time printed on ths console, I could see that immediately, when the error code 1425 occurs, the windows screen is frozen, that is no longer updated. And from this point onwards, all calls to begin paint for each of my child window yields hDC as 0 and produces this 1425 error code.

The value of hDC varies all the time, is not constant, even for the left window that has only one instance.hDC gives a new number every time BeginPaint is called.

Hhmm, you see, the problem occurs even when I run it at one cycle per second. Execution is well below 1/10 of a second, so I guess it is not a matter of timing, say when the timer passes an new message while the code is still running.

Not yet, but I will give it a try. I switched it off for all the ‘variable noit used’ messages coming up when compiling code that is not yet finished.

No need for apologies, surely not. There might be things I have overlooked, You see, this is a learning by doing process here. I dod spend some time on the learning curve - but still i do not consider myself a pro.

Thanks for your support
Norbert

1 Like

GUI programming is complicated. And even with experience you can stumble on bugs inside the graphical library itself


In your code, you wrote:

lrslt = EndPaint(hDC, tPS)

but in the EndPaint doc we have:

BOOL EndPaint(
  [in] HWND              hWnd,
  [in] const PAINTSTRUCT *lpPaint
);

Is it normal that you use hDC as the first argument of EndPaint, instead of hWnd?

1 Like