FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin para Harbour/xHarbour oPrinter:SayBitmap/ GlobalAlloc-GlobalFree ¿fuga de memoria?
Posts: 8
Joined: Thu Dec 15, 2011 12:12 AM
oPrinter:SayBitmap/ GlobalAlloc-GlobalFree ¿fuga de memoria?
Posted: Thu Dec 15, 2011 09:23 AM
Hola a todos,

He observado que cuando pinto un bitmap (se observa mejor en los bitmaps grandes) con oPrinter:SayBitMap() de la clase TPrinter se me produce una fuga de memoria. Lo observo con un una utilidad de sistema que me va diciendo la memoria usada en %. Cuando el .bmp es pequeño, al ser un % pequeño no se observa, pero cuando se hace la operacion varias veces o el bitmap es grande entonces se ve que la memoria no es released hasta que se sale del programa.
Entiendo que este no debe ser el comportamiento de GlobalAlloc()/ GlobalFree()

La verdad es que me corre prisa solucionarlo. Agradecido de antemano siquiera por alguna pista.
Anexo metodo (original) TPrinter():SayBitMap (Fwh 7.12/ xHarbour 1.0).
Code (fw): Select all Collapse
//----------------------------------------------------------------------------//

METHOD SayBitmap( nRow, nCol, xBitmap, nWidth, nHeight, nRaster ) CLASS TPrinter

   local hDib, aBmpPal, hBitmap, hPalette

   if ::hDC = 0
      return nil
   endif

   if ( ValType( xBitmap ) == "N" ) .or. ! File( xBitmap )
      aBmpPal  = PalBmpLoad( xBitmap )
      hBitmap  = aBmpPal[ 1 ]
      hPalette = aBmpPal[ 2 ]
      hDib   = DibFromBitmap( hBitmap, hPalette )
      PalBmpFree( hBitmap, hPalette )
   else
      hDib = DibRead( xBitmap )
   endif

   if hDib == 0
      return nil
   endif

   if ! ::lMeta
      hPalette = DibPalette( hDib )
   endif

   DibDraw( ::hDCOut, hDib, hPalette, nRow, nCol,;
            nWidth, nHeight, nRaster )

   GlobalFree( hDib )

   if ! ::lMeta
      DeleteObject( hPalette )
   endif

return nil


Saludos
Posts: 8
Joined: Thu Dec 15, 2011 12:12 AM
Re: oPrinter:SayBitmap/ GlobalAlloc-GlobalFree ¿fuga de memoria?
Posted: Thu Dec 15, 2011 11:55 AM
Hola,

Dejo como test del problema el TestPrn2.Prg que demuestra lo que digo: la pérdida/ fuga de memoria. La memoria se recupera solo al salir del programa. El problema se puede comprobar más facilmente imprimiendo pocos bitmaps, cuando el bitmap es grande, o bien, si es pequeño, imprimiendo muchas veces el bitmap.

