Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Using CImage?

984 views
Skip to first unread message

David Webber

unread,
Oct 8, 2008, 8:57:24 AM10/8/08
to
I am trying to use CImage for the first time. I give the code
schematically below. CImage::Save() is not working for me.

Am I missing some important step? The HRESULT indicates an unknown error.

(Stepping through image.Save(), I see it succeeding until the last step

Gdiplus::Bitmap bm( m_hBitmap, NULL );
status = bm.Save( pwszFileName, &clsidEncoder, NULL );

which fails.)


================
The code:
Schematically I have

HBITMAP hBitmap = ......;

// I then call a debugging routine which displays the bitmap
// in a dialogue: it looks ok.
// I now want to save it as a GIF

TCHAR szFilename[ ] = _T(".... .gif");

// This is a UNICODE string.
// It gives the full path to a non-existent file in
// and existing directory (which is not write protected).

CImage image;
image.Attach( hBitmap );
HRESULT hResult = image.Save( szFilename );
image.Detach();

==============

Any ideas?

Dave
--
David Webber
Author of 'Mozart the Music Processor'
http://www.mozart.co.uk
For discussion/support see
http://www.mozart.co.uk/mozartists/mailinglist.htm

Giovanni Dicanio

unread,
Oct 8, 2008, 9:58:51 AM10/8/08
to

"David Webber" <da...@musical-dot-demon-dot-co.uk> ha scritto nel messaggio
news:euex1VUK...@TK2MSFTNGP03.phx.gbl...

>I am trying to use CImage for the first time. I give the code
>schematically below. CImage::Save() is not working for me.
>
> Am I missing some important step? The HRESULT indicates an unknown
> error.

Hi Dave,

I think that CImage is a part of GDI+, so maybe it is required to initialize
GDI+.
Do you call GdiplusStartup() and GdiplusShutdown properly in your code?

