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

Image.SetPropertyItem does not work

505 views
Skip to first unread message

peter

unread,
Sep 8, 2009, 10:09:46 PM9/8/09
to
I want to use Item.SetPropertyItem to save property changes. I found an
microsoft example at
http://msdn.microsoft.com/en-us/library/system.drawing.image.getpropertyitem%28VS.80%29.aspx

I copy as following, and I modified the jpg files to my files and change the
property id to 40092, that is Comments of JPG property.

The code does not work. Could anybody tell me what is the problem or how can
I save property's values?

private void DemonstratePropertyItem(PaintEventArgs e)
{

// Create two images.
Image image1 = Image.FromFile("c:\\MyPhoto1.jpg");
Image image2 = Image.FromFile("c:\\MyPhoto2.jpg");

// Get a PropertyItem from image1.
PropertyItem propItem = image1.GetPropertyItem(40092);

// Change the ID of the PropertyItem.
propItem.Id = 40092;

// Set the PropertyItem for image2.
image2.SetPropertyItem(propItem);

// Draw the image.
e.Graphics.DrawImage(image2, 20.0F, 20.0F);
}


Family Tree Mike

unread,
Sep 8, 2009, 10:30:01 PM9/8/09
to

Your code example does not actually save image2 back to disk after
modifying the comments. Put image2.Save("somefilename.jpg") in the code
after making the change to the comments.

--
Mike

peter

unread,
Sep 9, 2009, 2:20:38 AM9/9/09
to
Hi Mike,

Thank u first.

I add the

image2.Save("D:\\PeterTemp\\pic\\d2.jpg");

in the code, but it cause an error message: A generic error occurred in
GDI+. and the comments was not saved.

code is copied below.

private void Form1_Paint(object sender, PaintEventArgs e)

{

ExtractMetaData(e);

// Create two images.

Image image1 = Image.FromFile("D:\\PeterTemp\\pic\\d1.jpg");

Image image2 = Image.FromFile("D:\\PeterTemp\\pic\\d2.jpg");

// Get a PropertyItem from image1.

PropertyItem propItem = image1.GetPropertyItem(40092);

// Change the ID of the PropertyItem.

//propItem.Id = 40092;

// Set the PropertyItem for image2.

image2.SetPropertyItem(propItem);

image2.Save("D:\\PeterTemp\\pic\\d2.jpg");

// Draw the image.

//e.Graphics.DrawImage(image2, 20.0F, 20.0F);

}

"Family Tree Mike" <FamilyT...@ThisOldHouse.com> wrote in message
news:Oxb%23sVPMK...@TK2MSFTNGP06.phx.gbl...

Peter Duniho

unread,
Sep 9, 2009, 3:52:25 AM9/9/09
to
On Tue, 08 Sep 2009 23:20:38 -0700, peter <peter...@hotmail.com> wrote:

> Hi Mike,
>
> Thank u first.
>
> I add the
>
> image2.Save("D:\\PeterTemp\\pic\\d2.jpg");
>
> in the code, but it cause an error message: A generic error occurred in
> GDI+. and the comments was not saved.

I can't speak for the specific example, but I commonly run into all sorts
of problems with generic GDI+ errors when I try to do sophisticated things
with images. GDI+ is very limited in what it can handle, and usually when
you get one of these uninformative generic errors, it just means you've
asked GDI+ to do something that hasn't been implemented in it.

In your example, you appear to be trying to change the EXIF "Description"
tag's property value. I don't recall whether I've ever tried to modify
that particular property in a JPEG file before, but it may well be that
GDI+ simply just doesn't support that particular tag.

Pete

Family Tree Mike