Code (fw): Select all Collapse
//----------------------------------------------------------------------------//
// Samples\TestPrn2.Prg
 function TestPerdidaDeMemoria()

   local oPrn, oFont
   local nRowStep, nColStep
   local nRow := 0, nCol := 0, n, m
   Local nMio
   
   IF !File("..\bitmaps\fivewin.bmp")
       msginfo("Se necesita ..\bitmaps\fivewin.bmp para hacer el test !!")
       return nil
   ENDIF
   
   // PrnSetSize( 2100, 1200 )     To adjust a different printer paper size!

   PRINT oPrn NAME "Testing the printer object from FiveWin" PREVIEW

      if Empty( oPrn:hDC )
         return nil          // Printer was not installed or ready
      endif

      DEFINE FONT oFont NAME "Ms Sans Serif" SIZE 0, -12 OF oPrn

      nRowStep = oPrn:nVertRes() / 20   // We want 20 rows
      nColStep = oPrn:nHorzRes() / 15   // We want 15 cols
      for nMio:= 1 to 400
      PAGE
         oPrn:SayBitmap( 1, 1, "..\bitmaps\fivewin.bmp" )
         for n = 1 to 20  // rows
             nCol = 0
             oPrn:Say( nRow, nCol, Str( n, 2 ), oFont )
             nCol += nColStep
             for m = 1 to 15
                oPrn:Say( nRow, nCol, "+", oFont )
                nCol += nColStep
             next
             nRow += nRowStep
         next
         oPrn:Line( 0, 0, nRow, nCol )
      ENDPAGE
      next
      for nMio:= 1 to 400
      PAGE
         nRow = 0
         oPrn:SayBitmap( 1, 1, "..\bitmaps\fivewin.bmp" )
         for n = 1 to 20  // rows
             nCol = 0
             oPrn:Say( nRow, nCol, Str( n + 20, 2 ), oFont )
             nCol += nColStep
             for m = 1 to 15
                oPrn:Say( nRow, nCol, "+", oFont )
                nCol += nColStep
             next
             nRow += nRowStep
         next
         oPrn:Line( 0, 0, nRow, nCol )
      ENDPAGE
      next

   ENDPRINT

   oFont:End()      // Destroy the font object

return nil


Saludos
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: oPrinter:SayBitmap/ GlobalAlloc-GlobalFree ¿fuga de memoria?
Posted: Thu Dec 15, 2011 01:41 PM
A.

Prueba asi:

Code (fw): Select all Collapse
   ...     
      aBmpPal  = PalBmpLoad( xBitmap )
      hBitmap  = aBmpPal[ 1 ]
      hPalette = aBmpPal[ 2 ]
      hDib   = DibFromBitmap( hBitmap, hPalette )
      // PalBmpFree( hBitmap, hPalette )
      DeleteObject( hBitmap )
      DeleteObject( hPalette )
      ...
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 8
Joined: Thu Dec 15, 2011 12:12 AM
Re: oPrinter:SayBitmap/ GlobalAlloc-GlobalFree ¿fuga de memoria?
Posted: Thu Dec 15, 2011 03:49 PM
Antonio,

Muchas gracias por responder. Lamentablemente no ha funcionado. Lo he probado con PalBmpRead() puesto que tengo el bitmap en disco. Aquí te dejo como ha quedado el metodo despues de las modificaciones:
Code (fw): Select all Collapse
METHOD SayBitmap( nRow, nCol, xBitmap, nWidth, nHeight, nRaster ) CLASS TPrinter

   local hDib, aBmpPal, hBitmap, hPalette

   if ::hDC = 0
      return nil
   endif

   if ( ValType( xBitmap ) == "N" ) .or. ! File( xBitmap )
      aBmpPal  = PalBmpLoad( xBitmap )
      hBitmap  = aBmpPal[ 1 ]
      hPalette = aBmpPal[ 2 ]
      hDib     = DibFromBitmap( hBitmap, hPalette )
      PalBmpFree( hBitmap, hPalette )
   else
      ******** Yo uso bitmaps desde fichero .bmp **********
//      hDib = DibRead( xBitmap )
      aBmpPal  = PalBmpRead(::hdc,  xBitmap )
      hBitmap  = aBmpPal[ 1 ]
      hPalette = aBmpPal[ 2 ]
      hDib     = DibFromBitmap( hBitmap, hPalette )
      DeleteObject(hBitMap)
      DeleteObject(hPalette)
   endif

   if hDib == 0
      return nil
   endif

   if ! ::lMeta
      hPalette = DibPalette( hDib )
   endif

   DibDraw( ::hDCOut, hDib, hPalette, nRow, nCol,;
             nWidth, nHeight, nRaster )

   GlobalFree( hDib )

   if ! ::lMeta
      DeleteObject( hPalette )
   endif

return nil


