2010-09-07 4 views
9

AKTUALIZACJAFile.Delete braku kiedy Image.FromFile nazwano go wcześniej, pomimo podejmowania kopię załadowanego obrazu i niszcząc oryginalny

Użyłem poniżej rozwiązań (ładowanie Image ze strumienia), ale się nowy problem. img obiekt jest absolutnie poprawny instancja klasy Image z wszystkimi polami wypełnionymi poprawnymi wartościami. Ale nazywając

img.Save("path/to/new/image.bmp"); 

na nim powoduje nowego wyjątku dla GDI + (System.Runtime.InteropServices.ExternalException w interfejsie GDI +) - dostaję komunikat o błędzie, ale jest w języku polskim, nie jestem pewien, jak to przetłumaczyć.

pytanie Original

Mam problem z C# .NET Framework 2.0

Zasadniczo I'am chce osiągnąć:

Image img = Image.FromFile("Path/To/Image.bmp"); 
File.Delete("Path/To/Image.bmp"); // Exception, the file is in use! 

Ważne jest dla mnie, aby zachować kopię obrazu w pamięci, gdy oryginalny plik został usunięty. Chociaż jest to jakoś dziwne, że .NET nadal blokuje plik na dysku twardym, mimo że nie jest już potrzebny do żadnej operacji (cały obraz jest teraz w pamięci, czyż nie?)

Próbowałem tego rozwiązania:

Image img = new Image(Image.FromFile("Path/To/Image.bmp")); // Make a copy 
        // this should immiedietaly destroy original loaded image 
File.Delete("Path/To/Image.bmp"); // Still exception: the file is in use! 

mogę zrobić:

Image img = null; 
using(Image imgTmp = Image.FromFile("Path/To/Image.bmp")) 
{ 
    img = new Bitmap(imgTmp.Width, imgTmp.Height, imgTmp.PixelFormat); 
    Graphics gdi = Graphics.FromIage(img); 
    gdi.DrawImageUnscaled(imgTmp, 0, 0); 
    gdi.Dispose(); 
    imgTmp.Dispose(); // just to make sure 
} 
File.Delete("Path/To/Image.bmp"); // Works fine 
// So I have img! 

Ale to wydaje mi się prawie jak za pomocą nuke zabić błąd ... i rodzi kolejny problem: GDI słabo wspierać Naniesienie wizerunków palety opartej na siebie (i palety większość stanowią moje zbiory).

Czy robię coś nie tak? Czy jest jakiś lepszy sposób na zapisanie Obrazu w pamięci i usunięcie oryginalnego pliku z dysku twardego?

Odpowiedz

16

To powinno załatwić sprawę:

Image img = null; 
    using (var stream = File.OpenRead(path)) { 
    img = Image.FromStream(stream); 
    } 
    File.Delete(path); 

UPDATE: Nie używaj powyższy kod!

Znalazłem powiązany artykuł bazy wiedzy: http://support.microsoft.com/?id=814675

Rozwiązaniem jest naprawdę skopiuj bitmapę jak opisano w artykule. Mam zakodowane dwa sposoby, że artykuł wymienia (pierwszy był jeden robiłeś, drugi jest jeden w swojej odpowiedzi, ale bez użycia unsafe):

public static Image CreateNonIndexedImage(string path) { 
    using (var sourceImage = Image.FromFile(path)) { 
    var targetImage = new Bitmap(sourceImage.Width, sourceImage.Height, 
     PixelFormat.Format32bppArgb); 
    using (var canvas = Graphics.FromImage(targetImage)) { 
     canvas.DrawImageUnscaled(sourceImage, 0, 0); 
    } 
    return targetImage; 
    } 
} 

[DllImport("Kernel32.dll", EntryPoint = "CopyMemory")] 
private extern static void CopyMemory(IntPtr dest, IntPtr src, uint length); 

public static Image CreateIndexedImage(string path) { 
    using (var sourceImage = (Bitmap)Image.FromFile(path)) { 
    var targetImage = new Bitmap(sourceImage.Width, sourceImage.Height, 
     sourceImage.PixelFormat); 
    var sourceData = sourceImage.LockBits(
     new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), 
     ImageLockMode.ReadOnly, sourceImage.PixelFormat); 
    var targetData = targetImage.LockBits(
     new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), 
     ImageLockMode.WriteOnly, targetImage.PixelFormat); 
    CopyMemory(targetData.Scan0, sourceData.Scan0, 
     (uint)sourceData.Stride * (uint)sourceData.Height); 
    sourceImage.UnlockBits(sourceData); 
    targetImage.UnlockBits(targetData); 
    targetImage.Palette = sourceImage.Palette; 
    return targetImage; 
    } 
} 
+1

Dzięki, działa bezbłędnie i jest szybszy niż moje rozwiązanie :) – PiotrK

+1

WHOW !!! Masz moje WIELKIE podziękowania. Działa to jak uroku :) (rozwiązanie z DllImport) thx dużo :) –

+0

Obraz utworzony przez tę funkcję traci swój RawFormat (który mówi format, w którym obraz został zapisany jako). Może to stanowić problem, jeśli chcesz zapisać go w strumieniu, który da wyjątek podczas przesyłania strumienia i RawFormat obrazu. Ponadto, podczas zapisywania obrazu, zawsze będzie domyślnie png, chyba że podano inaczej. – user886079