unread,
Sep 9, 2009, 5:32:40 PM9/9/09
to
peter wrote:
> Hi Mike,
>
> Thank u first.
>
> I add the
>
> image2.Save("D:\\PeterTemp\\pic\\d2.jpg");
>
> in the code, but it cause an error message: A generic error occurred in
> GDI+. and the comments was not saved.
>
> code is copied below.
>
> private void Form1_Paint(object sender, PaintEventArgs e)
>
> {
>
> ExtractMetaData(e);
>
> // Create two images.
>
> Image image1 = Image.FromFile("D:\\PeterTemp\\pic\\d1.jpg");
>
> Image image2 = Image.FromFile("D:\\PeterTemp\\pic\\d2.jpg");
>
> // Get a PropertyItem from image1.
>
> PropertyItem propItem = image1.GetPropertyItem(40092);
>
> // Change the ID of the PropertyItem.
>
> //propItem.Id = 40092;
>
> // Set the PropertyItem for image2.
>
> image2.SetPropertyItem(propItem);
>
> image2.Save("D:\\PeterTemp\\pic\\d2.jpg");
>
> // Draw the image.
>
> //e.Graphics.DrawImage(image2, 20.0F, 20.0F);
>
> }
>
>
>


I yield to Pete on the GDI+ (and most other!) issues.

Are you sure you know where the error occurs? You added a new method
(ExtractMetaData(e)) which would appear to work with the PaintEventArgs.
You also moved this into a paint handler. Changing image properties
in a paint handler is not a normal place. I would try and narrow down
where the error actually happens.


--
Mike

peter

unread,
Sep 10, 2009, 8:33:49 PM9/10/09
to

"Family Tree Mike" <FamilyT...@ThisOldHouse.com> wrote in message
news:%23xd4MUZ...@TK2MSFTNGP06.phx.gbl...


The ExtractMetaData(e) was copied from MSDN, it just display the Id, type of
properties. I removed the function anyway.

The error : A generic error occurred in GDI+. was positively from
image2.Save("D:\\PeterTemp\\pic\\d2.jpg"); Because I put a breakpoint on it.

Thanks

Peter


kndg

unread,
Sep 10, 2009, 9:39:30 PM9/10/09
to

Hi Peter,

I did try the MSDN example and it work flawlessly.
The problem you are having is due to the file "d2.jpg" is being locked
by the GDI+ and cause the saving operation to fail.
One solution is to create another folder in "pic" folder and save the
updated image to that folder.

image2.Save("D:\\PeterTemp\\pic\\output\\d2.jpg")

Regards.

peter

unread,
Sep 14, 2009, 8:22:02 PM9/14/09
to

"kndg" <re...@this.newsgroup> wrote in message
news:%23qsANEo...@TK2MSFTNGP04.phx.gbl...


Hi,

Thanks very much!!!

d2.jpg was downloaded from a web site. I saved it to somewhere else it still
not working.
Rather than use d2.jpg, I use MyPic.jpg that comes from my camera, then if I
save it to somewhere else, it worked!

So, the question is how can I let the picture not locked by GDI+, so that I
don't have to save the picture to other location?

Second question is why the picture from internet cannot be modified?


Peter Duniho

unread,
Sep 14, 2009, 8:42:36 PM9/14/09
to
On Mon, 14 Sep 2009 17:22:02 -0700, peter <peter...@hotmail.com> wrote:

> d2.jpg was downloaded from a web site. I saved it to somewhere else it
> still
> not working.
> Rather than use d2.jpg, I use MyPic.jpg that comes from my camera, then
> if I
> save it to somewhere else, it worked!
>
> So, the question is how can I let the picture not locked by GDI+, so
> that I
> don't have to save the picture to other location?

You can release the lock by creating a duplicate of the image you loaded
from the file and then disposing the original.

My apologies for not noticing the identical load and save filenames; I
should have caught that as a potential problem.

> Second question is why the picture from internet cannot be modified?

Can you please clarify? From your post, it sounds like you have a file
that even if you don't try to overwrite the original, still cannot be
saved. Is that correct? Do you get the exact same error you got when you
try to write over a locked file? Can you provide a concise-but-complete
code example, along with a link to download the specific image file?

