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

Capture Desktop image and Print to Printer

  • Filter
  • Time
  • Show
Clear All
new posts

  • Capture Desktop image and Print to Printer

    The following code will work in both PB/DLL and PB/CC:
      LOCAL di     AS DOCINFO
      LOCAL dn     AS ASCIIZ * 64
      LOCAL hWnd   AS LONG
      LOCAL hDC    AS LONG
      LOCAL hBmp   AS LONG
      LOCAL r      AS RECT
      LOCAL x      AS LONG
      LOCAL y      AS LONG
      pd.lStructSize = SIZEOF(pd)
      pd.hWndOwner   = %HWND_DESKTOP
      IF ISFALSE PrintDlg(pd) THEN
      END IF
      hWnd     = GetDeskTopWindow
      GetWindowRect hWnd, r
      hDC      = GetDC(hWnd)
      hBmpDC   = CreateCompatibleDC(pd.hDC)
      hBmp     = CreateCompatibleBitmap(pd.hDC, r.nRight, r.nBottom)
      SelectObject hBmpDC, hBmp
      BitBlt hBmpDC, 0, 0, r.nRight, r.nBottom, hDC, 0, 0, %SRCCOPY
      dn = "Printer Test"
      di.cbSize = SIZEOF(di)
      di.lpszDocName = VARPTR(dn)
      IF StartDoc(pd.hDC, di) > 0 THEN
        IF StartPage(pd.hDC) <= 0 THEN
        END IF
        ' scale the bitmap to the width of the page
        x = GetDeviceCaps(pd.hDC, %HORZRES)
        y = CLNG((x / r.nRight) * r.nBottom)
        SelectObject pd.hDC, hBmp
        StretchBlt pd.hDC, 0, 0, x, y, hBmpDC, 0, 0, r.nRight, r.nBottom, %SRCCOPY
        IF EndPage(pd.hDC) <= 0 THEN
        END IF
        EndDoc pd.hDC
      END IF
      DeleteDC hDC
      DeleteDC hBmpDC
      DeleteDC pd.hDC

    PowerBASIC Support
    mailto:[email protected][email protected]</A>
    Home of the BASIC Gurus

  • #2
    Alright, I'm gonna need help with this. Ken Lundberg also emailed me with the same problem.

    It works perfectly for me on my Windows NT 4.0 (SP3) machine and Windows 98 laptop. Both are connected to a network and printing to an HP DeskJet 697C and an HP LaserJet 6.

    Both machines are running at 16-bit (65536 colors). The WinNT machine is running at 1024x768 resolution and the laptop is running at 800x600 resolution.

    My *guess* is that it's a palette problem, but I'm not sure.


    PowerBASIC Support
    mailto:[email protected][email protected]</A>
    Home of the BASIC Gurus


    • #3
      I did not have a problem with it.
      I am on NT 4.0 SP3 printing to a networked Lexmark 1625 printer.
      Warped by the rain, Driven by the snow...


      • #4
        It's definitely a Windows 95/98 problem, but I'm still not sure how to fix it yet. I tried it on an older Windows 98 machine and it failed.


        PowerBASIC Support
        mailto:[email protected][email protected]</A>
        Home of the BASIC Gurus


        • #5
          According to the sdk
          ... BitBlt returns an error if the source and destination device contexts represent different devices ...
          You have a screen dc being blt'd to one comptible with the printer. That's probably why bitblt is failing. Printing bitmaps is best done with DIBs as described in Petzold and the article in Basically Speaking ( The bitmap for the screen is device dependent so you'll likely have inconsistent results trying to print it. It's interesting that the NT machines all seem to work.
          Best Regards,
          Don Dickinson


          • #6
            It works in Windows 98 SE too.

            Thanks for the heads up! I'll work on it this weekend and upload a fix.


            PowerBASIC Support
            mailto:[email protected][email protected]</A>
            Home of the BASIC Gurus


            • #7
              Hey Dave,
              What is Windows 98 SE - specifically what's SE? Someone asked me that yesterday and I didn't have an answer.
              Don Dickinson


              • #8
                Second Edition

                mailto:[email protected][email protected]</A>
                mailto:[email protected][email protected]</A>


                • #9
                  I think Don Dickinson wrote this code:
                  $COMPILE EXE
                  $DIM ALL
                  $INCLUDE "WIN32API.INC"
                  $INCLUDE "COMDLG32.INC"
                  '   1. PRELIMINARY STEP   -     **** IMPORTANT ****
                  '   You need to go into your file and change the declaration
                  '   for the BITMAPINFO structure's bmi Colors member to 256. If you don't
                  '   you'll gpf on all bitmaps that aren't monochrome.
                  '   TYPE BITMAPINFO
                  '      bmiHeader AS BITMAPINFOHEADER
                  '      bmiColors(256) AS RGBQUAD
                  '   END TYPE
                  FUNCTION BltImage(zFile AS ASCIIZ, BYVAL iPrinting AS INTEGER, _
                    BYVAL x AS DOUBLE, BYVAL y AS DOUBLE, BYVAL wid AS DOUBLE, _
                     '  The function will take the name of a .BMP file (sImageFile)
                     '  and a value (iPrinting) to tell us whether or not we're printing.
                     '  If printing we'll need to use the DIB calls.
                     '  Also passed are the left and top coordinates (x and y) and
                     '  width and height of the bitmap to display (wid and hgt).
                     '  The function will fit the image inside the rectangle defined
                     '  by x, y, wid, and hgt. All measurements are in inches.
                     '  Finally, the DC of the surface to blt to is passed (hDC)
                     '  Variable declarations
                    DIM ppiX AS LONG           ' pixels per inch in X direction
                    DIM ppiY AS LONG           ' pixels per inch in Y direction
                    DIM x1 AS LONG             ' This will contain the X position in Pixels
                    DIM y1 AS LONG             ' This will contain the Y position in Pixels
                    DIM w1 AS LONG             ' This will contain the width in Pixels
                    DIM h1 AS LONG             ' This will contain the height in Pixels
                    DIM hDesktop AS LONG       ' A handle to the desktop window (for reference)
                    DIM hBitmap AS LONG        ' A handle to the bitmap while it's in memory
                    DIM dcWindow AS LONG       ' We need to obtain a temporary DC
                    DIM dcBitmap AS LONG       ' Another temporary DC compatible with the
                                               ' dcWindow that will be used to reference
                                               ' the bitmap hBitmap.
                    DIM hOldBM AS LONG         ' Another temporary handle.
                    DIM bmSize AS LONG         ' How big is our DIB going to have to be
                    DIM ptrMem AS LONG         ' Pointer to the gMemory string.
                    DIM gMemory AS STRING      ' Space to put the DIB Bits
                    DIM rBitmap AS BITMAP      ' Structure describing the dependent bitmap
                    DIM bmInfo AS BITMAPINFO   ' Structure describing the independent bitmap
                    '- STEP 1 - ensure that we have properly altered the BITMAPINFO structure
                    IF SIZEOF(bmInfo) < 40 + 256 * 4 THEN
                      MsgBox "The BITMAPINFO structure doesn't have enough space in the " + _
                        "bmiColors array. Please define it with at least 256 array members."
                      FUNCTION = %False
                      '- STEP 2 - Do unit conversions from inches to output units
                      ppiX = GetDeviceCaps(dcOut, %LOGPIXELSX)
                      ppiY = GetDeviceCaps(dcOut, %LOGPIXELSY)
                      x1 = ppiX * x
                      y1 = ppiY * y
                      w1 = ppiX * wid
                      h1 = ppiY * hgt
                      '- STEP 3               Load the BMP
                      '  Load the bitmap into memory. This function requires Win95 or WinNT v4.
                      '  Instead of doing this, you could also pass the handle to the bitmap.
                      '  This call requires the width and height of the bitmap be passed as
                      '  well as the name of the file.
                      hBitmap = LoadImage(0, zFile, %IMAGE_BITMAP, 0, 0, %LR_LOADFROMFILE)
                      IF hBitmap = 0 THEN
                        MsgBox "Can't load bitmap"
                        FUNCTION = %FALSE
                        '- STEP 4            Get a temp DC for bmp manipulation
                        '  For printing the bitmap, we will need a temporary dc
                        '  compatible with the display. For blting to the screen,
                        '  we need a temporary dc to hold the bitmap that will
                        '  be blted to the screen. We'll just create one compaible
                        '  with the desktop window.
                        hDesktop = GetDesktopWindow()
                        dcWindow = GetDC(hDesktop)
                        dcBitmap = CreateCompatibleDC(dcWindow)
                        ReleaseDC hDesktop, dcWindow
                        IF iPrinting = %False THEN
                          '- STEP 5-A       Not printing, so blt the image.
                          '  If we're not printing, then copying the bitmap is a simple
                          '  matter of calling bitblt. We need to first get the height and
                          '  width of the bitmap so it can be proportionally fit into the
                          '  defined rectangle.
                          GetObject hBitmap, len(rBitmap), rBitmap
                          '- Proportionally fit the bitmap inside of the defined rectangle
                          IF (rBitmap.bmWidth / rBitmap.bmHeight) > (w1 / h1) THEN
                            h1 = w1 * rBitmap.bmHeight / rBitmap.bmWidth
                            w1 = h1 * rBitmap.bmWidth / rBitmap.bmHeight
                          END IF
                          hOldBM = SelectObject(dcBitmap, hBitmap)
                          BitBlt dcOut, x1, y1, w1, h1, dcBitmap, 0, 0, %SRCCOPY
                          SelectObject dcBitmap, hOldBM
                          FUNCTION = %True
                          '- STEP 4-B       Create a DIB
                          '  If we're blting to a printer, then we need to create
                          '  a DIB from the device dependent bitmap (hBitmap).
                          '  This area of the routine is the most involved. For those
                          '  not familiar with a DIB, I will quickly explain ...
                          '     A device dependent bitmap is a window object that
                          '     can be manipulated with API calls. A DIB is merely
                          '     a memory area containing pixel information and a
                          '     structure describing it. You can't use SelectObject
                          '     on a dib because it isn't a Windows Object. To convert
                          '     a device dependent bitmap into a DIB requires 2 things.
                          '        1. Fill out a structure containing the description of
                          '           the DIB's Bits
                          '        2. Get the actual bits in the correct format into an
                          '           area of memory.
                          '     Once these things are done, the API has a few calls
                          '     to manipulate the dibs based on it's structure and a
                          '     pointer to it's bits.
                          GetObject hBitmap, len(rBitmap), rBitmap
                          bmInfo.bmiHeader.biSize = len(bmInfo.bmiHeader)
                          bmInfo.bmiHeader.biWidth = rBitmap.bmWidth
                          bmInfo.bmiHeader.biHeight = rBitmap.bmHeight
                          bmInfo.bmiHeader.biPlanes = 1
                          bmInfo.bmiHeader.biBitCount = rBitmap.bmBitsPixel
                          bmInfo.bmiHeader.biCompression = %BI_RGB
                          '- Calculate space needed for the dib bits
                          bmSize = bmInfo.bmiHeader.biWidth
                          bmSize = (bmSize + 1) * (bmInfo.bmiHeader.biBitCount / 8)
                          bmSize = ((bmSize + 3) / 4) * 4
                          bmSize = bmSize * bmInfo.bmiHeader.biHeight
                          '- Allocate the memory
                          ON ERROR RESUME NEXT
                          gMemory = STRING$(bmSize, CHR$(0))
                          ON ERROR GOTO 0
                          IF ERR THEN
                            '- Couldn't get the memory
                            FUNCTION = %FALSE
                            '- Proportionally fit the bitmap inside of the defined rectangle
                            IF (rBitmap.bmWidth / rBitmap.bmHeight) > (w1 / h1) THEN
                              h1 = w1 * rBitmap.bmHeight / rBitmap.bmWidth
                              w1 = h1 * rBitmap.bmWidth / rBitmap.bmHeight
                            END IF
                            '- STEP 6               Copy Bits
                            '  This step takes the hBitmap object and copies it's
                            '  bits over to our memory in a DIB format.
                            ptrMem = STRPTR(gMemory)
                            GetDIBits dcBitmap, hBitmap, 0, bmInfo.bmiHeader.biHeight, _
                              BYVAL ptrMem, bmInfo, %DIB_RGB_COLORS
                            '- STEP 7               Blt the bits to the printer
                            '  FINALLY, the moment of truth. We have our structure
                            '  and memory full of DIB Bits, so we can call StretchDIBits
                            '  to blt the bits onto the printer's DC
                            StretchDIBits dcOut, x1, y1, w1, h1, 0, 0, bmInfo.bmiHeader.biWidth, _
                              bmInfo.bmiHeader.biHeight, BYVAL ptrMem, bmInfo, _
                              %DIB_RGB_COLORS, %SRCCOPY
                            FUNCTION = %True
                          END IF      '_ err, else
                          '- STEP 8               Clean up after myself
                          '  To ensure we don't have any memory leaks, we need
                          '  to clean up after ourselves.
                          DeleteObject hBitmap
                          DeleteDC dcBitmap
                          gMemory = ""
                        END IF	'_ if printing% = 0, else
                      END IF	'_ if hBitmap = 0, else
                    END IF	'_ if bad structure, else
                  END FUNCTION
                  FUNCTION WinMain(BYVAL hInstance AS LONG, BYVAL hPrevInstance AS LONG, _
                    lpCmdLine AS ASCIIZ PTR, BYVAL iCmdShow AS LONG) AS LONG
                    DIM zCommand AS ASCIIZ * 255
                    DIM i AS LONG
                    DIM dcPrint AS LONG
                    DIM iCopies AS LONG, iFrom AS LONG, iTo AS LONG
                    DIM iMin AS LONG, iMax AS LONG, iOptions AS LONG
                    DIM rDocInfo AS DOCINFO
                    DIM zString AS ASCIIZ * 256
                    '- The command line should contain the name of the bmp file
                    zCommand = @lpCmdLine
                    IF INSTR(zCommand, ".") < 1 THEN
                      zCommand = TRIM$(zCommand) + ".bmp"
                    END IF
                    IF DIR$(zCommand) = "" THEN
                      MsgBox "Usage: printbmp bitmap.bmp" + CHR$(13) + _
                        "Pass the bitmap name on the command line."
                      '- Prompt the user for a printer
                      iOptions = %PD_COLLATE + %PD_HIDEPRINTTOFILE + %PD_RETURNDC + _
                        %PD_NOSELECTION + %PD_PAGENUMS
                      iFrom = 1: iTo = 1: iMin = 1: iMax = 1: iCopies = 1
                      PrinterDialog %NULL, iOptions, dcPrint, iCopies, iFrom, iTo, iMin, iMax
                      IF dcPrint THEN
                        SetCursor LoadCursor(%NULL, BYVAL %IDC_WAIT)
                        '- Init the document
                        zString = "Bitmap Printed: " + zCommand
                        rDocInfo.lpszDocName = VARPTR(zString)
                        rDocInfo.lpszOutput = %NULL
                        StartDoc dcPrint, rDocInfo
                        FOR i = 1 TO iCopies
                          StartPage dcPrint
                          TextOut dcPrint, 200, 200, zString, LEN(zString)
                          '- Here's the big call to do the printing. For test purposes
                          '  I print it in at 1" over, 1" down, 3" wide, and 2" high
                          '  rectangle.
                          BltImage zCommand, %True, 1, 1, 3, 2, dcPrint
                          EndPage dcPrint
                        NEXT i
                        '- Clean up
                        EndDoc dcPrint
                        DeleteDC dcPrint
                        SetCursor LoadCursor(%NULL, BYVAL %IDC_ARROW)
                      END IF	'_ if dcPrint
                    END IF	'_ if bitmap file exist
                  END FUNCTION

                  PowerBASIC Support
                  mailto:[email protected][email protected]</A>

                  Home of the BASIC Gurus


                  • #10
                    Hi - does the Don Dickinson code compile under PB/CC? I get "COMDLG32.INC" not found when I try to compile it. Also, could you post an example of how to use it, please?


                    • #11
                      Dan --

                      COMDLG32.INC is contained in the WIN32API.ZIP file, which can be found here...


                      As for whether or not it works with PB/CC, it should work perfectly.

                      -- Eric

                      Perfect Sync: Perfect Sync Development Tools
                      Email: mailto:[email protected][email protected]</A>

                      [This message has been edited by Eric Pearson (edited February 08, 2000).]
                      "Not my circus, not my monkeys."


                      • #12
                        I use Don Dickinson's code above to print bmp files and it works
                        great. Thanks Don.

                        When a printer dialog opens it comes with "Portrait" orientation as
                        default. How can the default orientation be made to be "Landscape"
                        in the printer dialog ?



                        Gajanan Raje


                        • #13
                          Hi, I couldn't find the "COMDLG32.INC" file either. It is not included in the "WIN32API.ZIP" file from the files-section.
                          Can somebody help?


                          Werner Lentz
                          [email protected]


                          • #14
                            COMDLG32.INC is quite definitely included in the
                            archive. It may also be found in your PB/DLL WINAPI folder.

                            Please remember that the Source Code forum is for source code,
                            not discussions. Topic closed.

                            Tom Hanlin
                            PowerBASIC Staff