Yo no se manejar el API Win32, y por eso quiza diga una tonteria: ¿ puede ser que la memoria no se libere hasta que el dispositivo ::hdcout sea released y si el dispositivo no es released entonces por eso queda la memoria pillada ? A este tema le he dado mil vueltas, dentro de mis limitaciones, y no he conseguido hasta ahora sacarle punta.

Saludos
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: oPrinter:SayBitmap/ GlobalAlloc-GlobalFree ¿fuga de memoria?
Posted: Thu Dec 15, 2011 04:46 PM

A.

Podemos seguir haciendo otras pruebas. Comenta esta línea y prueba a ver si hay algún cambio:

// DibDraw( ::hDCOut, hDib, hPalette, nRow, nCol,;
// nWidth, nHeight, nRaster )

gracias

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 8
Joined: Thu Dec 15, 2011 12:12 AM
Re: oPrinter:SayBitmap/ GlobalAlloc-GlobalFree ¿fuga de memoria?
Posted: Thu Dec 15, 2011 04:59 PM
Antonio,

Antonio Linares wrote:A.

Podemos seguir haciendo otras pruebas. Comenta esta línea y prueba a ver si hay algún cambio:

// DibDraw( ::hDCOut, hDib, hPalette, nRow, nCol,;
// nWidth, nHeight, nRaster )
gracias


Comentando la funcion DibDraw(): NO dibuja nada y NO hay escape de memoria

Me encantaria seguir haciendo otras pruebas hasta dar con la tecla. Gracias
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: oPrinter:SayBitmap/ GlobalAlloc-GlobalFree ¿fuga de memoria?
Posted: Thu Dec 15, 2011 05:31 PM

Como compruebas la pérdida de memoria ?

El código fuente de DibDraw() está en FWH\source\winapi\dib.c pero no reserva/libera memoria.

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 8
Joined: Thu Dec 15, 2011 12:12 AM
Re: oPrinter:SayBitmap/ GlobalAlloc-GlobalFree ¿fuga de memoria?
Posted: Thu Dec 15, 2011 05:52 PM
Antonio,

La memoria, además de con GlobalMemoryStatusEx(), lo compruebo con una utilidad externa. Ambos devuelven siempre el mismo %, con lo que imagino que esta bien. En todo caso, cuando la carga de memoria es muy alta..., no se el %, entonces empieza a no pintar los bitmaps, como si no tuviese memoria... con lo que me lleva a pensar que ese % de memoria da un dato "bueno".
Code (fw): Select all Collapse
#pragma BEGINDUMP
#include "windows.h"
#include "hbapi.h"
#include "hbcomp.h"


HB_FUNC ( GLOBALMEMORYSTATUSEX  )
{
   MEMORYSTATUSEX mst;
   long nMem = hb_parnl(1);

   mst.dwLength = sizeof( MEMORYSTATUSEX );
   GlobalMemoryStatusEx( &mst );

   switch( nMem )
   {
      case 0:  hb_retnd( mst.dwMemoryLoad            ) ; break; // <- Con este compruebo la memoria usada (en %)
      case 1:  hb_retnd( mst.ullTotalPhys            ) ; break;
      case 2:  hb_retnd( mst.ullAvailPhys            ) ; break;
      case 3:  hb_retnd( mst.ullTotalPageFile        ) ; break;
      case 4:  hb_retnd( mst.ullAvailPageFile        ) ; break;
      case 5:  hb_retnd( mst.ullTotalVirtual         ) ; break;
      case 6:  hb_retnd( mst.ullAvailVirtual         ) ; break;
      case 7:  hb_retnd( mst.ullAvailExtendedVirtual ) ; break;
      default: hb_retnd( 0 ) ;
   }
}
#pragma ENDDUMP


Por decir algo: ¿ no sera que en DibDraw() algo se bloquea o queda sin release y eso impida a GlobalFree() hacer su trabajo ?
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: oPrinter:SayBitmap/ GlobalAlloc-GlobalFree ¿fuga de memoria?
Posted: Thu Dec 15, 2011 06:27 PM
A. (Antonio, Adolfo, Alfonso... ?) :-)