Pete

kndg

unread,
Sep 14, 2009, 10:51:02 PM9/14/09
to
> [...]

> d2.jpg was downloaded from a web site. I saved it to somewhere else it still
> not working.
> Rather than use d2.jpg, I use MyPic.jpg that comes from my camera, then if I
> save it to somewhere else, it worked!
>
> So, the question is how can I let the picture not locked by GDI+, so that I
> don't have to save the picture to other location?
>

By releasing the lock. :)
Actually, it is not easy and require some works.

One way is by putting some indirection to the original file.
Example,

string filename1 = "D:\\PeterTemp\\pic\\d1.jpg";
string filename2 = "D:\\PeterTemp\\pic\\d2.jpg";

FileStream fileStream = new FileStream(filename2, FileMode.Open);
byte[] buffer = new byte[fileStream.Length];
fileStream.Read(buffer, 0 , buffer.Length);
fileStream.Dispose(); // release the lock

MemoryStream memoryStream = new MemoryStream(buffer);
Image image1 = Image.FromFile(filename1);
Image image2 = Image.FromStream(memoryStream);

PropertyItem propItem = image1.GetPropertyItem(40092);
propItem.Id = 40092;
image2.SetPropertyItem(propItem);
image2.Save(filename2);

Another is by actually re-draw the original image.

Image image1 = Image.FromFile(filename1);
Image image2 = Image.FromFile(filename2);
Image image3 = new Bitmap(image2.Width, image2.Height);

Graphics g = Graphics.FromImage(image3);
g.DrawImage(image2, 0, 0);

PropertyItem[] metadatas = image2.PropertyItems;

foreach(var metadata in metadatas)
{
image3.SetPropertyItem(metadata);
}

PropertyItem property = image1.GetPropertyItem(40092);
property.Id = 40092;

image2.Dispose(); // release the lock

image3.SetPropertyItem(property);
image3.Save(filename2, ImageFormat.Jpeg);

But, take note that the above two methods will put some pressure on your
system if the image file is very large. Memory is expensive while disk
space is not. You have to weight the options.

Regards.

Peter Duniho

unread,
Sep 15, 2009, 1:12:47 AM9/15/09
to
On Mon, 14 Sep 2009 19:51:02 -0700, kndg <re...@this.newsgroup> wrote:

> [...]


> Another is by actually re-draw the original image.
>
> Image image1 = Image.FromFile(filename1);
> Image image2 = Image.FromFile(filename2);
> Image image3 = new Bitmap(image2.Width, image2.Height);
>
> Graphics g = Graphics.FromImage(image3);
> g.DrawImage(image2, 0, 0);

First, if you're going to do the above, you need to remember to dispose
the Graphics instance. Second, if you're just duplicating the image
exactly, there's no need to get a Graphics instance in the first place.
Just use the Bitmap constructor that takes an Image as an argument, and
.NET will do the copying for you automatically (added bonus: no Graphics
instance at all, never mind one you need to dispose :) ).

> PropertyItem[] metadatas = image2.PropertyItems;
>
> foreach(var metadata in metadatas)
> {
> image3.SetPropertyItem(metadata);
> }
>
> PropertyItem property = image1.GetPropertyItem(40092);
> property.Id = 40092;
>
> image2.Dispose(); // release the lock
>
> image3.SetPropertyItem(property);
> image3.Save(filename2, ImageFormat.Jpeg);
>
> But, take note that the above two methods will put some pressure on your
> system if the image file is very large. Memory is expensive while disk
> space is not. You have to weight the options.

The memory/disk space disparity is somewhat of a fallacy, and especially
on a 64-bit OS (which is becoming more and more common). As long as one
is not approaching the limits of the process's virtual address space (as
is often the case even on 32-bit Windows, and this is especially true when
dealing with image files, as they aren't usually large enough to put
pressure on the available address space, at least not on their own),
available memory and available disk space are basically the same thing.