(I'm not sure if CImage does that in its constructor and destructor.)

Giovanni


Giovanni Dicanio

unread,
Oct 8, 2008, 10:07:08 AM10/8/08
to

"Giovanni Dicanio" <giovanniD...@REMOVEMEgmail.com> ha scritto nel
messaggio news:%23BktQ4U...@TK2MSFTNGP06.phx.gbl...

> I think that CImage is a part of GDI+, so maybe it is required to
> initialize GDI+.
> Do you call GdiplusStartup() and GdiplusShutdown properly in your code?

Sorry, I confused *C*Image (which is from ATL) with Image (GDI+) :

http://msdn.microsoft.com/en-us/library/ms534462.aspx

However, you may want to try GDI+'s Image instead of CImage.

Or, if you want to still use CImage, I would try passing an explicit value
for 'guidFileType' parameter in CImage::Save() method:

http://msdn.microsoft.com/en-us/library/ms534462.aspx

As a final note, I really like the CxImage class:

http://www.codeproject.com/KB/graphics/cximage.aspx

Giovanni

Seetharam

unread,
Oct 8, 2008, 10:46:59 AM10/8/08
to
The following code works for me:

#include <atlimage.h>
DEFINE_GUID(ImageFormatBMP, 0xb96b3cab,0x0728,0x11d3,0x9d,0x7b,
0x00,0x00,0xf8,0x1e,0xf3,0x2e);
-----
void SaveImage(HBITMAP hBmp )
{
CImage img;
img.Attach(hBmp); // hBmp is a valid HBITMAP
CString str(_T("c:\\temp\\test.bmp");
img.Save(str,ImageFormatBMP);
img.Detach();
}

-SM

David Webber

unread,
Oct 8, 2008, 12:15:06 PM10/8/08
to

"Giovanni Dicanio" <giovanniD...@REMOVEMEgmail.com> wrote in message
news:Oclm%238UKJ...@TK2MSFTNGP04.phx.gbl...

>> I think that CImage is a part of GDI+, so maybe it is required to
>> initialize GDI+.
>> Do you call GdiplusStartup() and GdiplusShutdown properly in your code?

Thanks Giovanni.

Yes, as it happens. And I have now moved my image saving code to the DLL
from which these are called - with no difference in result.

> Sorry, I confused *C*Image (which is from ATL) with Image (GDI+) :
>
> http://msdn.microsoft.com/en-us/library/ms534462.aspx
>
> However, you may want to try GDI+'s Image instead of CImage.

CImage::Save() actually creates a GDI+ Bitmap and uses Bitmap::Save(...);

Stepping through it, it is happy that GDI+ is initialised and appears to
retrieve the "encoder" for a gif file corectly, but Bitmap::Save() (the
last call of CImage::Save() ) fails.

> Or, if you want to still use CImage, I would try passing an explicit value
> for 'guidFileType' parameter in CImage::Save() method:

It doesn't make any difference :-(

And doing it directly from the Gdiplus::Bitmap() has exactly the same
problem.

This is really frustrating!

> As a final note, I really like the CxImage class:
>
> http://www.codeproject.com/KB/graphics/cximage.aspx
>

I'll have a look, but I was trying to do it without recourse to third party
software. I have been using the excellent "FreeImage" for many years now,
but I'd hoped that standard library functions would do the trick these days.
I still can't see why they don't.

Giovanni Dicanio

unread,
Oct 8, 2008, 12:59:13 PM10/8/08
to

"David Webber" <da...@musical-dot-demon-dot-co.uk> ha scritto nel messaggio
news:eEi6lEW...@TK2MSFTNGP03.phx.gbl...

>>> Do you call GdiplusStartup() and GdiplusShutdown properly in your code?
>
> Thanks Giovanni.
>
> Yes, as it happens. And I have now moved my image saving code to the DLL
> from which these are called - with no difference in result.

Reading the source code of CImage class in <atlimage.h> (in VS2008), it can
be observed the presence of a static private data member in CImage:
's_initGDIPlus', which is a static instance of class CInitGDIPlus, whose
purpose in life seems to be to initialize and shutdown GDI+ properly.
So, it seems that there is no need of explicit GdiplusStartup/Shutdown() to
use CImage.

> CImage::Save() actually creates a GDI+ Bitmap and uses Bitmap::Save(...);
>
> Stepping through it, it is happy that GDI+ is initialised and appears to
> retrieve the "encoder" for a gif file corectly, but Bitmap::Save() (the
> last call of CImage::Save() ) fails.

You are referring to this code, correct?

Gdiplus::Bitmap bm( m_hBitmap, NULL );
status = bm.Save( pwszFileName, &clsidEncoder, NULL );

if( status != Gdiplus::Ok )
{
return E_FAIL.
}

Have you watched the actual value of 'status' after Gdiplus::Bitmap::Save()
method returns?
This could lead to meaningful information, better than the generic E_FAIL
HRESULT value.


> And doing it directly from the Gdiplus::Bitmap() has exactly the same
> problem.
>
> This is really frustrating!

I agree with you.
There must be some subtle bug here...
Instead these common-use classes should be designed and implemented to be
easy to use, and not cause subtle bug-hunting like this...

Giovanni

Giovanni Dicanio

unread,
Oct 8, 2008, 1:17:41 PM10/8/08
to
I created a simple dialog-based MFC application using VS2008, added a button
and the following handler:

<code>

void CTestCImageDlg::OnBnClickedButton1()
{
CString imageSrcFilename = TEXT("image1.gif");
CString imageDestFilename = TEXT("image1_copy.gif");

HRESULT hr;

CImage src;
hr = src.Load( imageSrcFilename );
ASSERT( SUCCEEDED(hr) );

HBITMAP hbmp = src.Detach();
ASSERT( hbmp != NULL );

CImage dest;
dest.Attach( hbmp );
hr = dest.Save( imageDestFilename );
ASSERT( SUCCEEDED(hr) );

dest.Detach();
}

</code>

and it works fine...

Weired...

Dave: are you sure that you don't have any kind of lock on the file you are
going to create? Is the directory writable?

Giovanni


David Webber

unread,
Oct 8, 2008, 1:44:33 PM10/8/08
to

"Giovanni Dicanio" <giovanniD...@REMOVEMEgmail.com> wrote in message
news:OSOsmnWK...@TK2MSFTNGP04.phx.gbl...

>I created a simple dialog-based MFC application using VS2008, added a
>button and the following handler:

>....
> and it works fine...

Thanks for your efforts ....

> Dave: are you sure that you don't have any kind of lock on the file you
> are going to create? Is the directory writable?

It is starting to look like that might be possible. The directory is
created within my "appdata" path using default security, so it *ought* to
be writable, as that's what appdata is for!

I'll check.

I'm getting encouraged that there must be something simple wrong. Perhaps
a reboot?

Giovanni Dicanio

unread,
Oct 8, 2008, 1:44:32 PM10/8/08
to

"David Webber" <da...@musical-dot-demon-dot-co.uk> ha scritto nel messaggio
news:u3frFxWK...@TK2MSFTNGP05.phx.gbl...

> (I am using Vista).

During the successful tests I did I was instead using XP SP2...

I'm not sure if it is an OS-related issue...

Giovanni


David Webber

unread,
Oct 8, 2008, 1:35:04 PM10/8/08
to

"Seetharam" <smi...@gmail.com> wrote in message
news:92f4416b-f2c6-4151...@u28g2000hsc.googlegroups.com...

Thanks. I *thought* it ought to be as simple as that.

I have tried this pretty much directly now (except using ImageFormatBMP from
the MS header file - but it's the same value) and it still fails (so it
wasn't just a problem with the GIF option).

(I am using Vista).

This is extremely annoying!

David Webber

unread,
Oct 8, 2008, 2:50:08 PM10/8/08
to

"Giovanni Dicanio" <giovanniD...@REMOVEMEgmail.com> wrote in message
news:OSOsmnWK...@TK2MSFTNGP04.phx.gbl...

> Dave: are you sure that you don't have any kind of lock on the file you
> are going to create? Is the directory writable?

The GDI+ Bitmap::Save() method is returning a status

Win32Error

about which, the help helpfully tells me "Indicates that the method
generated a Microsoft Win32 error".

But it is not any of

AccessDenied (Indicates that a write operation is not allowed on the
specified file. )
UnknownImageFormat (Indicates that the specified image file format is not
known.)
GdiplusNotInitialized
InvalidParameter
...

So all I have to do is work out what a "Win32Error" is when it's at home :-(

David Lowndes

unread,
Oct 8, 2008, 6:51:00 PM10/8/08
to
>So all I have to do is work out what a "Win32Error" is when it's at home :-(

Does calling GetLastError immediately afterwards give any relevant
information?

Dave

Mark Salsbery [MVP]

unread,
Oct 8, 2008, 7:18:21 PM10/8/08
to
Only 8 bpp images can be saved as GIF AFAIK.

Have you tried saving to BMP (just to test) or does that fail too?

Mark

--
Mark Salsbery
Microsoft MVP - Visual C++

"David Webber" <da...@musical-dot-demon-dot-co.uk> wrote in message
news:euex1VUK...@TK2MSFTNGP03.phx.gbl...

Mark Salsbery [MVP]

unread,
Oct 8, 2008, 7:23:08 PM10/8/08
to
"David Webber" <da...@musical-dot-demon-dot-co.uk> wrote in message
news:euex1VUK...@TK2MSFTNGP03.phx.gbl...
> I am trying to use CImage for the first time. I give the code
> schematically below. CImage::Save() is not working for me.
>
> Am I missing some important step? The HRESULT indicates an unknown
> error.
>
> (Stepping through image.Save(), I see it succeeding until the last step
>
> Gdiplus::Bitmap bm( m_hBitmap, NULL );
> status = bm.Save( pwszFileName, &clsidEncoder, NULL );

Also, what is the value of status after the Save() call here?

(sorry if you already answered that - I'm not seeing it anywhere :))

David Webber

unread,
Oct 9, 2008, 7:52:46 AM10/9/08
to

"Mark Salsbery [MVP]" <MarkSalsbery[MVP]@newsgroup.nospam> wrote in message
news:uUufWzZK...@TK2MSFTNGP04.phx.gbl...


Thanks Mark - yes it fails on saving as a bmp as well,,,

> Also, what is the value of status after the Save() call here?

... and the error is status=Win32Error.

> (sorry if you already answered that - I'm not seeing it anywhere :))

No problem - I'm getting desperate and all attempts to help are welcome!
I'll try GetLastError as David suggests, and also I'll experiment with a
different hBitmap.

I have obtained this one by a weird and wonderful route, and whilst I can
see it in my home-made debugging dialogue for showing bitmaps, there may be
something odd about it.

(I got a CImageList from a CToolbarCtrl, and then got an icon from the image
list, and then got the hBitmap from the icon. The Gdiplus::Bitmap can be
created directly from an hIcon, and I've tried that too, but trying to save
it fails in the same way.)

David Webber

unread,
Oct 9, 2008, 9:08:51 AM10/9/08
to

"David Lowndes" <Dav...@example.invalid> wrote in message
news:4odqe4pf2iuh4bcff...@4ax.com...

Eureka!

It tells me I have made a silly programming error, and am trying to save the
file in a directory which does not exist, (but one which has a very long
name which happens to be very similar to one which does exist) :-(

Slaps forehead :-(

Correcting this has fixed it!

I thought the Gdiplus::Status would have told me this, but now, slightly
older, slightly wiser, and with a lot less hair, I know to use GetLastError,
even when I have a Gdiplus::Status.

Sincerest (and rather embarrassed) thanks to you, and everyone who has
helped on this!

David Lowndes

unread,
Oct 9, 2008, 1:42:06 PM10/9/08
to
>> >So all I have to do is work out what a "Win32Error" is when it's at home
>> >:-(
>>
>> Does calling GetLastError immediately afterwards give any relevant
>> information?
>
>Eureka!

Great. I'm glad that's helped.

>I thought the Gdiplus::Status would have told me this, but now, slightly
>older, slightly wiser, and with a lot less hair, I know to use GetLastError,
>even when I have a Gdiplus::Status.

I don't know for sure, but I suspect it might only be applicable to
use GLE when the status returns Win32Error.

Dave

0 new messages