Prueba a implementar la función DibDraw() en tu aplicación y asi puedes hacerle modificaciones.

Añade este código al final de tu PRG principal:

Code (fw): Select all Collapse
#pragma BEGINDUMP

#include <windows.h>
#include <hbapi.h>

static LPSTR DibBits( LPBITMAPINFOHEADER lpBmp )
{
    LPSTR lpBits;
    WORD  wColors = DibNumColors( lpBmp ); // wDIBColors( lpBmp );

    lpBits  = ( (LPSTR) lpBmp ) + ( DWORD ) lpBmp->biSize;

    if( lpBmp->biSize == sizeof( BITMAPCOREHEADER ) )
       lpBits += wColors * sizeof( RGBTRIPLE );
    else
       lpBits += wColors * sizeof( RGBQUAD );

    return lpBits;
}

static BOOL DibDraw( HDC hDC, HGLOBAL hDib, WORD wCol, WORD wRow,
              HPALETTE hPalette, WORD wWidth, WORD wHeight, DWORD dwRop )
{
    LPBITMAPINFOHEADER lpBmp;
    LPSTR              lpBits;
    HPALETTE           oldPal = (HPALETTE) 0;
    HBITMAP            hBmpOld;

    lpBmp = ( LPBITMAPINFOHEADER ) GlobalLock( hDib );

    dwRop = ( dwRop == ( DWORD ) -1 ? SRCCOPY: dwRop );

    if( lpBmp )
    {
       lpBits = DibBits(lpBmp);

       if( hPalette )
       {
          oldPal = SelectPalette( hDC, hPalette, FALSE);
          RealizePalette( hDC );
       }

        if( ( wWidth == 0 ) || ( wHeight == 0 ) )
           SetDIBitsToDevice( hDC, wCol, wRow, lpBmp->biWidth,
                              lpBmp->biHeight, 0, 0, 0,
                              lpBmp->biHeight, lpBits, ( BITMAPINFO * ) lpBmp, DIB_RGB_COLORS );
        else
           StretchDIBits( hDC, wCol, wRow, wWidth, wHeight,
                          0, 0, lpBmp->biWidth, lpBmp->biHeight,
                          lpBits, ( BITMAPINFO * ) lpBmp, DIB_RGB_COLORS, dwRop );

        if( oldPal )
        {
           SelectPalette( hDC, oldPal, TRUE );
           RealizePalette( hDC );
        }

        GlobalUnlock( hDib );

        return TRUE;
    }
    return FALSE;
}

HB_FUNC( DIBDRAW ) // ( hDC, hMemBitmap, hPalette, nRow, nCol, nWidth, nHeight, nRop )
{
    HDC       hDC  = ( HDC ) hb_parnl( 1 );
    HGLOBAL   hDib = ( HGLOBAL ) hb_parnl( 2 );
    HPALETTE  hPal = ( HPALETTE ) hb_parnl( 3 );

    if( hDC && hDib )
       hb_retl( DibDraw( hDC, hDib, ( WORD ) hb_parni( 5 ), ( WORD ) hb_parni( 4 ), hPal,
                         ( WORD ) hb_parni( 6 ), ( WORD ) hb_parni( 7 ),
                         ( DWORD ) ( ( void * ) hb_parni( 8 ) != NULL ? ( DWORD ) hb_parni( 8 ): ( DWORD ) -1 ) ) );
}

#pragma ENDDUMP


Prueba a añadirlo a tu PRG y compueba si se compila bien y si funciona, gracias
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 8
Joined: Thu Dec 15, 2011 12:12 AM
Re: oPrinter:SayBitmap/ GlobalAlloc-GlobalFree ¿fuga de memoria?
Posted: Thu Dec 15, 2011 06:57 PM
A. de Antonio :-)

