Solved

StretchBlt problem on SOME systems

Posted on 1999-01-13
21
606 Views
Last Modified: 2013-12-03
I'm using the 'StretchBlt' API to perform a "print screen" operation. Everything works fine on my system and on some others. On many systems, however, 'StretchBlt' returns zero (=> FALSE => error) and the screen is not printed. Interestingly, the 'GetLastError' function (called right after 'StretchBlt') returns a 0 (i.e. no error)!

All the systems which I tested have a Pentium CPU and are running NT4 with SP3. These systems are connected to the same networked printer (a HP LaserJet 5Si MX). One system has a local printer; 'StretchBlt' fails on that as well. These systems are all quite similar, although they do vary in the color depth of the display. (However, even the system with only 256 colors fails...)

According to the 'GetDeviceCaps' API, the printer and all the screens are capable of performing the 'StretchBlt' function.

It doesn't seem to matter if a user has 'local admin' rights or not.

Does any have a clue as to WHY the 'StretchBlt' function would fail on many systems and yet work fine on some apparently-identical ones?

Thanks,
Wayne
0
Comment
Question by:wfb
  • 7
  • 7
  • 6
  • +1
21 Comments
 
LVL 22

Expert Comment

by:nietod
ID: 1418699
Is the bitmap your are StretchBlt()ing always compatible with the printer?  The screen is not necesarily compatible with the printer.  You might want to use GetDIBits() and StretchDIBlts() instead.

0
 

Author Comment

by:wfb
ID: 1418700
Under what conditions would the screen bitmap NOT be compatible with the printer? Is there some independent want of determining this?

Thanks,
Wayne
0
 
LVL 22

Expert Comment

by:nietod
ID: 1418701
I take back what I said.  Apparently  BitBlt() can be used with incompatible devices.  (Devices that use different representations, as a screen and a printer probably do--expecually since they almost always have different color formats.)  (I don't think this was always true, however,  There was a time when BitBlt() would not work with incompatible devices.)  However, since BitBlt() should work for this, then StretchBlt() will (it use BitBlt()).

I suspect the problem is that the printer is ussually a monochrome device and the screen is not.  Are you setting the background color correctly?  Pixels that match the background color become white and all others become black.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1418702
No that wouldn't explain your problem though.  You would get a black image and StretchBlt() wouldn't return an errer.  
0
 
LVL 1

Expert Comment

by:wirus
ID: 1418703
BitBlt and StretchBlt work with device-dependent bitmaps. Since that, they may fail copying between incompatible devices.
StretchDIBits will work fine, but you have to convert the bitmap taken from the screen into a DIB.
I suggest: create a (true color) DIB with CreateDIBSection, then, using memory device contexts, BitBlt the screen image to that DIB and, after freeing DCs, print the DIB with StretchDIBits.

(I haven't check all of this, It should work, but if it doesn't, just ask... :)) )
0
 

Author Comment

by:wfb
ID: 1418704
It is <possible> that wirus' approach might work, but it is far more involved than the 'StretchBlt' method. I just can't believe that SOME screens are "incompatible" while on other systems with very similar display adapters they seem to be "compatible"! (These are all HP systems with Matrox Millennium display adapters.) Don't forget that 'GetDeviceCaps' reports that the printer and the display support the 'StretchBlt' function.

It really looks like there is some OTHER aspect of the system which is preventing 'StretchBlt' from functioning. Hopefully, someone can suggest what that "other aspect" might be!

Wayne
0
 
LVL 1

Accepted Solution