Now, all that said...there's no need to copy the bitmap in order to avoid
the locked file. I suggested that earlier because it's the easiest
approach. But if one is really concerned about memory usage, then neither
of the methods suggested here (copying the file contents into a
MemoryStream, or copying the image itself) are appropriate. Instead, one
should simply open the file directly, using a FileStream constructor
overload that allows specification of the FileShare.ReadWrite value, so
that the file is not actually locked in the first place. For example:

string strFilename = ...;
FileStream stream = new FileStream(strFilename, FileMode.Open,
FileAccess.Read, FileShare.ReadWrite);

using (Image imgOriginal = Image.FromStream(stream))
{
// do stuff to image here

imgOriginal.Save(strFilename);
}

That way you only ever have one copy of the file in memory.

Pete

Peter Duniho

unread,
Sep 15, 2009, 1:33:04 AM9/15/09
to
On Mon, 14 Sep 2009 22:12:47 -0700, Peter Duniho
<no.pet...@no.nwlink.spam.com> wrote:

> [...] For example:


>
> string strFilename = ...;
> FileStream stream = new FileStream(strFilename, FileMode.Open,
> FileAccess.Read, FileShare.ReadWrite);
>
> using (Image imgOriginal = Image.FromStream(stream))
> {
> // do stuff to image here
>
> imgOriginal.Save(strFilename);
> }
>
> That way you only ever have one copy of the file in memory.

And of course, don't forget to dispose the FileStream too, once you're
really done with it (I failed to show that in the little snippet above).

kndg

unread,
Sep 15, 2009, 2:05:49 AM9/15/09
to
Peter Duniho wrote:
> On Mon, 14 Sep 2009 19:51:02 -0700, kndg <re...@this.newsgroup> wrote:
>
>> [...]
>> Another is by actually re-draw the original image.
>>
>> Image image1 = Image.FromFile(filename1);
>> Image image2 = Image.FromFile(filename2);
>> Image image3 = new Bitmap(image2.Width, image2.Height);
>>
>> Graphics g = Graphics.FromImage(image3);
>> g.DrawImage(image2, 0, 0);
>
> First, if you're going to do the above, you need to remember to dispose
> the Graphics instance. Second, if you're just duplicating the image
> exactly, there's no need to get a Graphics instance in the first place.
> Just use the Bitmap constructor that takes an Image as an argument, and
> ..NET will do the copying for you automatically (added bonus: no
> Graphics instance at all, never mind one you need to dispose :) ).
>

Hi Pete,

I had tried this before, but it will throw GDI+ exception

Image image3 = new Bitmap(image2);
...
image3.Save(filename2, ImageFormat.Jpeg);

I also had tried Image.Clone(), but no go.

>> PropertyItem[] metadatas = image2.PropertyItems;
>>
>> foreach(var metadata in metadatas)
>> {
>> image3.SetPropertyItem(metadata);
>> }
>>
>> PropertyItem property = image1.GetPropertyItem(40092);
>> property.Id = 40092;
>>
>> image2.Dispose(); // release the lock
>>
>> image3.SetPropertyItem(property);
>> image3.Save(filename2, ImageFormat.Jpeg);
>>
>> But, take note that the above two methods will put some pressure on
>> your system if the image file is very large. Memory is expensive
>> while disk space is not. You have to weight the options.
>

> [...]


>
> Now, all that said...there's no need to copy the bitmap in order to
> avoid the locked file. I suggested that earlier because it's the
> easiest approach. But if one is really concerned about memory usage,
> then neither of the methods suggested here (copying the file contents
> into a MemoryStream, or copying the image itself) are appropriate.
> Instead, one should simply open the file directly, using a FileStream
> constructor overload that allows specification of the
> FileShare.ReadWrite value, so that the file is not actually locked in
> the first place. For example:
>
> string strFilename = ...;
> FileStream stream = new FileStream(strFilename, FileMode.Open,
> FileAccess.Read, FileShare.ReadWrite);
>
> using (Image imgOriginal = Image.FromStream(stream))
> {
> // do stuff to image here
>
> imgOriginal.Save(strFilename);
> }
>
> That way you only ever have one copy of the file in memory.
>

