Unlocking image from PictureBox

北战南征 提交于 2019-12-08 14:04:24

问题


I'm currently developing an application to help scanning and showing images here at my work.

My application is build with multiple forms, the most important forms here is my mainForm to show statistics about the current scanning and a menustrip with different functinalities. I also have ImageViewerForm with a PictureBox which shows on the secondary monitor to view the current scanned image.

I'm using a Timer to poll the folder where the images are scanned to. When a new image has been scanned and the image is unlocked, i'll grap it into a FileStream and show it in the PictureBox, see below:

public static void SetPicture(string filename, PictureBox pb)
{
    try
    {
        Image currentImage;

        //currentImage = ImageFast.FromFile(filename);
        using (FileStream fsImage = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            currentImage = ScaleImage(Image.FromStream(fsImage), new Size(pb.Width, pb.Height));

            if (pb.InvokeRequired)
            {
                pb.Invoke(new MethodInvoker(
                delegate()
                {
                    pb.Image = currentImage;
                }));
            }
            else
            {
                pb.Image = currentImage;
            }
        }
    }
    catch (Exception imageEx)
    {
        throw new ExceptionHandler("Error when showing image", imageEx);
    }
}

public static Image ScaleImage(Image imgToResize, Size size)
{
    int sourceWidth = imgToResize.Width;
    int sourceHeight = imgToResize.Height;

    float nPercent = 0;
    float nPercentW = 0;
    float nPercentH = 0;

    nPercentW = ((float)size.Width / (float)sourceWidth);
    nPercentH = ((float)size.Height / (float)sourceHeight);

    if (nPercentH < nPercentW)
        nPercent = nPercentH;
    else
        nPercent = nPercentW;

    int destWidth = (int)(sourceWidth * nPercent);
    int destHeight = (int)(sourceHeight * nPercent);

    Bitmap b = new Bitmap(destWidth, destHeight);

    using (Graphics g = Graphics.FromImage(b))
    {
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
    }

    return b;
}

This way the image shown in the PictureBox shouldn't be locked, but it is. The problem is that the scanned image might have to be rescanned, and if I do that i'll get a sharing violation error when trying to overwrite the image file from the scanning software.

Anyone who's got an answer to what I can do?

SOLUTION

Thanks to @SPFiredrake I've got a solution to create a temp file to show in the PictureBox, leaving the original image unlocked.

public static void SetPicture(string filename, PictureBox pb)
{
    try
    {
        Image currentImage;

        //currentImage = ImageFast.FromFile(filename);
        using (FileStream fsImage = new FileStream(CreateTempFile(filename), FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            currentImage = ScaleImage(Image.FromStream(fsImage), new Size(pb.Width, pb.Height));

            if (pb.InvokeRequired)
            {
                pb.Invoke(new MethodInvoker(
                delegate()
                {
                    pb.Image = currentImage;
                }));
            }
            else
            {
                pb.Image = currentImage;
            }
        }
    }
    catch (Exception imageEx)
    {
        throw new ExceptionHandler("Error when showing image", imageEx);
    }
}

public static string CreateTempFile(string fileName)
{
    if (string.IsNullOrEmpty(fileName))
        throw new ArgumentNullException("fileName");
    if (!File.Exists(fileName))
        throw new ArgumentException("Specified file must exist!", "fileName");
    string tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + Path.GetExtension(fileName));
    File.Copy(fileName, tempFile);

    Log.New("Temp file created: " + tempFile);

    return tempFile;
}

回答1:


The problem here is that the image is being loaded from a FileStream, which is being locked by the PictureBox because it's holding a reference to the stream. What you should do is first load the picture into local memory (via a byte[] array) and then load the image from a MemoryStream instead. In your SetPicture method, you should try the following change and see if it works:

