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.
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.
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
StreamzuBitmapzuBitmapImageum eigentlich eineBitmapSourcezu erhalten?Wenn du das Bild per Binding übergeben möchtest, dann würde auch ein simples
byte[]genügen, was dann automatisch vomImageSourceConverterumgewandelt 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.
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.