This is my first take, but the strange thing is the GDI+ keeps the file
locked. Here is my code.

string filename1 = "D:\\Temp\\output1.jpg";
string filename2 = "D:\\Temp\\output2.jpg";

Image image1 = Image.FromFile(filename1);

using (FileStream stream = new FileStream(filename2, FileMode.Open,
FileAccess.Read, FileShare.ReadWrite))
{
using (Image image2 = Image.FromStream(stream))
{
PropertyItem property = image1.GetPropertyItem(271);
property.Id = 40092;
image2.SetPropertyItem(property);

image2.Save(filename2);
}
}

Am I missing something?

Regards,

Message has been deleted

kndg

unread,
Sep 15, 2009, 5:20:30 AM9/15/09
to
Peter Duniho wrote:
> On Mon, 14 Sep 2009 23:05:49 -0700, kndg <re...@this.newsgroup> wrote:
>
>> [...] Just use the Bitmap constructor that takes an Image as an

>>> argument, and ..NET will do the copying for you automatically (added
>>> bonus: no Graphics instance at all, never mind one you need to
>>> dispose :) ).
>>>
>>
>> Hi Pete,
>>
>> I had tried this before, but it will throw GDI+ exception
>>
>> Image image3 = new Bitmap(image2);
>> ...
>> image3.Save(filename2, ImageFormat.Jpeg);
>
> Did you dispose "image2" before trying to save over its file?
>

Yes, I did as you see from my previous code. I just modify the images
variable and comment out the drawing operations.

Image image3 = new Bitmap(images2);

//Graphics g = Graphics.FromImage(image3);
//g.DrawImage(image2, 0, 0);
.
.
.


image2.Dispose(); // release the lock

image3.SetPropertyItem(property);
image3.Save(filename2, ImageFormat.Jpeg);

However when I restart the PC, the exception gone... wierd.

> I've used that exact technique before, so I know it works in the general
> case. If you're seeing an exception, either you've left something out,
> or there's something unique about your specific scenario.
>
>> [...]


>> This is my first take, but the strange thing is the GDI+ keeps the
>> file locked. Here is my code.
>>
>> string filename1 = "D:\\Temp\\output1.jpg";
>> string filename2 = "D:\\Temp\\output2.jpg";
>>
>> Image image1 = Image.FromFile(filename1);
>>
>> using (FileStream stream = new FileStream(filename2, FileMode.Open,
>> FileAccess.Read, FileShare.ReadWrite))
>> {
>> using (Image image2 = Image.FromStream(stream))
>> {
>> PropertyItem property = image1.GetPropertyItem(271);
>> property.Id = 40092;
>> image2.SetPropertyItem(property);
>>
>> image2.Save(filename2);
>> }
>> }
>>
>> Am I missing something?
>

