Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

store images in a SQLite database, using GDI+

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

  • store images in a SQLite database, using GDI+

    Code:
    #COMPILE EXE
    #DIM ALL
    ' This application is just to try out storing images in a SQLite database
    ' and uses the GDIplus library to manipulate the images. It's not a
    ' masterclass, more of an exploration!
    '
    ' How to use
    ' 1. download the sqlite3.dll from the downloads page
    '     at www.sqlite.org into the same directory as the executable.
    ' 2. Change the database path in the PBMAIN to suit your system.
    '     It has to be the full path.
    ' 3. Compile under PB Windows 8.04
    '
    ' tested on Windows XP
    '
    ' May be used or copied at your own risk.
    '
    '       PLAN B
    '       -------
    'FILE            <<<<<<< APP >>>>>>>       DB      <<<<<<<<<<< APP >>>>>>>>>>
    'image file -> -------------------------> blob ---------> temp image file
    '     |                                                        |
    '     |                                                     istream
    '     |                                                        |
    '     +------------------> GDI+ image <------------------------+
    '
    ' we're on PLAN B!!
    '
    ' It would be good to get rid of the temporary file and load direct from the blob
    ' into the stream, but I don't know how to - suggestions welcome!
    '
    #INCLUDE "WIN32API.INC"
    #INCLUDE "comdlg32.inc"
    #INCLUDE "sqlite3.inc"
    %IDD_DIALOG1      =  101
    %IDC_getpic_bn    = 1004
    %IDC_picDB_bn     = 1005
    %IDC_savepicDB_bn = 1006
    %IDC_LABEL3       = 1007
    %IDC_LABEL4       = 1008
    %IDC_LISTBOX1     = 1009
    '------------------------------------------------------------------------------
    ' GDI+ status (error) codes
    %StatusOk = 0
    %StatusGenericError = 1
    %StatusInvalidParameter = 2
    %StatusOutOfMemory = 3
    %StatusObjectBusy = 4
    %StatusInsufficientBuffer = 5
    %StatusNotImplemented = 6
    %StatusWin32Error = 7
    %StatusWrongState = 8
    %StatusAborted = 9
    %StatusFileNotFound = 10
    %StatusValueOverflow = 11
    %StatusAccessDenied = 12
    %StatusUnknownImageFormat = 13
    %StatusFontFamilyNotFound = 14
    %StatusFontStyleNotFound = 15
    %StatusNotTrueTypeFont = 16
    %StatusUnsupportedGdiplusVersion = 17
    %StatusGdiplusNotInitialized = 18
    %StatusPropertyNotFound = 19
    %StatusPropertyNotSupported = 20
    '---------------------------------------------------------------------
    TYPE GdiplusStartupInput
       GdiplusVersion AS DWORD             '// Must be 1
       DebugEventCallback AS DWORD         '// Ignored on free builds
       SuppressBackgroundThread AS LONG    '// FALSE unless you're prepared to call
                                           '// the hook/unhook functions properly
       SuppressExternalCodecs AS LONG      '// FALSE unless you want GDI+ only to use
                                           '// its internal image codecs.
    END TYPE
    '---------------------------------------------------------------------
    '
    TYPE GdiplusStartupOutput
    '  // The following 2 fields are NULL if SuppressBackgroundThread is FALSE.
    '  // Otherwise, they are functions which must be called appropriately to
    '  // replace the background thread.
    '  //
    '  // These should be called on the application's main message loop - i.e.
    '  // a message loop which is active for the lifetime of GDI+.
    '  // "NotificationHook" should be called before starting the loop,
    '  // and "NotificationUnhook" should be called after the loop ends.
       NotificationHook AS DWORD
       NotificationUnhook AS DWORD
    END TYPE
    '
    DECLARE FUNCTION GdipDrawImageRectI LIB "gdiplus.dll" ALIAS "GdipDrawImageRectI" (BYVAL graphics AS LONG, BYVAL nImage AS LONG, BYVAL x AS LONG, BYVAL y AS LONG, BYVAL nWidth AS LONG, BYVAL Height AS LONG) AS LONG
    DECLARE FUNCTION GdipDeleteGraphics LIB "gdiplus.dll" ALIAS "GdipDeleteGraphics" (BYVAL graphics AS LONG) AS LONG
    DECLARE FUNCTION GdipCreateFromHDC LIB "gdiplus.dll" ALIAS "GdipCreateFromHDC" (BYVAL hdc AS LONG, graphics AS LONG) AS LONG
    DECLARE FUNCTION GdipGetImagePixelFormat LIB "gdiplus.dll" ALIAS "GdipGetImagePixelFormat" _
                (BYVAL nImage AS LONG, PixelFormat AS LONG) AS LONG
    DECLARE FUNCTION GdipGetImageDimension LIB "gdiplus.dll" ALIAS "GdipGetImageDimension" _
                (BYVAL nImage AS LONG, nWidth AS SINGLE, Height AS SINGLE) AS LONG
    DECLARE FUNCTION GdiplusStartup LIB "GDIPLUS.DLL" ALIAS "GdiplusStartup" _
                (token AS DWORD, inputbuf AS GdiplusStartupInput, outputbuf AS GdiplusStartupOutput) AS LONG
    DECLARE SUB GdiplusShutdown LIB "GDIPLUS.DLL" ALIAS "GdiplusShutdown" _
                (BYVAL token AS DWORD)
    DECLARE FUNCTION GdipLoadImageFromFile LIB "GDIPLUS.DLL" ALIAS "GdipLoadImageFromFile" _
                (BYVAL flname AS STRING, lpImage AS DWORD) AS LONG
    DECLARE FUNCTION GdipGraphicsClear LIB "gdiplus.dll" ALIAS "GdipGraphicsClear" _
                (BYVAL graphics AS LONG, BYVAL lColor AS LONG) AS LONG
    DECLARE FUNCTION GdipDisposeImage LIB "GDIPLUS.DLL" ALIAS "GdipDisposeImage" _
                (BYVAL lpImage AS DWORD) AS LONG
    DECLARE FUNCTION GdipLoadImageFromStream LIB "gdiplus.dll" ALIAS "GdipLoadImageFromStream" _
                (BYVAL pStream AS DWORD, BYREF pImage AS DWORD) AS LONG
    
    DECLARE SUB skIconise (BYVAL xPic AS LONG, BYVAL yPic AS LONG,            _ picture dimensions
                   BYVAL xCell AS LONG, BYVAL yCell AS LONG,          _ FRAME dimensions
                   BYREF xOfs AS LONG, BYREF yOfs AS LONG,            _ calc'd offset in frame
                   BYREF xSize AS LONG, BYREF ySize AS LONG)   ' thumbnail dimensions
    
    DECLARE FUNCTION CreateStreamOnHGlobal LIB "ole32.dll" ALIAS "CreateStreamOnHGlobal" _
            (BYVAL hGlobal AS DWORD, BYVAL fDeleteOnRelease AS DWORD, pstm AS DWORD) AS LONG
    
    GLOBAL gsPicPath    AS STRING ' current image file
    GLOBAL gszDBpath    AS ASCIZ * 256 ' DB path must be full path! initialised in PBMAIN
    GLOBAL ghDB         AS DWORD ' PIX database handle
    GLOBAL ghPicWnd     AS DWORD ' handle of image dialog
    GLOBAL ghImage      AS DWORD ' handle of current image
    '
    TYPE process_memory_counters
       cb                         AS DWORD   '// size of the structure, in bytes.
       pagefaultcount             AS DWORD   '// number of page faults.
       peakworkingsetsize         AS DWORD   '// peak working set size.
       workingsetsize             AS DWORD   '// current working set size.
       quotapeakpagedpoolusage    AS DWORD   '// peak paged pool usage.
       quotapagedpoolusage        AS DWORD   '// current paged pool usage.
       quotapeaknonpagedpoolusage AS DWORD   '// peak nonpaged pool usage.
       quotanonpagedpoolusage     AS DWORD   '// current nonpaged pool usage.
       pagefileusage              AS DWORD   '// current space allocated for the pagefile.
                                             '// those pages may or may not be in memory.
       peakpagefileusage          AS DWORD   '// peak space allocated for the pagefile.
    END TYPE
    
    
    DECLARE FUNCTION GetProcessMemoryInfo LIB "PSAPI.DLL" ALIAS "GetProcessMemoryInfo" ( _
            BYVAL hProcess AS DWORD, _
            ppsmemCounters AS PROCESS_MEMORY_COUNTERS, _
            BYVAL cb AS DWORD _
    ) AS LONG
    '---------------------------------------------------------------------------------
    FUNCTION CurrentProcessMemory() AS LONG
        LOCAL tMC AS Process_Memory_Counters
    
       tMC.cb = SIZEOF(tMC)
       IF GetProcessMemoryInfo(GetCurrentProcess, BYVAL VARPTR(tMC), SIZEOF(tMC)) THEN
           FUNCTION = tMC.WorkingSetSize
       ELSE
           FUNCTION = -1
       END IF
    END FUNCTION
    '--------------------------------------------------------------------
    TYPE tQuerystuff ' query attributes
    '    hDB                     AS DWORD       ' database handle
        hWnd                    AS DWORD       ' window handle
        pszSQL                  AS ASCIZ PTR   ' pointer to query string
        nQrows                  AS LONG        ' # of rows returned by last query
        nQcols                  AS LONG        ' # of columns returned by last query
        pQresults               AS DWORD       ' ptr to array of ptrs provided by sqlite3_get_table
        pQzErms                 AS ASCIZ PTR   ' ptr to sqlite3 error message
        selection               AS STRING * 1024 ' string of selected data
        pBlobData               AS DWORD       ' ptr to blob data in application (not in SQLite)
        lBlobBuffersize         AS LONG        ' length of blob buffer
    END TYPE
    '-------------------------------------------------------------------------------------------------------
    ' Computes location and size to stretch a bitmap preserving its aspect.
    '
    SUB skIconise (BYVAL xPic AS LONG, BYVAL yPic AS LONG,            _ picture dimensions
                   BYVAL xCell AS LONG, BYVAL yCell AS LONG,          _ FRAME dimensions
                   BYREF xOfs AS LONG, BYREF yOfs AS LONG,            _ calc'd offset in frame
                   BYREF xSize AS LONG, BYREF ySize AS LONG) EXPORT   ' thumbnail dimensions
      LOCAL SCALE AS SINGLE
    
        IF xPIC& THEN scale! = xCell& / xPic&
        IF scale! > 1 THEN scale! = 1
        xSize& = xPic& * scale!: ySize& = yPic& * scale!
      ' In case Height > 150 compute new scale factor
        IF ySize& > yCell& THEN
           IF yPic& THEN scale! = yCell& / yPic&
           xSize& = xPic& * scale!: ySize& = yPic& * scale!
        END IF
        xOfs& = (xCell& - xSize&) \ 2
        yOfs& = (yCell& - ySize&) \ 2
    END SUB
    '---------------------------------------------------------------------
    ' calls COM object's release method to decrement the reference count
    FUNCTION IUnknown_Release (BYVAL pthis AS DWORD PTR) AS DWORD
        LOCAL DWRESULT AS DWORD
        IF pthis THEN
           CALL DWORD @@pthis[2] USING IUnknown_Release(pthis) TO DWRESULT
           FUNCTION = DWRESULT
        END IF
    END FUNCTION
    '---------------------------------------------------------------------
    ' get a jpg filename
    FUNCTION getpic (hD AS LONG) AS STRING
        LOCAL buf, spath, sfile AS STRING
        LOCAL dwstyle AS DWORD
        LOCAL hFile AS LONG
    
        '------------------------ get database file
        dwStyle = %OFN_EXPLORER OR %OFN_FILEMUSTEXIST OR %OFN_HIDEREADONLY
        Buf   = "Picture files (*.JPG)|*.JPG|"
        'IF gddlpath <> "" THEN spath = gddlpath
        IF OpenFileDialog (hD, "Locate JPG file ", sfile, spath, buf, "JPG", dwstyle) = 0 THEN
           EXIT FUNCTION
        END IF
        FUNCTION = sfile
    END FUNCTION
    '--------------------------------------------------------------------
    FUNCTION sqlErrMsg( BYVAL hDB AS LONG ) AS DWORD ' ptr to asciz error msg
        DIM pzErr AS ASCIIZ PTR
        '    get the SQLite error message
        pzErr = sqlite3_errmsg(hDB)
        FUNCTION = pzErr
    END FUNCTION
    '------------------------------------------------------------------------------
    FUNCTION DoSQL ( BYVAL dwQStuff AS DWORD) AS LONG
        LOCAL pQstuff               AS tQueryStuff PTR
        LOCAL lresult               AS LONG
        LOCAL s                     AS STRING
        LOCAL pszErms, pszQuery, psz  AS ASCIZ PTR
    
    
        pQstuff = dwQstuff
        psz = @pQstuff.pszSQL
        s = @psz
        ' if the last query using this buffer has not been freed, free it!
        IF @pQstuff.pQresults <> 0 THEN
            sqlite3_free_table(@pQstuff.pQresults)
            @pQstuff.pQresults = 0
        END IF
    '    ? "DB=" + str$(@pQstuff.hDB) + " query= " + s, %MB_TASKMODAL,"debug"
        lresult = sqlite3_get_table(BYVAL ghdb, BYVAL @pQstuff.pszSQL, @pQstuff.pQresults, @pQstuff.nQrows, @pQstuff.nQcols, BYVAL @pQstuff.pQzErms)
        FUNCTION = lresult
        IF lresult <> %SQLITE_OK THEN
            pszErms = SQLErrMsg(ghdb)
            pszQuery = @pQstuff.pszSQL
            ? TRIM$(@pszErms) + $CRLF + _
              "SQL was: " + @pszQuery  + $CRLF
            sqlite3_free ( @pQstuff.pQzErms)
        END IF
    END FUNCTION
    
    '----------------------------------------------------------------------------
    ' write the image file to pictures table
    ' first parameter is pointer to a buffer containing the image
    ' second parameter is image buffer length
    FUNCTION ImgFile2Blob ( BYVAL psBuffer AS DWORD, imagesize AS LONG) AS LONG
        LOCAL hfile                 AS LONG
        LOCAL sbuffer               AS STRING
        LOCAL i, lresult            AS LONG
        LOCAL Qstuff                AS tQueryStuff
        LOCAL ppStmt                AS DWORD
        LOCAL psz1, psz2, pzTail, pszErms, pszQuery  AS ASCIZ PTR
        LOCAL sSQL                  AS STRING
        LOCAL pzresults()           AS ASCIZ PTR
        '
        IF gspicpath = "" THEN
            ? "That image is already in the database!" _
               + $CRLF + $CRLF + "no action!",%MB_TASKMODAL,"warning"
               FUNCTION = 1
            EXIT FUNCTION
        END IF
        sSQL = "create table if not exists pictures (key, content blob)"
        Qstuff.pszSQL = STRPTR(sSQL)
        IF DoSQL (BYVAL VARPTR(Qstuff)) <> %SQLITE_OK THEN
            FUNCTION = 3
            GOTO done
        END IF
        ' does the key supplied already exist? if so, warn & exit
        sSQL = "select count(1) from pictures where key = '" + PARSE$(gspicpath,"\",-1) + "'"
        Qstuff.pszSQL = STRPTR(sSQL)
        IF DoSQL (BYVAL VARPTR(Qstuff)) <> %SQLITE_OK THEN
            FUNCTION = 4
            GOTO done
        END IF
        DIM pzresults(0 TO Qstuff.nQrows * Qstuff.nQcols -1) AS ASCIZ PTR AT QStuff.pQResults
        IF VAL(TRIM$( @pzresults(1))) > 0 THEN
            ? "database already contains an image of that name (" + PARSE$(gspicpath,"\",-1) + ")!" _
               + $CRLF + $CRLF + "no action!",%MB_TASKMODAL,"warning"
            FUNCTION = 5
            EXIT FUNCTION
        END IF
        sqlite3_free_table(Qstuff.pQResults)
        ' write the bitmap thus extracted into bitbuffer to the database
        sSQL = "insert into pictures values (?,?)"
        i = INSTR(sSQL, "?")
        sSQL = LEFT$(sSQL, i - 1) + "'" + PARSE$(gspicpath,"\",-1) + "'" + MID$(sSQL, i + 1)
        lresult = sqlite3_prepare_v2(BYVAL ghDB, BYVAL STRPTR(sSQL), BYVAL -1, ppStmt, pzTail)
        IF lresult <> %SQLITE_OK THEN
            psz1 = SQLErrMsg(ghDB)
            ? "savePic:" + TRIM$(@psz1) + $CRLF + _
              "SQL was: " + sSQL  + $CRLF,%MB_TASKMODAL,"warning"
            sqlite3_free ( Qstuff.pQzErms)
            FUNCTION = 6
            GOTO done
        END IF
        lresult = sqlite3_bind_blob( ppStmt, 1, BYVAL psbuffer, imagesize, %SQLITE_STATIC )
        GOSUB checkresult
        DO
            sqlite3_step(ppStmt)
            IF lresult <> %SQLITE_ROW THEN EXIT LOOP
        LOOP
        GOSUB checkresult
    
        lresult = sqlite3_reset(ppStmt)
        GOSUB checkresult
    done:
        EXIT FUNCTION
    checkresult:
        IF lresult <> %SQLITE_OK THEN
           ? "Error binding SQLite variable! " + STR$(lresult), %MB_TASKMODAL,"Warning"
        END IF
        RETURN
    
    END FUNCTION
    '------------------------------------------------------------------------
    CALLBACK FUNCTION PicDlgCB()
        STATIC   ghimage                AS DWORD
        LOCAL    hDC, pgraphics         AS DWORD
        LOCAL    r                      AS rect
        LOCAL    W, H, lPixelFormat     AS LONG
        LOCAL    l, framex, framey AS LONG
        STATIC   ofsx, ofsy, tX, tY      AS LONG
        LOCAL    maxwidth, maxheight     AS LONG
        LOCAL    origwidth, origheight   AS SINGLE
        STATIC   sGDIPStatusCodes()      AS STRING ' table of error codes referenced by GDIP status codes
        LOCAL    sGDIPfn                 AS STRING ' GDIP function name for error reporting
        STATIC   hGlobal, pGlobalBuffer, iImageStream AS DWORD
        LOCAL    imagesize, hfile        AS LONG
        STATIC   simagebuffer            AS STRING ' loaded with image when file is selected for viewing
    
        SELECT CASE AS LONG CBMSG
            CASE %WM_INITDIALOG
                ' clear down the previous stream, buffer and image, if any
                GOSUB clearstreamstuff
                ' initialise static variables
                ghimage  = 0
                hdc     = 0
                pgraphics = 0
                ghPicWnd = CBHNDL
                ofsx = 0: ofsy = 0: tX = 0: tY = 0
                ' load GDIPlus error messages
                GOSUB LoadGDIPERMS
            '
            CASE %WM_ERASEBKGND
                IF ghimage = 0 THEN FUNCTION = 0 : EXIT SELECT
                hdc = getdc(CBHNDL)
                l = GdipCreatefromHDC ( hDC, pGraphics)
                IF l THEN sGDIPfn = "GdipCreatefromHDC": GOSUB gdiperror
                ' this image may be smaller than the last one
                ' so clear the client area before displaying it
                GdipGraphicsClear ( pGraphics, -1)
                ' draw image
                l = GdipDrawImageRectI(pGraphics, ghimage, ofsX, ofsY, tX, tY)
                IF l THEN sGDIPfn = "GdipDrawImageRectI": GOSUB gdiperror
                releaseDC (CBHNDL, hDC)
                l = gdipdeletegraphics(pgraphics)
                IF l THEN sGDIPfn = "gdipdeletegraphics": GOSUB gdiperror
                FUNCTION = 1
            '        '
            CASE %WM_USER + 401 ' GET IMAGE
                ' user has pressed the load picture button
                ' clear down the previous stream, buffer and image, if any
                GOSUB clearstreamstuff
                gspicpath = GetPic(CBHNDL) 'ucode$
                ' load file to image buffer
                TRY
                    hfile = FREEFILE
                    OPEN gspicpath FOR BINARY ACCESS READ AS hfile  'gsPicPath
                    imagesize = INT(LOF(hfile))
                    simagebuffer = SPACE$(imagesize)
                    GET #hfile, 1, simagebuffer
                    CLOSE #hfile
                    '? "IF2B 2" + gspicpath + $CRLF + "image size" + STR$(imagesize),%MB_TASKMODAL,"debug"
                CATCH
                    ? "error reading image file " + gspicpath + $CRLF + ERROR$,%MB_TASKMODAL,"warning"
                    CLOSE #hfile
                    EXIT FUNCTION
                END TRY
                IF ghimage THEN
                    l = gdipdisposeimage (ghimage)
                    IF l THEN sGDIPfn = "gdipdisposeimage": GOSUB gdiperror
                END IF
                l = GDIPLoadImageFromFile (BYVAL UCODE$(gspicPath), BYREF ghimage)
                IF l THEN sGDIPfn = "GDIPLoadImageFromFile": GOSUB gdiperror
                CONTROL SET TEXT getparent(CBHNDL), %IDC_LABEL4, TRIM$(gsPicPath)
                GOSUB showpic
            '
            CASE %WM_USER + 402 ' LOAD FROM DB
                ' clear down the previous stream, buffer and image, if any
                GOSUB clearstreamstuff
                ' user has loaded a file from the DB
                ' a ptr to it is in wparam, length in lparam
                ' set up a buffer must be GlobalAlloc etc
                hGlobal = GlobalAlloc(%GMEM_MOVEABLE OR %GMEM_NODISCARD, CBLPARAM)
                IF hGlobal THEN
                   pGlobalBuffer = GlobalLock(hGlobal)
                   IF pGlobalBuffer THEN
                      ' copy the image into the stream
                      CopyMemory pGlobalBuffer, BYVAL CBWPARAM, CBLPARAM
                      ' create a stream on the buffer
                      IF CreateStreamOnHGlobal(hGlobal, BYVAL 0&, iImageStream) = 0 THEN
                          ' load the image from the stream
                          l = GdipLoadImageFromStream (iImageStream, ghImage)
                          IF l THEN sGDIPfn = "GdipLoadImageFromStream": GOSUB gdiperror
                          'CALL IUnknown_Release(iImageStream)
                      END IF
                      'GlobalUnlock pGlobalBuffer
                   END IF
                   'GlobalFree hGlobal
                END IF
                'gspicpath = ucode$(gspicpath)
                GOSUB showpic
            '
            CASE %WM_USER + 403 ' SAVE TO DB
                IF ImgFile2Blob ( BYVAL STRPTR(simageBuffer), LEN(simageBuffer)) = 0 THEN
                    GOSUB clearstreamstuff
                    CONTROL SET TEXT getparent(CBHNDL), %IDC_LABEL4, TRIM$(gspicpath) + $CRLF + " saved to database"
                END IF
            '
            CASE %WM_DESTROY
                ' clear down the previous stream, buffer and image, if any
                GOSUB clearstreamstuff
                releaseDC( CBHNDL, hdc )
    
        END SELECT
        EXIT FUNCTION
    ''''''''''''
    showpic:
        IF ghimage = 0 THEN EXIT FUNCTION
        getClientrect(CBHNDL, r)
        ' get image height, width & pixel format
        l = GdipGetImageDimension (BYVAL ghimage, BYREF origwidth, BYREF origheight)
        IF l THEN sGDIPfn = "GdipGetImageDimension": GOSUB gdiperror
        maxwidth  =  r.nright - r.nleft : maxheight = r.nbottom - r.ntop
        l = GdipGetImagePixelFormat (BYVAL ghimage, BYREF lPixelFormat)
        IF l THEN sGDIPfn = "GdipGetImagePixelFormat": GOSUB gdiperror
        ' derive new image size for display on the dailog, preserving aspect ratio
        ' the results are declared STATIC so that the %WM_ERASEBKGND handler can use them
        skIconise ( BYVAL origwidth, BYVAL origheight, BYVAL maxwidth, BYVAL maxheight, _
                    BYREF ofsX, BYREF ofsY, BYREF tX, BYREF tY)
        ' tidy up
        'gdipdeletegraphics pGraphics
        'IF l THEN sGDIPfn = "GdipDeleteGraphics": GOSUB gdiperror
        'releaseDC( CBHNDL, hdc )
        ' force redraw via WM_ERASEBKGND
        invalidaterect CBHNDL, r, %TRUE
        RETURN
    ''''''''''''
    clearstreamstuff:
        ' clear down the previous stream, buffer and image, if any
        IF ghimage THEN
            ' assumption here that we are only referencing the iImageStream once
            IF iImageStream <> 0 THEN
                l = IUnknown_Release(BYVAL iImageStream)
                IF GlobalUnlock (pGlobalBuffer) <> 0 THEN
                    l = getlasterror()
                    IF l <> 0 THEN ? "global memory not unlocked!" + STR$(l), %MB_TASKMODAL,"debug"
                END IF
                IF globalfree (hglobal) <> 0 THEN
                    ? "global memory not freed!", %MB_TASKMODAL,"debug"
                END IF
                pGlobalBuffer = 0
                iImageStream = 0
            END IF
            l = gdipdisposeimage(ghimage)
            ghimage = 0
            IF l THEN sGDIPfn = "GdipDisposeImage": GOSUB gdiperror
        END IF
        RETURN
    gdiperror:
        ? sGDIPfn + ":" + sGDIPStatusCodes(l), %MB_TASKMODAL,"warning"
        EXIT FUNCTION
        RETURN
    ''''''''''''''''''''''
    LoadGDIPERMS:
        DIM sGDIPStatusCodes(0 TO 20) AS STATIC STRING
        sGDIPStatusCodes(%StatusOk                        ) = "Ok
        sGDIPStatusCodes(%StatusGenericError              ) = "Generic Error
        sGDIPStatusCodes(%StatusInvalidParameter          ) = "Invalid Parameter
        sGDIPStatusCodes(%StatusOutOfMemory               ) = "Out Of Memory
        sGDIPStatusCodes(%StatusObjectBusy                ) = "Object Busy
        sGDIPStatusCodes(%StatusInsufficientBuffer        ) = "Insufficient Buffer
        sGDIPStatusCodes(%StatusNotImplemented            ) = "Not Implemented
        sGDIPStatusCodes(%StatusWin32Error                ) = "Win32 Error
        sGDIPStatusCodes(%StatusWrongState                ) = "Wrong State
        sGDIPStatusCodes(%StatusAborted                   ) = "Aborted
        sGDIPStatusCodes(%StatusFileNotFound              ) = "File Not Found
        sGDIPStatusCodes(%StatusValueOverflow             ) = "Value Overflow
        sGDIPStatusCodes(%StatusAccessDenied              ) = "Access Denied
        sGDIPStatusCodes(%StatusUnknownImageFormat        ) = "Unknown Image Format
        sGDIPStatusCodes(%StatusFontFamilyNotFound        ) = "Font Family Not Found
        sGDIPStatusCodes(%StatusFontStyleNotFound         ) = "Font Style Not Found
        sGDIPStatusCodes(%StatusNotTrueTypeFont           ) = "Not TrueType Font
        sGDIPStatusCodes(%StatusUnsupportedGdiplusVersion ) = "Unsupported Gdiplus Version
        sGDIPStatusCodes(%StatusGdiplusNotInitialized     ) = "Gdiplus Not Initialized
        sGDIPStatusCodes(%StatusPropertyNotFound          ) = "Property Not Found
        sGDIPStatusCodes(%StatusPropertyNotSupported      ) = "Property Not Supported
        RETURN
    
    END FUNCTION
    '--------------------------------------------------------------------
    ' return the query results concatenated into a single string
    ' parameters are :
    ' control id, start row, start column, row count, column count, row seperator, col seperator
    FUNCTION GetQResultsasString ( BYVAL pQdw AS DWORD, _
                                   lstartrow AS LONG, lrowcount AS LONG, _
                                   lstartcol AS LONG, lcolcount AS LONG, _
                                   srowsep AS STRING, scolsep AS STRING) AS STRING
        LOCAL pQStuff           AS tQueryStuff PTR
        LOCAL cx                AS LONG ' control index
        LOCAL i, j, n           AS LONG ' ephemeral counter
        LOCAL s                 AS STRING ' ephemeral string
        LOCAL ps()              AS ASCIZ PTR ' pointer array to map onto sqlite results table
    
        pQstuff = pQDW
        DIM ps ( 0 TO (@pQstuff.nQrows * @pQstuff.nQcols) -1) AT @pQstuff.pQresults
        FOR i = lstartrow TO lstartrow + lrowcount - 1
            FOR j = lstartcol TO lstartcol + lcolcount -1
                n = i * lcolcount + j
                s = s + scolsep + TRIM$(@ps(i * lcolcount + j))
            NEXT
            s = s + srowsep
        NEXT
        IF ASC(s) = ASC(scolsep) THEN s = MID$(s,2)
        FUNCTION = s
    END FUNCTION
    
    '--------------------------------------------------------------------
    ' populate a listbox
    ' parameters - 1st is listbox id, 2nd is true if column headings are required, 3rd is column seperator
    FUNCTION q2lB ( BYVAL dwQstuff AS DWORD, lstartrow AS LONG, lendrow AS LONG, scolsep AS STRING, snoresultmsg AS STRING) AS LONG
        LOCAL i, lx                     AS LONG
        LOCAL s                         AS STRING
        LOCAL pQstuff                   AS tQueryStuff PTR
    
        ON ERROR GOTO er99
        pQstuff = dwQstuff
        sendmessage(@pQstuff.hwnd,%LB_RESETCONTENT, 0, 0 )
        IF lendrow = 0 THEN
            sendmessage (@pQstuff.hWnd, %LB_ADDSTRING, 0, BYVAL STRPTR(snoresultmsg))
            EXIT FUNCTION
        END IF
        FOR i = lstartrow TO lendrow
            s = GetQresultsasString (BYVAL pQstuff, i, 1, 0, @pQstuff.nQcols,"",sColSep)
            sendmessage (@pQstuff.hWnd, %LB_ADDSTRING, 0, BYVAL STRPTR(s))
        NEXT
        FUNCTION = @pQstuff.nQrows
        EXIT FUNCTION
    er99:
        ? "error in Q2LB at" + STR$(ERL)+ STR$( ERR),%MB_TASKMODAL
    
    END FUNCTION
    ''----------------------------------------------------------
    '' this is a limited example of the use of sqlite3_column_blob
    '' reads a blob in column 0 of a query which returns a singe row
    '' won't work with multi-row or multi-column!
    SUB ReadBlob ( BYVAL dwQStuff AS DWORD )
        LOCAL  pQstuff              AS tQueryStuff PTR
        LOCAL   ppStmt              AS DWORD
        LOCAL   pzTail, pszErms, pszQuery  AS ASCIZ PTR
        LOCAL   dwObjAddr           AS DWORD
        LOCAL   lresult             AS LONG
        LOCAL   tempptr             AS DWORD PTR
        LOCAL   n                   AS LONG
    
        ON ERROR GOTO er99
        pQstuff = dwQstuff
    
        IF sqlite3_prepare_v2( ghDB, BYVAL @pQstuff.pszSQL, -1, ppStmt, pzTail ) <> %SQLITE_OK THEN
            pszErms = SQLErrMsg(ghDB)
            pszQuery = @pQstuff.pszSQL
            ? "Failed to prepare query when reading Blob!" + $CRLF + $CRLF + _
            TRIM$(@pszErms) + $CRLF + _
            "SQL was: " + @pszQuery  + $CRLF + $CRLF + _
            "...Load form failed!!!!",%MB_TASKMODAL,"Warning"
            sqlite3_free ( @pQstuff.pQzErms)
            GOTO done
        END IF
        '
        pszQuery = @pQstuff.pszSQL
        DO
            lresult = sqlite3_step(ppStmt)
    '        ?      "<" + @pszquery + ">",%MB_TASKMODAL,"debug"
            SELECT CASE lresult
                CASE %SQLITE_ROW : EXIT LOOP
                CASE ELSE
                    pszErms = SQLErrMsg(ghDB)
                    pszQuery = @pQstuff.pszSQL
                    '? TRIM$(@pszErms) + $CRLF + "SQL was: " + @pszQuery  + $CRLF
                    sqlite3_free ( @pQstuff.pQzErms)
                    GOTO done
            END SELECT
        LOOP
    
        tempptr = sqlite3_column_blob(BYVAL ppStmt, BYVAL 0)   ' load blob into allocated memory & return pointer
        n = sqlite3_column_bytes(BYVAL ppStmt, BYVAL 0)   ' Get Blob size
        IF n > @pQstuff.lBlobBuffersize THEN
            n = @pQstuff.lBlobBuffersize ' avoid overrun
        ELSE
            @pQstuff.lBlobBuffersize = n ' buffer size may have changed, tell caller
        END IF
        movememory (BYVAL @pQstuff.pBlobdata, BYVAL Tempptr, n)  ' Copy the Blob from SQLite's space to our own
    done:
        IF sqlite3_finalize(ppStmt) <> %SQLITE_OK THEN
            ? "Failed to finalize query when reading Blob!" + $CRLF + $CRLF + _
             "...Load form may have failed!!!!",%MB_TASKMODAL,"Warning"
            EXIT SUB
        END IF
        EXIT SUB
    er99:
        ? "error in READBLOB at" + STR$(ERL)+ STR$( ERR),%MB_TASKMODAL
    END SUB
    '----------------------------------------------------------
    ' return the length of the blob in in column 0 of query
    FUNCTION GetBlobLen ( BYVAL dwQStuff AS DWORD ) AS LONG
        LOCAL  pQstuff              AS tQueryStuff PTR
        LOCAL   llen                AS LONG ' length of dynamic string array
        LOCAL   ppStmt              AS DWORD
        LOCAL   pzTail, pszErms, pszQuery  AS ASCIZ PTR
        LOCAL   dwObjAddr           AS DWORD
        LOCAL   lresult             AS LONG
        LOCAL   tempptr             AS DWORD PTR
        LOCAL   n                   AS LONG
    
        ON ERROR GOTO er99
        pQstuff = dwQstuff
    
        IF sqlite3_prepare_v2( ghDB, BYVAL @pQstuff.pszSQL, -1, ppStmt, pzTail ) <> %SQLITE_OK THEN
            pszErms = SQLErrMsg(ghDB)
            pszQuery = @pQstuff.pszSQL
            ? "Failed to prepare query when reading Blob!" + $CRLF + $CRLF + _
            TRIM$(@pszErms) + $CRLF + _
            "SQL was: " + @pszQuery  + $CRLF + $CRLF _
            ,%MB_TASKMODAL,"Warning"
            GOTO done
        END IF
        '
        pszQuery = @pQstuff.pszSQL
        DO
            lresult = sqlite3_step(ppStmt)
            SELECT CASE lresult
                CASE %SQLITE_ROW : EXIT LOOP
                CASE ELSE
                    pszErms = SQLErrMsg(ghDB)
                    pszQuery = @pQstuff.pszSQL
                    ? TRIM$(@pszErms) + $CRLF + "SQL was: " + @pszQuery  ,%MB_TASKMODAL,"Warning"
                    sqlite3_free ( @pQstuff.pQzErms)
                    GOTO done
            END SELECT
        LOOP
    
        tempptr = sqlite3_column_blob(BYVAL ppStmt, BYVAL 0)   ' load blob into allocated memory & return pointer
        n = sqlite3_column_bytes(BYVAL ppStmt, BYVAL 0)   ' Get Blob size
        @pQstuff.lBlobBuffersize = n ' buffer size may have changed, tell caller
        FUNCTION = n
    done:
        IF sqlite3_finalize(ppStmt) <> %SQLITE_OK THEN
            ? "Failed to finalize query when reading Blob!" + $CRLF + $CRLF _
             ,%MB_TASKMODAL,"Warning"
            EXIT FUNCTION
        END IF
        EXIT FUNCTION
    er99:
        ? "error in GETBLOBLEN at" + STR$(ERL)+ STR$( ERR),%MB_TASKMODAL
    END FUNCTION
    '------------------------------------------------------------------------------
    SUB loadfromDB ( hD AS DWORD, skey AS STRING)
        LOCAL i, hfile, l, lcx, lex, lret, n, nrecs, x, y _
                                        AS LONG ', x, y AS LONG
        LOCAL Qstuff                    AS tQueryStuff
        LOCAL spath, sAppName, sType    AS STRING
        LOCAL sTemp()                   AS STRING
        LOCAL   s, squery               AS STRING
        LOCAL   imagesize                    AS LONG
        LOCAL   dwobjaddr               AS DWORD PTR
        LOCAL   pszresults()            AS ASCIZ PTR
        LOCAL   hdc                     AS DWORD
        LOCAL   r                       AS rect
        LOCAL hglobal, iImageStream, pGlobalBuffer AS DWORD
    
        ON ERROR GOTO er99
    '--- load the picture into the buffer
        sQuery = "select content from pictures where key = '" + TRIM$(sKey) + "'"
        Qstuff.pszSQL = STRPTR(squery)
        imagesize = getbloblen(BYVAL VARPTR(Qstuff))
        s = STRING$(imagesize, $SPC)
        Qstuff.pBlobdata = STRPTR(s)
        Qstuff.lBlobBufferSize = imagesize
        readblob (BYVAL VARPTR(Qstuff))
        CONTROL SET TEXT getparent(ghPicWnd), %IDC_LABEL4, STR$(imagesize) + " bytes from database: " + skey
        sendmessage ghPicWnd, %WM_USER + 402, BYVAL Qstuff.pBlobdata, imagesize
        EXIT SUB
    er99:
        ? "error in LOADFROMDB at" + STR$(ERL)+ STR$( ERR),%MB_TASKMODAL
    END SUB
    
    '------------------------------------------------------------------------------
    CALLBACK FUNCTION ShowLBCB()
        LOCAL   sz                  AS ASCIZ * 64
        STATIC  sQuery              AS STRING
        LOCAL   lselitem            AS LONG
        LOCAL   s                   AS STRING
        LOCAL   lresult             AS LONG
        STATIC  hLB                 AS DWORD
        STATIC  Qstuff              AS tQueryStuff
        LOCAL   pbyte               AS BYTE PTR
    
        SELECT CASE AS LONG CBMSG
            '
            CASE %WM_INITDIALOG
                '' initialise statics
                sQuery = ""
                hLB = 0
                s = STRING$(SIZEOF(qstuff),0)
                POKE$ VARPTR(qstuff), PEEK$(STRPTR(s), LEN(s))
                ''
                Qstuff.hWnd = getdlgitem(CBHNDL, %IDC_LISTBOX1)' window handle
                sQuery = "select distinct key from pictures"
                Qstuff.pszSQL = STRPTR(sQuery)
                IF DoSQL (BYVAL VARPTR(Qstuff)) <> %SQLITE_OK THEN
                    DIALOG END CBHNDL, 0
                    EXIT SELECT
                END IF
                IF Qstuff.nQrows = 0 THEN
                    ?"The database contains no pictures!",%MB_TASKMODAL,"Warning"
                    DIALOG END CBHNDL, 0
                    EXIT SELECT
                END IF
                Qstuff.selection = ""
                q2lB ( BYVAL VARPTR(Qstuff), 1, Qstuff.nQrows, "", "")
            '
            CASE %WM_COMMAND
                SELECT CASE AS LONG CBCTL
                    CASE %IDC_LISTBOX1
                        IF HI(WORD,CBWPARAM) = %LBN_SELCHANGE THEN
                            ' get the index of the selected item
                            lselitem = sendmessage(Qstuff.hWnd, %LB_GETCURSEL, 0, 0)
                            'returns the index of the current selection in wparam
                            sendmessage (Qstuff.hWnd, %LB_GETText, lselitem, BYVAL VARPTR(sz))
                            loadfromDB(getparent(CBHNDL), TRIM$(sz))
                            DIALOG END CBHNDL, 0
                        END IF
                END SELECT
            CASE %WM_DESTROY
                ' free SQLite memory
                IF Qstuff.pQresults <> 0 THEN sqlite3_free_table(Qstuff.pQresults)
                Qstuff.pQresults = 0
        END SELECT
    END FUNCTION
    '------------------------------------------------------------------------------
    FUNCTION ShowLB(BYVAL hParent AS DWORD) AS LONG
        LOCAL lRslt AS LONG
        LOCAL hDlg  AS DWORD
    
        DIALOG NEW hParent, "Choose a Picture", _
                     70, 70, 191, 96, _
                     %WS_POPUP OR %WS_BORDER OR %WS_DLGFRAME OR %WS_SYSMENU OR _
                     %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_CLIPSIBLINGS OR %WS_VISIBLE OR _
                     %DS_MODALFRAME OR %DS_3DLOOK OR %DS_NOFAILCREATE OR %DS_SETFONT, _
                     %WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR, _
                     TO hDlg
        CONTROL ADD LISTBOX, hDlg, %IDC_LISTBOX1, , 0, 0, 190, 95
    
        DIALOG SHOW MODAL hDlg, CALL ShowLBCB TO lRslt
        FUNCTION = lRslt
    END FUNCTION
    
    '------------------------------------------------------------------------
    CALLBACK FUNCTION ShowDIALOG1Proc()
        LOCAL s AS STRING
        STATIC hPicWnd      AS DWORD
    
        SELECT CASE AS LONG CBMSG
            CASE %WM_INITDIALOG
                DIALOG NEW CBHNDL, "", 85, 5, 190, 125, %WS_POPUP OR %WS_BORDER OR _
                    %WS_POPUP OR %WS_CHILD OR _
                    %WS_CLIPSIBLINGS OR %WS_VISIBLE OR %DS_3DLOOK OR %DS_NOFAILCREATE OR _
                    %DS_SETFONT, %WS_EX_CONTROLPARENT OR %WS_EX_TOOLWINDOW OR _
                    %WS_EX_TOPMOST OR %WS_EX_LEFT OR %WS_EX_LTRREADING TO hPicWnd
                DIALOG SHOW MODELESS hPicWnd, CALL PicDlgCB 'TO lRslt
    
            CASE %WM_COMMAND
                SELECT CASE AS LONG CBCTL
                    CASE %IDC_getpic_bn
                        IF HI(WORD,CBWPARAM) = %BN_CLICKED THEN
                            sendmessage hPicWnd, %WM_USER + 401, 0, 0
                        END IF
                    CASE %IDC_picDB_bn
                        ' pop up a ListBox of saved queries from which one can be selected
                        ShowLB(CBHNDL)
    
                    CASE %IDC_savepicDB_bn
                        sendmessage ghPicWnd, %WM_USER + 403, 0, 0
                END SELECT
        END SELECT
    END FUNCTION
    '----------------------------------------------------------------------------------------
    FUNCTION ShowDIALOG1(BYVAL hParent AS DWORD) AS LONG
        LOCAL lRslt AS LONG
        LOCAL hDlg  AS DWORD
    
        DIALOG NEW hParent, "SQLite blob example ", _
                   139, 129, 279, 160, _
                   %WS_POPUP OR %WS_BORDER OR %WS_DLGFRAME OR %WS_SYSMENU OR %WS_MINIMIZEBOX OR _
                   %WS_CLIPSIBLINGS OR %WS_VISIBLE OR %DS_MODALFRAME OR %DS_3DLOOK OR _
                   %DS_NOFAILCREATE OR %DS_SETFONT, _
                   %WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR, _
                   TO hDlg
        CONTROL ADD BUTTON, hDlg, %IDC_getpic_bn, "find a picture", 5, 5, 75, 15
        CONTROL ADD BUTTON, hDlg, %IDC_picDB_bn, "Load picture from DB", 5, 35, 75, 15
        CONTROL ADD BUTTON, hDlg, %IDC_savepicDB_bn, "save pic to DB", 5, 20, 75, 15
        CONTROL ADD LABEL,  hDlg, %IDC_LABEL3, "by Chris Holbrook August 2008", 5, 105, 65, 25
        CONTROL ADD LABEL,  hDlg, %IDC_LABEL4, "", _
                                               85, 135, 190, 18, _
                                               %SS_SUNKEN,
        DIALOG SHOW MODAL hDlg, CALL ShowDIALOG1Proc TO lRslt
        FUNCTION = lRslt
    END FUNCTION
    '=======================================================================
    FUNCTION PBMAIN()
        STATIC nstatus AS LONG
        STATIC token AS DWORD
        STATIC StartupInput AS GdiplusStartupInput
    
        gszdbpath = "c:\PIX.DB"                    '<<<<<<<<<<<<<<<<<<<<<<<<<<< CHANGE THIS!!!!
        StartupInput.GdiplusVersion = 1
        nStatus = GdiplusStartup(token, StartupInput, BYVAL %NULL)
        IF nStatus THEN
            MSGBOX  "Error initializing GDI+", %mb_applmodal, "Warning"
            EXIT FUNCTION
        END IF
        sqlite3_open( gszDBpath, ghDB )
        ShowDIALOG1 %HWND_DESKTOP
        sqlite3_close( ghDB )
        GdiplusShutdown token
    
    END FUNCTION
Working...
X