Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 783
  • Last Modified:

StretchBlt problem on SOME systems

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
wfb
Asked:
wfb
  • 7
  • 7
  • 6
  • +1
1 Solution
 
nietodCommented:
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
 
wfbAuthor Commented:
Under what conditions would the screen bitmap NOT be compatible with the printer? Is there some independent want of determining this?

Thanks,
Wayne
0
 
nietodCommented:
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
Visualize your virtual and backup environments

Create well-organized and polished visualizations of your virtual and backup environments when planning VMware vSphere, Microsoft Hyper-V or Veeam deployments. It helps you to gain better visibility and valuable business insights.

 
nietodCommented:
No that wouldn't explain your problem though.  You would get a black image and StretchBlt() wouldn't return an errer.  
0
 
wirusCommented:
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
 
wfbAuthor Commented:
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
 
wirusCommented:
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
 
nietodCommented:
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
 
wfbAuthor Commented:
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
 
nietodCommented:
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
 
wfbAuthor Commented:
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
 
wirusCommented:
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
 
wirusCommented:
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
 
wirusCommented:
and one more

there is:

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

should be:

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

sorry about that
\Virus

0
 
wfbAuthor Commented:
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
 
wfbAuthor Commented:
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
 
wfbAuthor Commented:
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
 
wirusCommented:
(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
 
demonPCommented:
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
 
wirusCommented:
(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
 
nietodCommented:
>> >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

Efficient way to get backups off site to Azure

This user guide provides instructions on how to deploy and configure both a StoneFly Scale Out NAS Enterprise Cloud Drive virtual machine and Veeam Cloud Connect in the Microsoft Azure Cloud.

  • 7
  • 7
  • 6
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now