> In that specific example, I don't see anything obviously wrong.
> Assuming that there's nothing important in the code you left out, we're
> either back to some issue with the PropertyItem itself, or an OS/.NET
> version difference.
>
> I'm testing on Windows 7 RTM, .NET 3.5 SP1. I've included a
> concise-but-complete code example below that demonstrates the use of
> FileShare.ReadWrite to successfully modify and save over the original
> file, without the need for an in-memory copy. You should be able to use
> that without any additional effort to see whether you see a locking
> issue on your configuration; if not, then there's something about the
> specific usage your code has that's causing problems, rather than a
> general failure with a locked file (and even in that latter case, it
> would be some sort of version-specific issue, as I don't have any
> locking issues with the code I'm posting).
>
> Pete
>
>
> [...]

I had copied exactly your code (no modification made) but it throws GDI+
exception. I did restart the PC but still the same. I'm using Windows XP
with .NET 3.5 SP1. I tried this on Vista but also same error. I have
no Windows 7 to test, but if what you said is true then I think it is
version-specific issue.

Regards.

Peter Duniho

unread,
Sep 15, 2009, 4:18:12 PM9/15/09
to
On Tue, 15 Sep 2009 02:20:30 -0700, kndg <re...@this.newsgroup> wrote:

> [...]


> I had copied exactly your code (no modification made) but it throws GDI+
> exception. I did restart the PC but still the same. I'm using Windows XP
> with .NET 3.5 SP1. I tried this on Vista but also same error. I have
> no Windows 7 to test, but if what you said is true then I think it is
> version-specific issue.

I was able to verify the exception on my XP SP3, .NET 3.5 SP1 computer. I
didn't spend any time actually trying to debug the error; I hate the lack
of information in the GDI+ exceptions, and since I've usually been
successful finding work-arounds without too much trouble, I try to avoid
spending time debugging them in more detail.

Obviously, if it's a versioning issue, it's with the OS version and not
.NET. I suppose the good news is that whatever it is, Microsoft fixed it
in Windows 7. :)

Pete

kndg

unread,
Sep 15, 2009, 9:57:09 PM9/15/09
to
Peter Duniho wrote:
> On Tue, 15 Sep 2009 02:20:30 -0700, kndg <re...@this.newsgroup> wrote:
>
>> [...]
>> I had copied exactly your code (no modification made) but it throws
>> GDI+ exception. I did restart the PC but still the same. I'm using
>> Windows XP with .NET 3.5 SP1. I tried this on Vista but also same
>> error. I have no Windows 7 to test, but if what you said is true then
>> I think it is version-specific issue.
>
> I was able to verify the exception on my XP SP3, .NET 3.5 SP1 computer.
> I didn't spend any time actually trying to debug the error; I hate the
> lack of information in the GDI+ exceptions, and since I've usually been
> successful finding work-arounds without too much trouble, I try to avoid
> spending time debugging them in more detail.
>
> Obviously, if it's a versioning issue, it's with the OS version and not
> ..NET. I suppose the good news is that whatever it is, Microsoft fixed
> it in Windows 7. :)
>

Yeah, can hardly wait the Windows 7 to reach the shelf :)

Peter Duniho

unread,
Sep 15, 2009, 10:59:51 PM9/15/09
to
On Tue, 15 Sep 2009 18:57:09 -0700, kndg <re...@this.newsgroup> wrote:

> [...] I suppose the good news is that whatever it is, Microsoft


>> fixed it in Windows 7. :)
>>
>
> Yeah, can hardly wait the Windows 7 to reach the shelf :)

Just a month and a week to go! :)

In the meantime, you could still be running the RC...it's practically the
same bits, and won't expire until well after the RTM version is out.

Pete

peter

unread,
Sep 16, 2009, 1:47:51 AM9/16/09
to

"Peter Duniho" <no.pet...@no.nwlink.spam.com> wrote in message
news:op.u0bw9...@macbook-pro.local...

Thanks!!!

I heared if using WPF can avoid the problem. The problem is in GDI+. But I
don't know how to write WPF application.

Peter


Peter Duniho

unread,
Sep 16, 2009, 3:37:06 AM9/16/09
to
On Tue, 15 Sep 2009 22:47:51 -0700, peter <peter...@hotmail.com> wrote:

> I heared if using WPF can avoid the problem. The problem is in GDI+. But
> I
> don't know how to write WPF application.

WPF and Forms are both APIs for dealing with the GUI. On the other hand,
the graphics stuff is independent of either (in the System.Drawing
namespace). I don't think that WPF provides an API for saving bitmaps, so
no...I doubt you can avoid the problem using it. :)

Pete

0 new messages