Moin.
Ich habe hier die Aufgabenstellung ein Bitmap zügig zwischen zwei Anwendungen zu transferieren und teste gerade "MemoryMappedFile".
Nach erfolgreichen Tests innerhalb einer Anwendung und über zwei Test-Anwendungen hinweg, wollte ich nun das Schreiben und Lesen in eine eigene Assembly packen, um später noch Features wie Dateinamen-Logging oder sowas bequem einbauen zu können.
Das Problem ist nun, dass beim Konvertieren des System.Drawing.Bitmaps in ein BitmapSource über einen MemoryStream ein allgemeiner GDI+ Fehler auftritt, wenn ich die Lese-Funktion in eine eigene Assembly packe und darüber aufrufe. Befindet sich derselbe Code im Codebehind meiner Testanwendungen passiert das nicht.
Alles .Net Core 9 Projekte, alle Abhängigkeiten zu Drawing usw. sind identisch.
Hat jemand eine Idee?
Der Code:
BitmapSource ReadBitmapFromMemoryMappedFile(string mapName)
{
try
{
using(var mmf = MemoryMappedFile.OpenExisting(mapName))
{
using(var stream = mmf.CreateViewStream())
{
Bitmap bitmap = new Bitmap(stream);
return ConvertBitmapToBitmapSource(bitmap);
}
}
}
catch
{
return null;
}
}
BitmapSource ConvertBitmapToBitmapSource(Bitmap bitmap)
{
if(bitmap != null)
{
using(MemoryStream memoryStream = new MemoryStream())
{
bitmap.Save(memoryStream, ImageFormat.Bmp);
memoryStream.Position = 0;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memoryStream;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
else
{
return null;
}
}
Befindet sich derselbe Code im Codebehind meiner Testanwendungen passiert das nicht.
Wirklich 100% gleich, inkl. Aufruf und Verarbeitung? Eher nicht, oder? Daher ignorier ich mal die Aussage für meine Antwort.
Implementierungen wie Bitmap sind so umgesetzt, dass Du den Stream nicht schließen darfst, solange die Instanz lebt. Du musst Dich aber aktiv um die managed resources kümmern.
In der Dokumentation steht auch:
You must keep the stream open for the lifetime of the Image.
Ich vermute daher das, als Quelle des Problems.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Warum dieses Stream
zu Bitmap
zu BitmapImage
um eigentlich eine BitmapSource
zu erhalten?
Wenn du das Bild per Binding übergeben möchtest, dann würde auch ein simples byte[]
genügen, was dann automatisch vom ImageSourceConverter
umgewandelt wird. Dieses byte-Array kannst du dann irgendwie in irgendeinem Thread-Kontext erzeugen und dann im UI-Thread an das Control übergeben.
Hat die Blume einen Knick, war der Schmetterling zu dick.
Zitat von Abt
Befindet sich derselbe Code im Codebehind meiner Testanwendungen passiert das nicht.
Wirklich 100% gleich, inkl. Aufruf und Verarbeitung? Eher nicht, oder? Daher ignorier ich mal die Aussage für meine Antwort.
Copy-Paste der Read-Funktion, ersetzen des Methodenaufrufs durch die Methode in der Klasse.
Ich vermute daher das, als Quelle des Problems.
Werde ich mal schauen, verstehe dennoch nicht, warum es im Code-Behind geht.
Zitat von BlonderHans
Warum dieses
Stream
zuBitmap
zuBitmapImage
um eigentlich eineBitmapSource
zu erhalten?Wenn du das Bild per Binding übergeben möchtest, dann würde auch ein simples
byte[]
genügen, was dann automatisch vomImageSourceConverter
umgewandelt wird. Dieses byte-Array kannst du dann irgendwie in irgendeinem Thread-Kontext erzeugen und dann im UI-Thread an das Control übergeben.
Berechtigte Frage. Im Endeffekt soll am Ende eine BitmapSource stehen, der Anfang ist durch einen Stream vorgegeben. Dazwischen wird noch ein wenig an Bildverarbeitung stattfinden, das ist aber noch lange nicht implementiert. Das direkte Konvertieren aus byte[] werde ich mir aber merken, das könnte eventuell nochmal Verwendung finden.
BitmapSource
braucht man idR um diese einem Image-Control als Source
zuzuweisen, aber genau dieser Source
Eigenschaft kann ich auch einen Stream
, einen Dateinamen
(bzw. auch eine Url) eine URI
oder eben ein byte[]
hinwerfen und der ImageSourceConverter
macht daraus dann so eine BitmapSource
Instanz (unter der Haube erzeugt der ein BitmapFrame
)Es spricht eigentlich alles dagegen, für das was du da vor hast, überhaupt dich mit diesen UI Typen (Bitmap
, BitmapImage
, BitmapSource
) herumzuschlagen.
Hat die Blume einen Knick, war der Schmetterling zu dick.
Das sind alles gute Hinweise. Ich würde gerne ergänzen, dass bisher nur geschaut wird, ob die Übergabe überhaupt korrekt funktioniert (erste Verwendung eines MappedMemoryFiles meinerseits) und ich dabei direkt in besagten Fehler gerannt bin.
Es lag in der Tat am geschlossenen Stream beim Erstellen des Bitmaps. Ich interpretiere das so, dass obiger Code im Codebehind mehr oder weniger zufälliger Weise funktioniert, da der Garbage-Collector den Stream noch nicht abgeräumt hat. Scheinbar landet der Stream wohl in Gen1/0 des GC. Weiter habe ich mich da jetzt nicht eingelesen.