He puesto el codigo que me has enviado y he probado y todo sigue igual (sigue consumiendo memoria).
Luego he probado a comentar la StretchDIBits( ) y entonces he observado que NO pinta nada pero NO consume memoria.

Code (fw): Select all Collapse
    //       StretchDIBits( hDC, wCol, wRow, wWidth, wHeight, 0, 0, lpBmp->biWidth, lpBmp->biHeight,
    //                      lpBits, ( BITMAPINFO * ) lpBmp, DIB_RGB_COLORS, dwRop );
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: oPrinter:SayBitmap/ GlobalAlloc-GlobalFree ¿fuga de memoria?
Posted: Thu Dec 15, 2011 07:29 PM
Antonio,

Bueno, lo importante es que ya sabemos de donde viene el problema: función StretchDIBits().

Ahora pensemos una estrategia...

Miremos por ejemplo el código que use Wine para la función StretchDIBits():
http://cvs.winehq.org/cvsweb/wine/dlls/gdi/Attic/dib.c?rev=1.22&content-type=text/x-cvsweb-markup
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: oPrinter:SayBitmap/ GlobalAlloc-GlobalFree ¿fuga de memoria?
Posted: Thu Dec 15, 2011 07:37 PM
Buscando en google veo que otros programadores (en otros lenguajes) han tenido problemas tambien con esta función:

http://stackoverflow.com/questions/8036855/how-to-handle-gdi-resource-leak

Quizá eso nos de una pista de por donde buscar...
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: oPrinter:SayBitmap/ GlobalAlloc-GlobalFree ¿fuga de memoria?
Posted: Thu Dec 15, 2011 07:41 PM

Antonio,

Si no haces el PREVIEW (quitando esa clausula), se produce la misma pérdida ?

gracias por tus pruebas,

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 8
Joined: Thu Dec 15, 2011 12:12 AM
Re: oPrinter:SayBitmap/ GlobalAlloc-GlobalFree ¿fuga de memoria?
Posted: Thu Dec 15, 2011 07:54 PM
Antonio,

RESPUESTA 1:
Pues siento decirte que creo que NO es la StretchDIBits() porque si modifico lo que me has mandado y pongo:

Code (fw): Select all Collapse
...
//        if( ( wWidth == 0 ) || ( wHeight == 0 ) )
        if( 1 )
           SetDIBitsToDevice( hDC, wCol, wRow, lpBmp->biWidth,
                              lpBmp->biHeight, 0, 0, 0,
                              lpBmp->biHeight, lpBits, ( BITMAPINFO * ) lpBmp, DIB_RGB_COLORS );
        else
//           StretchDIBits( hDC, wCol, wRow, wWidth, wHeight,
//                          0, 0, lpBmp->biWidth, lpBmp->biHeight,
//                          lpBits, ( BITMAPINFO * ) lpBmp, DIB_RGB_COLORS, dwRop );
...


Entonces la imagen SI se pinta, sin redimensionar (utilizando SetDIBitsToDevice()), pero SI hay fuga de memoria.

¿ Sera por alguna funcion comun para SetDIBitsToDevice() y StretchDIBits() ?

RESPUESTA 2:
Linea ! digo bingo ! Sin preview NO hay fuga.
Posts: 8
Joined: Thu Dec 15, 2011 12:12 AM
Re: oPrinter:SayBitmap/ GlobalAlloc-GlobalFree ¿fuga de memoria?
Posted: Fri Dec 16, 2011 08:20 AM
Antonio,

Encontrado el problema; resulta que yo utilizo una RPreview modificada por algun compañero del foro, siento no recordar el nombre; es una RPreview que lleva una TListView con las miniaturas de las paginas. Pues bien, he puesto la preview original de fwh y YA NO HAY CONSUMO DE MEMORIA !!

Muchisimas gracias Antonio por tu ayuda, ha sido de gran valor.

Feliz Navidad !

Saludos