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);
}
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
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...
> 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
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
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
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.
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?
> 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
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.
> [...]
> 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
> [...] 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).
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,
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.
> [...]
> 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
Yeah, can hardly wait the Windows 7 to reach the shelf :)
> [...] 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
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
> 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