Announcement

Collapse
No announcement yet.

Static Variables And Memory

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Static Variables And Memory

    I'm using a lot of STATIC variables in the CALLBACK functions of my dialogs. After ending of a dialog the memory occupied by the STATIC variables normally is no more needed and should be released. But this, at least by means of DDT, is not possible. The doc says: "Static variables retain their values as long as the program is running." What is the apporiate method to use variables, which are visible throughout the CALLBAK during the lifetime of the dialog, and which can be "destroyed" after "DIOLOG END..." so that the memory is released? GlobalAlloc() / GlobalFree() is obviously not the recommended way for use in DDT CALLBACKs...
    www.praecom.de

  • #2
    Can't be done.

    The space for those variables is in your program's data segment which is TOTALLY under the control of compiler-generated code.

    But how many STATIC variables can you have that it makes a difference?

    But let's just say it does make a difference... if so, you can allocate that space yourself (here GlobalAlloc could be useful, or that new thing in 9x, whats that GLOBAL MEM ALLOC?), save the 4-byte handle to the block somewhere, and GlobalFree the block on WM_DESTROY.

    Heck, you'd have to do this anyway if you needed your dialog procedure to be re-entrant, so go ahead and do it now.

    MCM
    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      I came to these considerations when looking for memory leaks. Every time when I call a dialog containing a %LBS_NODATA listbox with an array of more than thousand items the memory occupied by the program is increasing. I checked the usual "suspects" like missing DeleteObject() but can't find a mistake. The dialog is - as most of my dialogs - situated in a DLL. Any idea?
      www.praecom.de

      Comment


      • #4
        LBS_NODATA
        Specifies a no-data list box. Specify this style when the count of items in the list box will exceed one thousand. A no-data list box must also have the LBS_OWNERDRAWFIXED style, but must not have the LBS_SORT or LBS_HASSTRINGS style.
        If you have a leak, I'll bet it's in your GDI code under WM_DRAWITEM. Code not shown.
        Michael Mattias
        Tal Systems (retired)
        Port Washington WI USA
        [email protected]
        http://www.talsystems.com

        Comment


        • #5
          Code:
          CASE %WM_DRAWITEM, %WM_MEASUREITEM
                 IF CBWPARAM = idlist THEN
                    SubclassArtilistproc GetDlgItem(CBHNDL, CBWPARAM), CBMSG, CBWPARAM, CBLPARAM
                 END IF
          
                 IF CBWPARAM = %IDC_ListSaldoLBL THEN
                    SubClassArtiSaldoTBXProc GetDlgItem(CBHNDL, CBWPARAM), CBMSG, CBWPARAM, CBLPARAM
                 END IF
          
                 IF CBWPARAM = %IDC_ListSaldo2LBL THEN
                    SubClassArtiSaldo2TBXProc GetDlgItem(CBHNDL, CBWPARAM), CBMSG, CBWPARAM, CBLPARAM
                 END IF
          Perhaps you mean the SubClassproc of the listbox?
          www.praecom.de

          Comment


          • #6
            Heere is the %WM_DRAWITEM-section of ent SubClassproc:
            Code:
            FUNCTION SubclassArtilistproc(BYVAL hWnd AS LONG, BYVAL wMsg AS LONG, _
                                          BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG
            
                LOCAL lpdis AS DRAWITEMSTRUCT PTR
                LOCAL ztxt AS ASCIIZ*%MAX_PATH
                LOCAL hPen AS DWORD,x AS DWORD
                LOCAL i AS LONG,t AS LONG,itd AS LONG
                LOCAL cnt AS LONG
                LOCAL fmt AS STRING
                LOCAL rc AS RECT
                LOCAL BkColorList,iLines,tabBMP,Topindex AS LONG
                LOCAL hBmp AS DWORD
            
                BkColorList = RGB(255, 255, 196)
                iLines = 2
            
                SELECT CASE wMsg
                   CASE %WM_DRAWITEM
                       lpdis = lParam
                       IF @lpdis.itemID = %TIME_ZONE_ID_INVALID THEN EXIT FUNCTION
            
                     SELECT CASE @lpdis.itemAction
                     CASE %ODA_DRAWENTIRE, %ODA_SELECT
                          IF @lpdis.itemID MOD 2 = 0 THEN BkColorList = RGB(192, 192, 192)
            
                          REDIM Colors(UBOUND(gTabsArtlist) + 1) AS LONG
            
                          'CLEAR BACKGROUND
                          IF (@lpdis.itemState AND %ODS_SELECTED) = 0 THEN                 'if not selected
                             SetBkColor @lpdis.hDC, BkColorList
                             SetTextColor @lpdis.hDC, GetSysColor(%COLOR_WINDOWTEXT)
                             FillRect @lpdis.hDC, @lpdis.rcItem,CreateSolidBrush(BkColorList)
                          ELSE
                             SetBkColor @lpdis.hDC, GetSysColor(%COLOR_HIGHLIGHT)             'sel text background
                             IF gWaren(@lpdis.itemID).gemerkt THEN
                                SetTextColor @lpdis.hDC,RGB(0,255,0)       'sel text color
                             ELSE
                                SetTextColor @lpdis.hDC, GetSysColor(%COLOR_HIGHLIGHTTEXT)       'sel text color
                             END IF
                             FillRect @lpdis.hDC, @lpdis.rcItem, GetSysColorBrush(%COLOR_HIGHLIGHT) 'clear background
                          END IF
            
                          IF gWaren(@lpdis.itemID).gemerkt THEN
                             FOR i = 0 TO UBOUND(Colors)
                                 Colors(i) = RGB(0,128,128)
                             NEXT i
                          ELSE
                             SetTextColor @lpdis.hDC, GetSysColor(%COLOR_WINDOWTEXT)       'text color
                          END IF
            
                          'draw gridlines between items
                          hPen = CreatePen(%PS_SOLID, 1, GetSysColor(%COLOR_3DFACE))
                          hPen = SelectObject(@lpdis.hDC, hPen)
                          MoveToEx @lpdis.hDC, 0, @lpdis.rcItem.nBottom - 1, BYVAL %NULL
                          LineTo @lpdis.hDC, @lpdis.rcItem.nRight, @lpdis.rcItem.nBottom - 1
                          DeleteObject SelectObject(@lpdis.hDC, hPen)
            
                          'GET ITEM'S TEXT AND DRAW TEXT
                          InflateRect @lpdis.rcItem, -2, -2
            
                          '1. line:
                          ztxt = PARSE$(Get_ArtiListLines(Get_Parent(hWnd),@lpdis.itemID),$CRLF,1)
            
                          IF INSTR(ztxt,"{bmp:Basissatz}") THEN
                             hbmp = LoadBitmap(GetModuleHandle(BYVAL %NULL),BYVAL MAKLNG(%IDB_BITMAP17,0)) 'B
                          ELSEIF INSTR(ztxt,"{bmp:Gebindesatz}") THEN
                             hbmp = LoadBitmap(GetModuleHandle(BYVAL %NULL),BYVAL MAKLNG(%IDB_BITMAP19,0)) 'G
                          END IF
            
                          tabBMP = Txt_Width_Pix(Get_Parent(hWnd),EXTRACT$(ztxt,$TAB))
            
                          IF Ist_Gebindesatz(gWaren(@lpdis.itemID)) THEN
                             Colors(4) = %GRAY
                          END IF
                          ListlinesClr Get_Parent(hWnd),2,ztxt,lpdis,gTabsArtlist(),Colors() 'DLLs\Dlgrout.inc
            
                          '2. line:
                          zTxt = PARSE$(Get_ArtiListLines(Get_Parent(hWnd),@lpdis.itemID),$CRLF,2)
            
                          IF Ist_Gebindesatz(gWaren(@lpdis.itemID)) THEN
                             IF gArtilistSaldo <> %SaldoArtikelAnzahl THEN
                                Colors(4) = %GRAY
                             ELSEIF gArtilistSaldo = %SaldoArtikelAnzahl THEN
                                Colors(4) = 0
                             END IF
                          END IF
            
                          CONTROL SEND Get_Parent(hWnd), GetDlgCtrlID(hWnd), %LB_GETITEMHEIGHT, 0, 0 TO i 'Get height of one line
            
                          IF hbmp THEN
                             rc.nLeft = 3 : rc.nRight = rc.nLeft + 12 'Set cordinates
                             rc.ntop = @lpdis.rcItem.ntop + i / iLines
                             rc.nbottom = @lpdis.rcItem.nbottom - 1
                             DrawState @lpdis.hDC, 0&, 0&, hbmp, 0&, rc.nLeft, _
                                          rc.nTop + 2, 0, 0, %DST_BITMAP
                             DeleteObject hbmp
                          END IF
            
                          ListlinesClr Get_Parent(hWnd),i / 2,ztxt,lpdis,gTabsArtlist(),Colors() 'DLLs\Dlgrout.inc
            
                          IF gWaren(@lpdis.itemID).DurchlPosten = "J" AND gArtilistSaldo <> %SaldoArtikelAnzahl THEN
                             hbmp = LoadBitmap(GetModuleHandle(BYVAL %NULL),BYVAL MAKLNG(%IDB_BITMAP18,0)) 'd red
                             rc.nLeft = tabBMP - 12 : rc.nRight = rc.nLeft + 12 'Set cordinates
                             rc.ntop = @lpdis.rcItem.ntop + i / iLines
                             rc.nbottom = @lpdis.rcItem.nbottom - 1
                             DrawState @lpdis.hDC, 0&, 0&, hbmp, 0&, rc.nLeft, _
                                          rc.nTop + 2, 0, 0, %DST_BITMAP
                             DeleteObject hbmp
                          END IF
            
                          'FOCUS RECT AROUND SELECTED ITEM
                          IF (@lpdis.itemState AND %ODS_SELECTED) THEN      'if selected
                             CALL DrawFocusRect(@lpdis.hDC, @lpdis.rcItem)  'draw a focus rectangle around all
                          END IF
            
                          FUNCTION = %TRUE
                          EXIT FUNCTION
            
                     CASE %ODA_FOCUS
                          DrawFocusRect @lpdis.hDC, @lpdis.rcItem 'draw focus rectangle
            
                     END SELECT
                     
                     ...
            
                END SELECT
                DIALOG GET USER hWnd, 1 TO x
                FUNCTION = CallWindowProc(x, hWnd, wMsg, wParam, lParam)
                END FUNCTION
            www.praecom.de

            Comment


            • #7
              Umm.... probably..

              That is, I think you were oin the right path looking for missing DeleteObject() calls... but without seeing the code where you Get/Create the objects to be deleted, nobody can tell if you are missing a DeleteObject().

              That is, somewhere you are doing a CreatePen(), CreateBrush() or CreateFont() ; but we can't tell where where that function is not 'paired' with a DeleteObject() for same.

              (BTW, don't call it a "subclass proc" unless the control is actually subclassed. Not that it is an error, it's just quite confusing because the term "subclass proc" is generally used only in this case).
              Michael Mattias
              Tal Systems (retired)
              Port Washington WI USA
              [email protected]
              http://www.talsystems.com

              Comment


              • #8
                Well, here's one leak...
                Code:
                FillRect @lpdis.hDC, @lpdis.rcItem,CreateSolidBrush(BkColorList)
                From that code, no way you can DeleteObject() of the brush you are creating.

                MCM
                Michael Mattias
                Tal Systems (retired)
                Port Washington WI USA
                [email protected]
                http://www.talsystems.com

                Comment


                • #9
                  Code:
                  LOCAL hBrush as long
                  hBrush = CreateSolidBrush(BkColorList)
                  FillRect @lpdis.hDC, @lpdis.rcItem,hBrush
                  DeleteObject hBrush
                  Thank's a lot! I have to change this at many points of my code.
                  www.praecom.de

                  Comment


                  • #10
                    One more thing before you go...

                    This code is incorrect:

                    Code:
                    SELECT CASE @lpdis.itemAction
                    Note in the SDK docs for DRAWITEMSTRUCT that "itemAction" can be one or more of the provided values, meaning that it's a bit mask. You must test for each value using the AND operator, like this:

                    Code:
                    IF (@lpdis.itemAction AND %ODA_FOCUS) THEN ' Draw control in focused state.
                    kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

                    Comment


                    • #11
                      Probably you are right. But almost all samples from the POFFs use it like I did, and it works.
                      www.praecom.de

                      Comment


                      • #12
                        Probably you are right. But almost all samples from the POFFs use it like I did, and it works
                        How do you know it "works" ALL THE TIME? (Answer: you don't).

                        Taking the time to "do it right" will always pay for itself in the long run.
                        Michael Mattias
                        Tal Systems (retired)
                        Port Washington WI USA
                        [email protected]
                        http://www.talsystems.com

                        Comment


                        • #13
                          I take it the question about de-allocating STATIC variables is now off the table?
                          Michael Mattias
                          Tal Systems (retired)
                          Port Washington WI USA
                          [email protected]
                          http://www.talsystems.com

                          Comment


                          • #14
                            I changed all CreateSolidBrush() as shown above (DeleteObject hbrush) but my program is still eating memory... Is it shure that there is no connection with the allocated memory for STATIC variables which cannot be released?
                            www.praecom.de

                            Comment


                            • #15
                              The most powerful debugging technique which I have used under similar circumstances is to cut out parts of the application until the problem goes away. Then I know that the last part cut out contained the problem. Then I build a miniature application with just enough functionality to demonstate the problem. If this does not lead me to the solution, then at least I have a compileable application which can be posted here for these world-class problem-solvers to get their teeth into!

                              Comment


                              • #16
                                ... Is it shure that there is no connection with the allocated memory for STATIC variables which cannot be released?
                                Yes.
                                Michael Mattias
                                Tal Systems (retired)
                                Port Washington WI USA
                                [email protected]
                                http://www.talsystems.com

                                Comment


                                • #17
                                  ...can be posted here for these world-class problem-solvers to get their teeth into...
                                  Concerning this point I tell you a short story: In August 2007 I had informed the PB-support about a bug in PBFORMS. Pretty often when you assign new accelerators to an existing menu PBFORMS uses the wrong identifyers and does not join the accelerators to the related menu. So you have to correct that manually which, in the long run, takes a lot of time. I had sent a code snippet which made the problem very clear and repeatable, and Jeff Daniels from Support said he would pass this to the R&D department. I am still waiting for an answer, exactly 455 days until now(!), and the bug has neither been fixed.

                                  But this does not belong to the current thread. Concerning the memory issue I found something interesting: The lost of memory seems to be a problem of PowerBasic rather than of my faulty code. The following snippet shows: When you call, from a console application, a windows-dialog situated in a DLL, more and more memory is lost each time you call the function in the DLL. And this without one relevant line in the called function! I have tested the memory with the "process_memoryinfo"-tool: http://www.powerbasic.com/support/pb...ad.php?t=24120

                                  Console-app:
                                  Code:
                                  #INCLUDE "WIN32API.INC"
                                  #INCLUDE "process_memoryinfo.inc"
                                  
                                  DECLARE FUNCTION ShowTOPWindow  LIB "MEMTEST.DLL" (BYVAL hParent AS DWORD) AS LONG
                                  DECLARE SUB Call_FunctionInDLL(BYVAL hPrnt AS LONG)
                                      
                                  
                                  FUNCTION WINMAIN(BYVAL hCurInstance AS LONG, _
                                                   BYVAL hPrevInstance AS LONG, _
                                                   lpszCmdLine AS ASCIIZ PTR, _
                                                   BYVAL nCmdShow AS LONG) AS LONG
                                                   
                                      Call_FunctionInDLL GetDeskTopWindow()
                                                   
                                  END FUNCTION
                                  
                                  SUB Call_FunctionInDLL(BYVAL hPrnt AS LONG)
                                      LOCAL s AS STRING, l AS LONG, i AS LONG
                                      LOCAL hFile AS LONG, szFile AS ASCIIZ * %MAX_PATH
                                      LOCAL pID   AS DWORD, sBuff AS STRING
                                      LOCAL memcnt AS LONG
                                      
                                      FOR i = 1 TO 10
                                          
                                         GOSUB memtest_1
                                          
                                         ShowTOPWindow hPrnt
                                         
                                         GOSUB memtest_2
                                         
                                      NEXT i
                                      EXIT SUB
                                      
                                          memtest_1:
                                             INCR memcnt
                                             szFile    = "Process_memory_Statistics_" + FORMAT$(memcnt) + ".txt"
                                  
                                             hFile    = FREEFILE
                                             OPEN       szFile  FOR OUTPUT AS hFile
                                             PID      = GetCurrentProcessID
                                             sBUff    = "Process Memory Statistics for Process ID " & STR$(PID) & " on " & DATE$ & " at " & TIME$
                                             PRINT      #hFile, sBUff
                                             PRINT      #hFile,
                                  
                                             L        = GetProcessMemoryStatistics (s)
                                  
                                             sBUff    = "Process Memory Statistics 1"
                                             PRINT #hFile, sBuff
                                             PRINT #hFile, s
                                             PRINT #hFile,
                                          RETURN
                                  
                                          memtest_2:
                                             L        = GetProcessMemoryStatistics (s)
                                             sBUff    = "Process Memory Statistics 2"
                                             PRINT #hFile, sBuff
                                             PRINT #hFile, s
                                             PRINT #hFile,
                                             sBuff    = "END OF REPORT"
                                             PRINT #hFile, sBuff
                                  
                                             CLOSE hFile
                                  
                                             PID = SHELLExecute (GetDesktopWindow, "open", szFile, BYVAL %NULL, BYVAL %NULL, %SW_SHOW)
                                          RETURN
                                  END SUB
                                  DLL:
                                  Code:
                                  #COMPILE DLL "\Memtest.dll"
                                  #DIM ALL
                                  
                                  FUNCTION ShowTOPWindow(BYVAL hParent AS DWORD) EXPORT AS LONG
                                     
                                      ? "ShowTOPWindow() in DLL"
                                      
                                  END FUNCTION
                                  www.praecom.de

                                  Comment


                                  • #18
                                    application, a windows-dialog situated in a DLL, more and more memory is lost each time you call the function in the DLL
                                    That is a documented bug in prior versions and the workaround for non-upgraders is in the PowerBase forum.

                                    Ooops, does not apply when statically linked.

                                    My Bad.
                                    Last edited by Michael Mattias; 20 Nov 2008, 09:30 AM.
                                    Michael Mattias
                                    Tal Systems (retired)
                                    Port Washington WI USA
                                    [email protected]
                                    http://www.talsystems.com

                                    Comment


                                    • #19
                                      This is bad news. I have replaced almost all console-emelents in my apps by windows-dialogs (located in DLLs), and the console compiler will be obsolet for me within a few months. So it's not worth to buy the current version for the short remaining time. On the other hand I have this annoying memory leak...
                                      www.praecom.de

                                      Comment


                                      • #20
                                        I can't find it but I know there is a WinApi function which gives you a count of the current GDI objects owned by your application. That would be a handy thing for you in this case.

                                        Maybe someone else remembers the name of that function.
                                        Michael Mattias
                                        Tal Systems (retired)
                                        Port Washington WI USA
                                        [email protected]
                                        http://www.talsystems.com

                                        Comment

                                        Working...
                                        X