public static void SetPicture(string filename, PictureBox pb)
{
    try
    {
        Image currentImage;
        byte[] imageBytes = File.ReadAllBytes(filename);
        using(MemoryStream msImage = new MemoryStream(imageBytes))
        {
            currentImage = ScaleImage(Image.FromStream(msImage), new Size(pb.Width, pb.Height));
        ....
}

Edit: After our conversation in Chat, updating with the fix that you ended up using:

public static void SetPicture(string filename, PictureBox pb)
{
    try
    {
        Image currentImage;
        string tempFile = Path.Combine(Path.GetTempDirectory(), Guid.NewGuid().ToString() + Path.GetExtension(filename));
        File.Copy(filename, tempFile);
        //currentImage = ImageFast.FromFile(filename);
        using (FileStream fsImage = new FileStream(tempFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            ...

This way you are using the temp files to actually load the picture box, leaving the original file untouched (outside of the initial copy).




回答2:


Once a Bitmap is loaded, you're not holding onto the filestream anymore, so everything should work. However, if you're talking about the instant when loading is taking place and the scanning tries to overwrite that file - always scan to a "temporary" or junk-named file (use a GUID as the name). Once scanning is complete, rename that file to JPG - which your display form will then pick up and display properly.

This way, re-scans will only involve trying to rename the temporary file multiple times with a "wait" to prevent that little area of overlap.




回答3:


Your code works fine for me. I took an exact copy and called it repeatedly with the same image file.

SetPicture(@"c:\temp\logo.png", pictureBox1);

Something else is locking the file. Can you share your calling code?




回答4:


I guess you're done with your work by now.
Still, I am posting in case someone else has the same issue.
I had the same problem : I load an image in a PictureBox control

picture.Image = new Bitmap(imagePath);  

and when attempting to move it

File.Move(source, destination);  

mscorlib throws an exception :
The process cannot access the file because it is beeing used by another process

I found a solution ( although in VB.Net rather than C# ) here PictureBox "locks" file, cannot move/delete

The author of the post clones the original image and loads the cloned image to the PictureBox control.
I slighty changed the code and came up with this :

private Bitmap CloneImage(string aImagePath) {  
    // create original image
    Image originalImage = new Bitmap(aImagePath);

    // create an empty clone of the same size of original
    Bitmap clone = new Bitmap(originalImage.Width, originalImage.Height);

    // get the object representing clone's currently empty drawing surface
    Graphics g = Graphics.FromImage(clone);

    g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
    g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;

    // copy the original image onto this surface
    g.DrawImage(originalImage, 0, 0, originalImage.Width, originalImage.Height);

    // free graphics and original image
    g.Dispose();
    originalImage.Dispose();

    return clone;
    }

So, we code will be :

picture.Image = (Image)CloneImage(imagePath);  

Doing so, I had no more exception when moving files.
I think it is a good alternative way to do that, and you don't need a temporary file.




回答5:


this is Jack code but in Visual Basic .NET and the casting goes inside the function

 Private Function CloneImage(aImagePath As String) As Image
        ' create original image
        Dim originalImage As Image = New Bitmap(aImagePath)

        ' create an empty clone of the same size of original
        Dim clone As Bitmap = New Bitmap(originalImage.Width, originalImage.Height)

        ' get the object representing clone's currently empty drawing surface
        Dim g As Graphics = Graphics.FromImage(clone)

        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None
        g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor
        g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed

        ' copy the original image onto this surface
        g.DrawImage(originalImage, 0, 0, originalImage.Width, originalImage.Height)

        ' free graphics and original image
        g.Dispose()
        originalImage.Dispose()

        Return CType(clone, Image)
    End Function

So calling will be

picture.Image = CloneImage(imagePath)

Thank you Jack,




回答6:


Response to this question from MS ...

for me is workikng ok ...

internal void UpdateLastImageDownloaded(string fullfilename)
{
    this.BeginInvoke((MethodInvoker)delegate()
    {
        try
        {
            //pictureBoxImage.Image = Image.FromFile(fullfilename);

            //Bitmap bmp = new Bitmap(fullfilename);
            //pictureBoxImage.Image = bmp;

            System.IO.FileStream fs;
            // Specify a valid picture file path on your computer.
            fs = new System.IO.FileStream(fullfilename, System.IO.FileMode.Open, System.IO.FileAccess.Read);
            pictureBoxImage.Image = System.Drawing.Image.FromStream(fs);
            fs.Close();
        }
        catch (Exception exc)
        {
            Logging.Log.WriteException(exc);
        }
    });
}



回答7:


While trying to figure out a solution for my C# Windows Form I came across a helpful article mentioning how to load a picture in picture box without "Locking" the original picture itself but an instance of it. Hence if you try to delete, rename or do whatever you want to the original file you won't be notified by an error message mentioning "The file it's in use by another process" or anything else!

This is a reference to the article.

To Sum Up I believe that this solution is VERY useful when handling with SMALL amount of pictures, because applying this method in larger numbers could lead to lack of memory.



来源:https://stackoverflow.com/questions/10431868/unlocking-image-from-picturebox

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!