by:
wirus earned 200 total points
ID: 1418705
The problem is not with StretchBlt or BitBlt compatibility!!! This is the device compatibility problem!!
Just do simple experiment: load a bitmap with LoadImage function and try to print it: if you don't specify LR_CREATEDIBSECTION flag it may fail (you'll propably get a black rectangle). (Printing a DIB with StretchBlt may also fail (I'm not sure), then use StretchDIBits as described in help...).
StretchBlt is not generally prevented from functioning: it just can't copy between different devices if they don't use the same data format. Windows functions do not support converting from one device format to another but they support converting from and to device independent format.
Have you tried blitting bitmaps from pronter dc to the screen? It will, propably, not work (anyway it's pointless), so, why the other direction should?

hope you'll get it working
virus
0
 
LVL 22

Expert Comment

by:nietod
ID: 1418706
I always thought that copying DDB between different devices was impossible, and that is what I said here originally.  But the BitBlt() docs seem to suggest otherwise.
0
 

Author Comment

by:wfb
ID: 1418707
nietod,

It certainly seems possible (in most cases) to copy DDBs between different devices, at least using 'StretchBlt'. I will try 'BitBlt' and see if it behaves the same way. My (old, 1993) documentation for 'BitBlt' does state that "BitBlt returns an error if the source and destination device contexts represent different devices." Have you seen newer docs that say otherwise? (I'll check the MSDN and see what I can find there...)

Wayne

0
 
LVL 22

Expert Comment

by:nietod
ID: 1418708
YES, it does say that.  I missed that.  (And I looked for it.)  What fooled me was the line that says that if the two DCs have different color formats (which is one way in which devices are incompatible) the color format will be converted.  I took that to mean that it could handle incompatible devices.  Nope.

So as I said at the start, use GetDIBits() and StretchDIBits().
0
6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

 

Author Comment

by:wfb
ID: 1418709
wirus,

It seems that your method using DIBs may be the only satisfactory method of doing a "screen copy". Unfortunately, I am not too familiar with this area of Windows programming. Would it be possible for you to show me the steps required (in more detail than you provided above) to use this approach?

Currently, I have the following following information available in my program:

'hDCscreen' for the screen, along with the x & y starting point and the width & height of that part of the screen which contains the image which I wish to copy to the printer.

'hDCprinter' for the printer, along with the x & y starting point and the width & height that I wish for the screen copy to occupy.

In other words, I have all the values needed to call 'StretchBlt', assuming that that routine was ALWAYS able to copy from the screen to the printer. (As I mentioned before, 'StretchBlt' seems to work fine on MOST systems.)

BTW, I also tried the following approach:

1. Call 'CreateCompatibleBitmap' using 'hDCprinter' to create 'hBitmap'.

2. Call 'CreateCompatibleDC' using 'hDCprinter' to create 'hDCmem'

3. Call 'SelectObject' to select 'hBitmap'(1) into device context 'hDCmem'(2)

4. 'BitBlt' from 'hDCscreen' to 'hDCmem'

5. 'StretchBlt' from 'hDCmem' to 'hDCprinter'.

This approach seems to work, BUT the resulting "screen print" does not retain the (required!) grayscale information! Thoughts?

All help would be greatly appreciated!

Wayne
0
 
LVL 1

Expert Comment

by:wirus
ID: 1418710
hi,

I'm copying working code here (parts were taken from two working projects, but I haven't checked them together (well, I think it'll work...))...

1. this will create the DIB I was writing about and put its data address into lpBits pointer

HBITMAP init24bitdibsection(unsigned long width, unsigned long height,
      BITMAP& bitmap,      void*& lpBits)
{
      BITMAPINFOHEADER bmiHeader;
      LPBITMAPINFO bmi = (LPBITMAPINFO) &bmiHeader;
      memset(&bmiHeader, 0, sizeof(bmiHeader));
      bmiHeader.biSize = sizeof bmiHeader;
      bmiHeader.biWidth = width;
      bmiHeader.biHeight = height;
      bmiHeader.biPlanes = 1;
      bmiHeader.biBitCount = 24;
      bmiHeader.biCompression = BI_RGB;
      bmiHeader.biXPelsPerMeter = 1000;
      bmiHeader.biYPelsPerMeter = 1000;
      HBITMAP hbmp = ::CreateDIBSection(NULL, bmi, DIB_RGB_COLORS,
            &lpBits, NULL, 0);
      if (hbmp)
            ::GetObject(hbmp, sizeof(bitmap), (LPVOID*) &bitmap);
      return hbmp;
}

BITMAP bmp;
LPVOID lpBits;
HBITMAP hDib = init24bitdibsection(sourcewidth, sourceheight, bmp, lpBits);
if (hDib)
ERROR( "Something sucks");

2. call CreateCompatibleDC with your hDCscreen (or NULL, then screen dc will be taken automatically)

HDC memdc = CreateCompatibleDC(NULL);

3. select the DIB into that memdc, and blt the screen rectangle into in (you wrote, you have all the info required for this)

// remember to restore oldb before deleting this context
HBITMAP oldb = (HBITMAP) SelectObject(memdc, hDib);

BitBlt(memdc, 0, 0, bmp.bmWidth, bmp.bmHeight, hDCscreen, sourcex, sourcey, SRCCOPY);

4. call SetStretchBltMode to make the picture look right :)

SetStretchBltMode(hDCprinter, HALFTONE);

5. use StretchDIBits like this:

BITMAPINFOHEADER bmih;
memset(&bmih, 0, sizeof(bmih);
bmih.biSize = sizeof bmih;
bmih.biWidth = bmp.bmWidth;
bmih.biHeight = bmp.bmHeight;
bmih.biPlanes = 1;
bmih.biBitCount = bmp.bmBitsPixel;
bmih.biCompression = BI_RGB;

::StretchDIBits(hDCpronter, imagerect.left, imagerect.top, imagerect.Width(), imagerect.Height(), 0, 0, bmp.bmWidth, bmp.bmHeight, lpBits,(LPBITMAPINFO)&bmih,DIB_RGB_COLORS, SRCCOPY);

have a nice debugging :))
\Virus

0
 
LVL 1

Expert Comment

by:wirus
ID: 1418711
one more:

I don't know if your current solution works or not:
before you rewrite it just try SetStretchBltMode to correct the problem

Viva la Revolution!
Manny Calavera

0
 
LVL 1

Expert Comment

by:wirus
ID: 1418712
and one more

there is:

if (hDib)
ERROR(".....")

should be:

if (!hDib)
ERROR("...")

sorry about that
\Virus

0
 

Author Comment

by:wfb
ID: 1418713
wirus,

I've just started working on your suggested 'StretchDIBits' approach. It seems that something is wrong with your call to 'CreateDIBSection': my documentation shows only 4 arguments:

  HDC hdc; // handle of device context
  LPBITMAPINFO lpInfo; // address of bitmap data
  DWORD fInit;  // scan line order
  DWORD iUsage;  // color format

Your code, on the other hand, has 6 arguments for 'CreateDIBSection' and I can't really tell what the proper arguments should be!

Hope you can clear this up,

Wayne
0
 

Author Comment

by:wfb
ID: 1418714
wirus,

I've just looked at some later documentation for 'CreateDIBSection' and it seems that your are right - there should be 6 arguments! Sorry for the confusion.

Wayne
0
 

Author Comment

by:wfb
ID: 1418715
wirus,

I've got your DIB approach to work, even on the system which did not seem to support the 'StretchBlt' method! Many thanks!

Just two remaining questions/comments, if I might:

(1) After the call to 'StretchDIBits', is this the proper way to "clean up" & release memory:

  SelectObject (memdc,oldb);  // Reselect old object before
  DeleteDC (memdc);           // deleting the memory DC.
  DeleteObject (hDib);        // Delete DIB bitmap.

Have I left out anything?

(2) I can reduce the amount of data sent to the printer if I change 'biBitCount' from 24 to 16 (while leaving 'biCompression' set to BI_RGB). Is there any reason NOT to use a value of 16 in all cases? (A value of 8 causes a VERY dark image to be printed.)

Thanks again,

Wayne
0
 
LVL 1

Expert Comment

by:wirus
ID: 1418716
(1) Yes, it's right. (You can't free dib's memory yourself.)
(2) Well, I'm not sure. Generally some devices seem not to support 16bpp images (I've read it somewhere but I haven't had such problems, so, I'm not sure...)
Actually, the amount of data sent to printer depends on the printer dc bpp. Why?- Stretching the image from 75dpi or less  to 300 or over means means increasing it's size even a few times (writing a large amount of data in the destination format). So, I think it would be safer to leave the DIB with 24bpp (for compatibility reasons).

says
\Virus

0
 

Expert Comment

by:demonP
ID: 1418717
wirus,
     I found the following type-cast in your answer:

BITMAPINFOHEADER bmiHeader;
LPBITMAPINFO bmi = (LPBITMAPINFO) &bmiHeader;

Do you think it is safe to use ? I was a little
afraid system will read some meaningless byte outside
the BITMAPINFOHEADER struct to fill "RGBQUAD          bmiColors[1]; ". In this case , is bmiColors[] not important
to the following call ?



One more question, what the "1000" mean ?
Do you mean "any value big enough" ?

bmiHeader.biXPelsPerMeter = 1000;
bmiHeader.biYPelsPerMeter = 1000;


Thanks .

0
 
LVL 1

Expert Comment

by:wirus
ID: 1418718
(1) System checks BITMAPINFOHEADER fields to determine number of aditional RGBQUAD fields. Since BITMAPINFOHEADER structure supports size checking (ev. version checking -> biSize field) you can define EXACT size of BITMAPINFO structure using BITMAPINFOHEADER fields. System will not read any aditional bytes because contents of BITMAPINFOHEADER fields say there are no aditional data.

(2) There is bmiColors[1] instead of bmiColors[] because some compilers may nor support zero-sized arrays.

(3)
> bmiHeader.biXPelsPerMeter = 1000;
> bmiHeader.biYPelsPerMeter = 1000;
Sorry about that. I wrote I used parts of working and tested code. These fields should be initialized to any value (I haven't checked if 0 is a valid one too, but I think it would be ok...).
I've used value of 1000 because at the very beginning of developing this part of program I used these fields to determine stretch factor (I printed bitmaps 300x300, one inch is about 3 cm, so I assumed there're 1000 pixels per meter for 300dpi), currently they are not used and have no meaning but I haven't removed the code up till now... :)))

\Virus
0
 
LVL 22

Expert Comment

by:nietod
ID: 1418719
>> >bmiHeader.biXPelsPerMeter = 1000;
>>   > bmiHeader.biYPelsPerMeter = 1000;
>>    Sorry about that. I wrote I used parts of working and tested code.
>> These fields should be initialized to any value (I haven't checked if 0
>>  is a valid one too, but I think it would be ok...).
These fields are used by the OS (and maybe some apps) when there are multiple images designed for different resolution to choose from for a task.  (This may be used in loading cursors and icons or other resources.)  That does not apply in this case.   You can set it to 0 to indicate the resolution does not matter.
0

Featured Post

What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

Join & Write a Comment

This article describes a technique for converting RTF (Rich Text Format) data to HTML and provides C++ source that does it all in just a few lines of code. Although RTF is coming to be considered a "legacy" format, it is still in common use... po…
What my article will show is if you ever had to do processing to a listbox without being able to just select all the items in it. My software Visual Studio 2008 crystal report v11 My issue was I wanted to add crystal report to a form and show…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
This video gives you a great overview about bandwidth monitoring with SNMP and WMI with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're looking for how to monitor bandwidth using netflow or packet s…

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now