I am attempting to save an embedded shape as an image using C#.
If the object is embedded as an actual image (WMF/JPEG) I can retrieve the image without issue but when the object is an embedded shape or an OLE Object that displays as an image in Word I cannot seem to extract or retrieve said object to then either copy to the clipboard or save said image.
Here is my current code sample; either the object is empty or I get the following error:
System.Runtime.InteropServices.ExternalException: 'A generic error occurred in GDI+.'
Any help is appreciated. Thank you
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace ImageMagickSandboxWinForms
{
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
public static BitmapSource ConvertBitmap(Bitmap source)
{
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
source.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
public static Bitmap BitmapFromSource(BitmapSource bitmapsource)
{
Bitmap bitmap;
using (var outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapsource));
enc.Save(outStream);
bitmap = new Bitmap(outStream);
}
return bitmap;
}
private void button1_Click(object sender, EventArgs e)
{
string physicsDocLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
physicsDocLocation += #"\[Doc path Here].docx";
var wordApp = new Microsoft.Office.Interop.Word.Application();
var wordDoc = wordApp.Documents.Open(physicsDocLocation);
int iCount = wordDoc.InlineShapes.Count;
for (int i = 1; i < (wordDoc.InlineShapes.Count + 1); i++)
{
var currentInlineShape = wordDoc.InlineShapes[i];
currentInlineShape.Range.Select();
wordDoc.ActiveWindow.Selection.Range.Copy();
BitmapSource clipBoardImage = System.Windows.Clipboard.GetImage();
Bitmap bmpClipImage = BitmapFromSource(clipBoardImage);
string finalPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), #"TestConversions");
finalPath += #"\" + Guid.NewGuid().ToString() + ".jpg";
using (MemoryStream memory = new MemoryStream())
{
using (FileStream fs = new FileStream(finalPath, FileMode.Create, FileAccess.ReadWrite))
{
bmpClipImage.Save(memory, ImageFormat.Jpeg); <<<---- Error happens here.
byte[] bytes = memory.ToArray();
fs.Write(bytes, 0, bytes.Length);
}
}
}
wordDoc.Close();
wordApp.Quit();
}
}
}
i have these code in my library, dunno where i have found that but hope you do the job for you: i am using Clippboard to trap the different images, jus t dont forget, Thread is needed to access Clipboard
for (var i = 1; i <= wordApplication.ActiveDocument.InlineShapes.Count; i++)
{
var inlineShapeId = i;
var thread = new Thread(() => SaveInlineShapeToFile(inlineShapeId, wordApplication));
// STA is needed in order to access the clipboard
// https://stackoverflow.com/a/518724/700926
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
}
// General idea is based on: https://stackoverflow.com/a/7937590/700926
protected static void SaveInlineShapeToFile(int inlineShapeId, Application wordApplication)
{
// Get the shape, select, and copy it to the clipboard
var inlineShape = wordApplication.ActiveDocument.InlineShapes[inlineShapeId];
inlineShape.Select();
wordApplication.Selection.Copy();
// Check data is in the clipboard
if (Clipboard.GetDataObject() != null)
{
var data = Clipboard.GetDataObject();
// Check if the data conforms to a bitmap format
if (data != null && data.GetDataPresent(DataFormats.Bitmap))
{
// Fetch the image and convert it to a Bitmap
var image = (Image) data.GetData(DataFormats.Bitmap, true);
var currentBitmap = new Bitmap(image);
// Save the bitmap to a file
currentBitmap.Save(#"C:\Users\Username\Documents\" + String.Format("img_{0}.png", inlineShapeId));
}
}
}
following if you are using Winform or WPF the clipboard acts differently for an image:
if (Clipboard.ContainsImage())
{
// ImageUIElement.Source = Clipboard.GetImage(); // does not work
System.Windows.Forms.IDataObject clipboardData = System.Windows.Forms.Clipboard.GetDataObject();
if (clipboardData != null)
{
if (clipboardData.GetDataPresent(System.Windows.Forms.DataFormats.Bitmap))
{
System.Drawing.Bitmap bitmap = (System.Drawing.Bitmap)clipboardData.GetData(System.Windows.Forms.DataFormats.Bitmap);
ImageUIElement.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty,BitmapSizeOptions.FromEmptyOptions());
Console.WriteLine("Clipboard copied to UIElement");
}
}
}
after if its not functionam due to a bug in translation of format, there is this solution . So its infrecnh but its easily to understand the logic of the using of "DeviceIndependentBitmap"
Related
Below is code for a VS2015 C# console app where I printing an 8.5x14 SSRS report (no data connection). When I print the report from Report Builder, it renders correctly. When I print it from my console app, the report doesn't fill the page. I am rendering the report to a stream where I set the size to 8.5x14, then printing it, setting the document size to 8.5x14. Not sure what else I need to do to get it to fit.
If anyone would take a look and offer advise, I would be very grateful.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using Microsoft.Reporting.WinForms;
namespace SULabelPrinting
{
class Program
{
private static int m_currentPageIndex;
private static IList<Stream> m_streams;
static void Main()
{
try
{
LocalReport report = new LocalReport();
report.ReportPath = #"..\..\Test.rdl";
Export(report);
Print();
}
catch (Exception ex)
{
string msg = ex.Message;
}
}
// Routine to provide to the report renderer, in order to
// save an image for each page of the report.
private static Stream CreateStream(string name, string fileNameExtension, Encoding encoding, string mimeType, bool willSeek)
{
Stream stream = new MemoryStream();
m_streams.Add(stream);
return stream;
}
// Export the given report as an EMF (Enhanced Metafile) file.
private static void Export(LocalReport report)
{
string deviceInfo =
#"<DeviceInfo>
<OutputFormat>EMF</OutputFormat>
<PageWidth>14in</PageWidth>
<PageHeight>8.5in</PageHeight>
<MarginTop>.1in</MarginTop>
<MarginLeft>.1in</MarginLeft>
<MarginRight>.1in</MarginRight>
<MarginBottom>.1in</MarginBottom>
</DeviceInfo>";
Warning[] warnings;
m_streams = new List<Stream>();
report.Render("Image", deviceInfo, CreateStream,
out warnings);
foreach (Stream stream in m_streams)
stream.Position = 0;
}
// Handler for PrintPageEvents
private static void PrintPageHandler(object sender, PrintPageEventArgs ev)
{
Metafile pageImage = new
Metafile(m_streams[m_currentPageIndex]);
// Adjust rectangular area with printer margins.
Rectangle adjustedRect = new Rectangle(
ev.PageBounds.Left - (int) ev.PageSettings.HardMarginX,
ev.PageBounds.Top - (int) ev.PageSettings.HardMarginY,
ev.PageBounds.Width,
ev.PageBounds.Height);
// Draw a white background for the report
ev.Graphics.FillRectangle(Brushes.White, adjustedRect);
// Draw the report content
ev.Graphics.DrawImage(pageImage, adjustedRect);
// Prepare for the next page. Make sure we haven't hit the end.
m_currentPageIndex++;
ev.HasMorePages = (m_currentPageIndex < m_streams.Count);
}
private static void Print()
{
if (m_streams == null || m_streams.Count == 0)
throw new Exception("No stream to print.");
PrintDocument printDoc = new PrintDocument();
printDoc.DefaultPageSettings.PaperSize = new PaperSize("Legal", 850, 1400);
printDoc.DefaultPageSettings.Landscape = true;
printDoc.PrintPage += new PrintPageEventHandler(PrintPageHandler);
m_currentPageIndex = 0;
printDoc.Print();
}
public void Dispose()
{
if (m_streams != null)
{
foreach (Stream stream in m_streams)
stream.Close();
m_streams = null;
}
}
}
}
If it helps, below is a link to the project.
SULabelPrinting.zip
Thanks,
Steve.
The complete code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//Display byte array as image
private void button1_Click(object sender, EventArgs e)
{
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
Image img = ByteArrayToImage(File.ReadAllBytes("")); //fill with path info
pictureBox1.Image = (Image)img.Clone();
}
//Convert to image from bytes
public Image ByteArrayToImage(byte[] byteArrayIn)
{
using (MemoryStream ms = new MemoryStream(byteArrayIn))
{
ms.Position = 0;
Image returnImage = Image.FromStream(ms);
return returnImage;
}
}
//Open Image for conversion
private void loadImage_Click(object sender, EventArgs e)
{
OpenFileDialog opf = new OpenFileDialog();
DialogResult dr = new DialogResult();
dr = opf.ShowDialog();
if (dr == DialogResult.OK)
{
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
pictureBox1.Image = Image.FromFile(opf.FileName);
}
}
private void convertImage_Click(object sender, EventArgs e)
{
//Choose Path to Save To
SaveFileDialog saveDialog = new SaveFileDialog();
saveDialog.Title = "Choose Directory and File Name";
saveDialog.Filter = "Canga Comix *.CCMX|*.CCMX";
DialogResult dr = saveDialog.ShowDialog();
if (dr == DialogResult.OK)
{
byte[] array;
//Save Image
using (MemoryStream ms = new MemoryStream())
{
Image img = pictureBox1.Image;
img.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
array = ms.ToArray();
}
using(FileStream fs = new FileStream(saveDialog.FileName, FileMode.Create))
{
fs.Write(array, 0, array.Length);
}
}
}
//clear image
private void clearImage_Click(object sender, EventArgs e)
{
pictureBox1.Image = null;
}
}
}
These are pretty standard. I have a test program that uses these methods. It opens an image in a pictureBox and then converts it to byte[] and clears the pictureBox Image. Then you can hit Load byte[] and it will display the picture properly as it runs the ByteArrayToImage method.
Then if I save out a picture from my other program and try to open it in the test program it gives me the unholy "Parameter is not valid" error. Even though both text documents are exactly the same as far as I can tell.
This code contains a common problem. Here you create an Image bound to a stream containing your bitmap...
public Image ByteArrayToImage(byte[] byteArrayIn)
{
using (MemoryStream ms = new MemoryStream(byteArrayIn))
{
ms.Position = 0;
Image returnImage = Image.FromStream(ms);
return returnImage;
}
}
... and the using block disposes the MemoryStream instance on the way out, which makes the Image rather useless.
You can get an Image that manages its own memory instead of expecting your stream to stick around by calling Clone():
public Image ByteArrayToImage(byte[] byteArrayIn)
{
using (MemoryStream ms = new MemoryStream(byteArrayIn))
using (Image returnImage = Image.FromStream(ms))
{
return returnImage.Clone();
}
}
The clone isn't bound to the original stream.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace convert
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load_1(object sender, EventArgs e)
{
// Image image = Image.FromFile(#"C:\Users\Public\Pictures\Sample Pictures\Koala.jpg");
// Set the PictureBox image property to this image.
// ... Then, adjust its height and width properties.
// pictureBox1.Image = image;
//pictureBox1.Height = image.Height;
//pictureBox1.Width = image.Width;
string strFileName = #"C:\Users\Public\Pictures\Sample Pictures\Koala.jpg";
Bitmap bitmap = new Bitmap(strFileName);
//bitmap.Save("testing.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
pictureBox1.Image = bitmap;
pictureBox1.Height = bitmap.Height;
pictureBox1.Width = bitmap.Width;
}
}
}
I am using the above code for converting jpg file into bitmap. It works but I need to know how to stream the jpg image and convert it into bitmap then display the bitmap image with out storing it. I am using c# and vb.net
Try this to convert to Bitmap :
public Bitmap ConvertToBitmap(string fileName)
{
Bitmap bitmap;
using(Stream bmpStream = System.IO.File.Open(fileName, System.IO.FileMode.Open ))
{
Image image = Image.FromStream(bmpStream);
bitmap = new Bitmap(image);
}
return bitmap;
}
Possibly easier:
var bitmap = new Bitmap(Image.FromFile(path));
Whenever I retrieve an image using my Generic Handler, I retrieve either an empty image or a broken image.
Here is my code.
aspx File:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
//imports
using DHELTASSys.Modules;
using System.Data;
using DHELTASSys.AuditTrail;
namespace DHELTASSys
{
public partial class EvaluateOffense : System.Web.UI.Page
{
DisciplineModuleBL discipline = new DisciplineModuleBL();
DHELTASSysAuditTrail audit = new DHELTASSysAuditTrail();
protected void Page_Load(object sender, EventArgs e)
{
string position = Session["Position"].ToString();
if (Session["EmployeeID"] == null)
{
Response.Redirect("LogIn.aspx");
} else if(position != "HR Manager")
{
Response.Redirect("AccessDenied.aspx");
}
discipline.Offense_emp_id = int.Parse(Session["OffenseID"].ToString());
DataTable dt = discipline.GetProof();
if (dt.Rows == null)
{
Label9.Visible = false;
Image1.Visible = false;
}
}
protected void btnEvaluate_Click(object sender, EventArgs e)
{
discipline.Offense_emp_id = int.Parse(Session["OffenseID"].ToString());
discipline.Decision = drpDecision.Text;
discipline.AddOffenseDecision();
audit.Emp_id = int.Parse(Session["EmployeeID"].ToString());
audit.AddAuditTrail(drpDecision.Text + "ed Employee's offense.");
}
}
}
Here is the handler:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
//imports
using System.Data;
using DHELTASSys.Modules;
using DHELTASSys.DataAccess;
namespace DHELTASSys
{
public class ShowImage : IHttpHandler, System.Web.SessionState.IRequiresSessionState
{
DisciplineModuleBL discipline = new DisciplineModuleBL();
public void ProcessRequest(HttpContext context)
{
if (context.Session["OffenseID"].ToString() == null) return;
int offense_emp_id = int.Parse(context.Session["OffenseID"].ToString());
discipline.Offense_emp_id = offense_emp_id;
DataTable dt = discipline.GetProof();
if (dt.Rows == null) return;
int id = 1;
string image = dt.Rows[0][1].ToString() + id;
string FileName = dt.Rows[0][0].ToString();
string FileContentType = dt.Rows[0][2].ToString();
Byte[] bytes = (Byte[])dt.Rows[0][1];
string imageBase64 = Convert.ToBase64String(bytes);
context.Response.ContentType = "image/" + FileContentType;
if (context.Request.QueryString["id"] == "1")
{
MemoryStream ms = new MemoryStream();
ms.Write(bytes, 0, bytes.Length);
context.Response.Buffer = true;
System.Drawing.Image imagen = System.Drawing.Image.FromStream(ms);
context.Response.BinaryWrite(bytes);
ms.Dispose();
}
else
{
return;
}
}
public bool IsReusable
{
get
{
return true;
}
}
}
}
And adding to that, here is my image object.
<asp:Image ID="Image1" runat="server" ImageUrl="~/ShowImage.ashx" />
I already tweaked my code in so many ways.
The image file is stored in SQL Server using the data type "Image"
As you can see, I'm using the session to retrieve the specified image from the Database.
I have no problem in accessing the session whatsoever.
Thanks in advance.
Your code seems a bit more complicated than necessary; you can write the binary data to the outputstream without needing to load it into a memory stream, and you're doing nothing with your System.Drawing.Image object. Try this:
context.Response.OutputStream.Write(bytes, 0, bytes.Length);
First, I don't see anything wrong with the way you are writing the image to the response stream. There's a few useless lines of code but I understand that as you said, you were tweaking your code in desperation. Basically, this should be good enough...
Byte[] bytes = (Byte[])dt.Rows[0][1];
context.Response.ContentType = "image/" + FileContentType;
if (context.Request.QueryString["id"] == "1")
{
context.Response.BinaryWrite(bytes);
context.ApplicationInstance.CompleteRequest(); //just to make sure the ASP.NET pipeline completes the request
}
else
{
return;
}
Now, that "should" work given that bytes has been correctly casted to a byte array. Make sure to add some proper exception handling and logging because I'm a bit suspicious that the problem it's somewhere else. So, follow these steps:
Clean up your code for better clarity
Debug the app
Implement some exception handling
Implement some sort of error logging
You should be able to get to the bottom of this issue since there's nothing wrong in sending an array of bytes using the BinaryWrite method
This is real example that pickup from my project
into page.cs set image url dynamically:
// for diffrent url using guid in url
imgProfilePic.ImageUrl = "GenericHandler_ShowImage.ashx?Ref=" + Guid.NewGuid().ToString() + "&n=" + lngEmployeeID;
into handler, get image by table adapter
// in handler
public void ProcessRequest(HttpContext context)
{
long EmployeeID = -1;
if (context.Request.QueryString["n"] != null)
EmployeeID = long.Parse(context.Request.QueryString["n"].ToString());
//else
// throw new ArgumentException("No parameter specified");
if (context.Request.QueryString["actid"] == "2")
{
ShowThumbPic(context, EmployeeID);
return;
}
//else ...
}
private void ShowThumbPic(HttpContext context, long EmployeeID)
{
context.Response.ContentType = "image/jpeg";
Stream myStream = GetEmpImage(EmployeeID);
byte[] myImgByteArray;
using (BinaryReader br = new BinaryReader(myStream))
{
myImgByteArray = br.ReadBytes((int)myStream.Length);
}
MemoryStream ms = new MemoryStream(byteArrayIn);
System.Drawing.Image img = System.Drawing.Image.FromStream(ms);
if (img.Height > 50 || img.Width > 50)
{
System.Drawing.Size siz = GetScaledSize(img.Size, new System.Drawing.Size(50, 50));
img = (System.Drawing.Image)ResizeImage(img, siz);
}
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
byte[] myBuffer = ms.ToArray();
int byteSeq = myBuffer.Length; //myStream.Read(myBuffer, 0, 4096);
if (byteSeq > 0)
context.Response.OutputStream.Write(myBuffer, 0, byteSeq);
}
private System.Drawing.Image GetEmployeeImage(long EmployeeID)
{
System.Drawing.Image img = null;
try
{
using (DAL.dstEmployeeTableAdapters.tbl_Employee_InfoTableAdapter ta = new DAL.dstEmployeeTableAdapters.tbl_Employee_InfoTableAdapter())
{
object obj = ta.spr_Employee_Info_GetPicture(EmployeeID);
if (obj != null)
{
MemoryStream ms = new MemoryStream((byte[])obj);
img = System.Drawing.Image.FromStream(ms);
}
}
}
catch (Exception x)
{
throw new Exception(Msg.Error_InDownloadPicture + x.Message);
}
return img;
}
public static Size GetScaledSize(Size ImageSize, Size FrameSize)
{
int newWidth = ImageSize.Width;
int newHeight = ImageSize.Height;
double ratioX = (double)FrameSize.Width / ImageSize.Width;
double ratioY = (double)FrameSize.Height / ImageSize.Height;
double ratio = Math.Min(ratioX, ratioY);
if (ratio < 1.0f) // if Frame is greater than image, resize it.
{
newWidth = (int)(ImageSize.Width * ratio);
newHeight = (int)(ImageSize.Height * ratio);
}
return new Size(newWidth, newHeight);
}
public static System.Drawing.Bitmap ResizeImage(System.Drawing.Image image, Size siz)
{
//a holder for the result
Bitmap result = new Bitmap(siz.Width, siz.Height);
// set the resolutions the same to avoid cropping due to resolution differences
result.SetResolution(image.HorizontalResolution, image.VerticalResolution);
//use a graphics object to draw the resized image into the bitmap
using (Graphics graphics = Graphics.FromImage(result))
{
//set the resize quality modes to high quality
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//draw the image into the target bitmap
graphics.DrawImage(image, 0, 0, result.Width, result.Height);
}
//return the resulting bitmap
return result;
}
I create thumbnails with the following page, Thumbnail.ashx:
<%# WebHandler Language="C#" Class="Thumbnail" %>
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Web;
public class Thumbnail : IHttpHandler {
private int _thumbnailSize = 150;
public void ProcessRequest(HttpContext context) {
string photoName = context.Request.QueryString["p"];
string cachePath = Path.Combine(HttpRuntime.CodegenDir, photoName + ".png");
if (File.Exists(cachePath)) {
OutputCacheResponse(context, File.GetLastWriteTime(cachePath));
context.Response.WriteFile(cachePath);
return;
}
string photoPath = context.Server.MapPath("../uploads/originals/" + photoName);
Bitmap photo;
try {
photo = new Bitmap(photoPath);
}
catch (ArgumentException) {
throw new HttpException(404, "Photo not found.");
}
context.Response.ContentType = "image/png";
int width, height;
if (photo.Width > photo.Height) {
width = _thumbnailSize;
height = photo.Height * _thumbnailSize / photo.Width;
}
else {
width = photo.Width * _thumbnailSize / photo.Height;
height = _thumbnailSize;
}
Bitmap target = new Bitmap(width, height);
using (Graphics graphics = Graphics.FromImage(target)) {
graphics.CompositingQuality = CompositingQuality.HighSpeed;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.DrawImage(photo, 0, 0, width, height);
using (MemoryStream memoryStream = new MemoryStream()) {
target.Save(memoryStream, ImageFormat.Png);
OutputCacheResponse(context, File.GetLastWriteTime(photoPath));
using (FileStream diskCacheStream = new FileStream(cachePath, FileMode.CreateNew)) {
memoryStream.WriteTo(diskCacheStream);
}
memoryStream.WriteTo(context.Response.OutputStream);
}
}
}
private static void OutputCacheResponse(HttpContext context, DateTime lastModified) {
HttpCachePolicy cachePolicy = context.Response.Cache;
cachePolicy.SetCacheability(HttpCacheability.Public);
cachePolicy.VaryByParams["p"] = true;
cachePolicy.SetOmitVaryStar(true);
cachePolicy.SetExpires(DateTime.Now + TimeSpan.FromDays(365));
cachePolicy.SetValidUntilExpires(true);
cachePolicy.SetLastModified(lastModified);
}
public bool IsReusable {
get {
return true;
}
}
}
When I try to delete the picture file physically or through code, I get a 'cannot access file, being used by another process" error.
Is this caused by the caching of the thumbnail? Or is the file not being closed somewhere I can't spot?
Could it be my upload file script is causing this?
You don't appear to be closing your MemoryStream anywhere? Garbage collection alone won't remove a file lock, would it? Tossing in a memoryStream.Close() at the end of ProcessRequest couldn't hurt.