10

Twoim problemem jest to, że new Image nadal wie, skąd pochodzi, po otrzymaniu uchwytu pliku od starego konstruktora kopiowania obrazu, więc środowisko wykonawcze nadal wie, że ma otwarty uchwyt do pliku.

może być w stanie obejść ten problem ze strumieniem Zamiast:

Image image; 
FileStream myStream = new FileStream(path); 

try 
{ 
    image = Image.FromStream(myStream); 
} 
finally 
{  
    myStream.Close(); 
    myStream.Dispose(); 
} 

//test that you have a valid Image and then go to work. 

Oto odkurzacz wersja z using klauzuli:

Image image; 
using(FileStream myStream = new FileStream(path)) 
{ 
    image = Image.FromStream(myStream); 
} 
//a using clause calls Dispose() at the end of the block, 
//which will call Close() as well 

Caveat emptor; Nie testowałem tego, nie mam gwarancji, że to rozwiąże problem, ale wydaje się to uzasadnione. Praca bezpośrednio ze Streamem daje ci, a nie implementację obrazu, kontrolę nad uchwytem pliku, dzięki czemu możesz upewnić się, że twój program zwalnia zasoby, gdy TY chcesz.

+0

Dzięki! Zweryfikowano to rozwiązanie (po drobnej poprawce zakresu dla 'myStream').Jest to znacznie prostsze niż akceptowana odpowiedź i nie wymaga ręcznego kopiowania pamięci. Szkoda, że ​​Msft nie przerzucił tej zmiany jako parametru do 'Image.FromFile()'! – nothingisnecessary

+4

Nie ... Błędy GDI + pojawiają się po usunięciu strumienia. – Nyerguds

+0

Musisz zamknąć strumień w instrukcji using. Używanie tylko dispposes nie powoduje przerwania połączeń z bazami danych ani zamykania plików. –

0

To działa dobrze, wadą jest to, że wymaga "niebezpiecznej" kompilacji.

gdy obraz jest ładowany ze strumienia wersja, że ​​został zabity, gdy ładowanie odbywa wyników w stanie zapisać obraz na płytę za pomocą klasycznej GDI +

public static unsafe Image LoadImageSafe(string path) 
{ 
    Image ret = null; 
    using (Image imgTmp = Image.FromFile(path)) 
    { 
     ret = new Bitmap(imgTmp.Width, imgTmp.Height, imgTmp.PixelFormat); 
     if (imgTmp.PixelFormat == PixelFormat.Format8bppIndexed) 
     { 
      ColorPalette pal = ret.Palette; 
      for (int i = 0; i < imgTmp.Palette.Entries.Length; i++) 
       pal.Entries[i] = Color.FromArgb(imgTmp.Palette.Entries[i].A, 
        imgTmp.Palette.Entries[i].R, imgTmp.Palette.Entries[i].G, 
        imgTmp.Palette.Entries[i].B); 
      ret.Palette = pal; 
      BitmapData bmd = ((Bitmap)ret).LockBits(new Rectangle(0, 0, 
       imgTmp.Width, imgTmp.Height), ImageLockMode.WriteOnly, 
       PixelFormat.Format8bppIndexed); 
      BitmapData bmd2 = ((Bitmap)imgTmp).LockBits(new Rectangle(0, 0, 
       imgTmp.Width, imgTmp.Height), ImageLockMode.ReadOnly, 
       PixelFormat.Format8bppIndexed); 

      Byte* pPixel = (Byte*)bmd.Scan0; 
      Byte* pPixel2 = (Byte*)bmd2.Scan0; 

      for (int Y = 0; Y < imgTmp.Height; Y++) 
      { 
       for (int X = 0; X < imgTmp.Width; X++) 
       { 
        pPixel[X] = pPixel2[X]; 
       } 
       pPixel += bmd.Stride; 
       pPixel2 += bmd2.Stride; 
      } 

      ((Bitmap)ret).UnlockBits(bmd); 
      ((Bitmap)imgTmp).UnlockBits(bmd2); 
     } 
     else 
     { 
      Graphics gdi = Graphics.FromImage(ret); 
      gdi.DrawImageUnscaled(imgTmp, 0, 0); 
      gdi.Dispose(); 
     } 
     imgTmp.Dispose(); // just to make sure 
    } 
    return ret; 
} 
+0

Wierzę, że używając 'Marshal.Copy (...)' faktycznie rozwiązuje niebezpieczną rzecz. Jest również prawdopodobnie bardziej wydajne niż kopiowanie bajt po bajcie. – Nyerguds

0

wystarczy umieścić

GC.Collect();

Pod koniec powinien działać dobrze

0

jako akcja inny sposób

try 
{ 
var img = Image.FromFile(s); 
var bmp = new Bitmap(img); 
img.Dispose(); 
File.Delete(s); 
} 
catch { } 
+0

Czym dokładnie jest "i", które sprzedajesz? Nie ma takiej zmiennej. – Nyerguds

+0

Przykro mi, to jest img –

Powiązane problemy