问题
I have recently made a change to a windows server to create thumbnails from a memory stream and return it, based upon http://weblogs.asp.net/bleroy/archive/2009/12/10/resizing-images-from-the-server-using-wpf-wic-instead-of-gdi.aspx. It does create the thumbnails correctly but after a few days it starts throwing errors like:
Message: The operation completed successfully
Exception Type: Win32Exception
StackTrace: at MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks)
at System.Windows.Threading.Dispatcher..ctor()
at System.Windows.Media.Imaging.BitmapDecoder..ctor(SafeMILHandle decoderHandle, BitmapDecoder decoder, Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, Boolean insertInDecoderCache, Boolean isOriginalWritable, Stream uriStream, UnmanagedMemoryStream unmanagedMemoryStream, SafeFileHandle safeFilehandle)
at System.Windows.Media.Imaging.BitmapDecoder.CreateFromUriOrStream(Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy, Boolean insertInDecoderCache)
at System.Windows.Media.Imaging.BitmapDecoder.Create(Stream bitmapStream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption)
...My Library Here...
This causes exceptions in other services like
Message: Could not find file 'C:\Windows\TEMP\ngdlieh3.dll'.
Exception Type: FileNotFoundException
StackTrace: at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)
at Microsoft.CSharp.CSharpCodeGenerator.FromFileBatch(CompilerParameters options, String[] fileNames)
at Microsoft.CSharp.CSharpCodeGenerator.FromSourceBatch(CompilerParameters options, String[] sources)
at Microsoft.CSharp.CSharpCodeGenerator.System.CodeDom.Compiler.ICodeCompiler.CompileAssemblyFromSourceBatch(CompilerParameters options, String[] sources)
at System.Xml.Serialization.Compiler.Compile(Assembly parent, String ns, XmlSerializerCompilerParameters xmlParameters, Evidence evidence)
at System.Xml.Serialization.TempAssembly.GenerateAssembly(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, Evidence evidence, XmlSerializerCompilerParameters parameters, Assembly assembly, Hashtable assemblies)
at System.Xml.Serialization.TempAssembly..ctor(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, String location, Evidence evidence)
at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace)
...My Other Library Here...
After doing some googling, I have found that this is often from memory leaks, in the System.Windows.Media.Imaging components ( see "The operation completed successfully" exception), I am at a bit of a loss as to how I should be disposing these objects however, as they do go out of scope and I am explicitly setting the objects to null after use and calling the garbage collector. The memory streams are being disposed of correctly.
The code is:
public static Stream CreateThumbnail(Stream originalStream, int width, int quality)
{
MemoryStream memoryStream = new MemoryStream();
BitmapDecoder imageDecoder;
BitmapFrame originalImage;
TransformedBitmap target;
BitmapFrame thumbnail;
JpegBitmapEncoder encoder;
try
{
imageDecoder = BitmapDecoder.Create(
originalStream,
BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.None);
originalImage = imageDecoder.Frames[0];
double scale = (double)width / originalImage.Width * 96 / originalImage.DpiX;
target = new TransformedBitmap(
originalImage,
new ScaleTransform(
scale,
scale,
0,
0));
thumbnail = BitmapFrame.Create(target);
encoder = new JpegBitmapEncoder();
encoder.QualityLevel = quality;
encoder.Frames.Add(thumbnail);
encoder.Save(memoryStream);
// Return streams to start so they can be used again
originalStream.Seek(0, SeekOrigin.Begin);
memoryStream.Seek(0, SeekOrigin.Begin);
}
finally
{
// Ensure all objects are no longer referenced
imageDecoder = null;
originalImage = null;
target = null;
thumbnail = null;
encoder = null;
// Force garbage collection, otherwise objects are stuck on gen 2 heap for a while
GC.Collect();
}
return memoryStream;
}
And it is called:
using (Stream thumbnailStream = Image.CreateThumbnail(imageSteam, SettingsHelper.Instance.ThumbnailWidth, SettingsHelper.Instance.ThumbnailQuality))
{
this.fileWriter.WriteImage(SettingsHelper.Instance.ThumbnailStorageLocation, thumbnailStream, thumbnailRelativeFilePath);
}
The original image stream is being disposed of correctly separately and that code has been working fine for 18 months.
As the errors occurs after a number of days it would seem to be a leak of some kind, however looking at process explorer the memory usage looks normal (50MB working set with a max of 101MB), the number of handles is 604 and has peaked at 670. No GDI or user handles.
Can anyone offer any suggestions?
回答1:
In case anyone's interested, I did not manage to solve this as such; I ended up using Imazen's LightResize instead, which seems to work well.
来源:https://stackoverflow.com/questions/20467564/windows-media-imaging-thumbnail-generation-causing-exceptions