WPF 생성비트맵 원본HBitmap() 메모리 누수
이미지를 픽셀 단위로 그려 WPF 내부에 표시해야 합니다.사용하여 이 작업을 수행하려고 합니다.System.Drawing.Bitmap
그 다음에 사용CreateBitmapSourceFromHBitmap()
를 생성하기 위해BitmapSource
WPF 이미지 컨트롤의 경우.어딘가에 메모리 누수가 있습니다. 왜냐하면.CreateBitmapSourceFromBitmap()
를 반복적으로 호출하면 메모리 사용량이 증가하고 응용 프로그램이 종료될 때까지 삭제되지 않습니다.내가 전화하지 않으면CreateBitmapSourceFromBitmap()
메모리 사용량에 눈에 띄는 변화는 없습니다.
for (int i = 0; i < 100; i++)
{
var bmp = new System.Drawing.Bitmap(1000, 1000);
var source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
bmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
source = null;
bmp.Dispose();
bmp = null;
}
어떻게 해야만 시스템을 해제할 수 있습니까?BitmapSource
기억력?
상태에 대한 MSDN:
언급
GDI DeleteObject 메서드를 호출하여 GDI 비트맵 개체에서 사용하는 메모리를 해제해야 합니다.
따라서 다음 코드를 사용합니다.
// at class level
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
// your code
using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(1000, 1000))
{
IntPtr hBitmap = bmp.GetHbitmap();
try
{
var source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(hBitmap);
}
}
나는 또한 너의 것을 교체했습니다.Dispose()
에의 방문.using
진술.
관리되지 않는 핸들을 다룰 때는 항상 "안전 핸들" 래퍼를 사용하는 것이 좋습니다.
public class SafeHBitmapHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[SecurityCritical]
public SafeHBitmapHandle(IntPtr preexistingHandle, bool ownsHandle)
: base(ownsHandle)
{
SetHandle(preexistingHandle);
}
protected override bool ReleaseHandle()
{
return GdiNative.DeleteObject(handle) > 0;
}
}
핸들을 표면화하는 즉시 다음과 같이 구성합니다(API가 IntPtr을 노출하지 않고 항상 안전한 핸들을 반환하는 것이 이상적임).
IntPtr hbitmap = bitmap.GetHbitmap();
var handle = new SafeHBitmapHandle(hbitmap , true);
다음과 같이 사용합니다.
using (handle)
{
... Imaging.CreateBitmapSourceFromHBitmap(handle.DangerousGetHandle(), ...)
}
SafeHandle 베이스는 자동 일회용/최종 사용자 패턴을 제공하며, ReleaseHandle 메서드를 재정의하기만 하면 됩니다.
저도 같은 요구사항과 문제(메모리 누수)가 있었습니다.저는 답변으로 표시된 것과 동일한 솔루션을 구현했습니다.그러나 솔루션은 효과가 있지만 성능에 허용할 수 없는 타격을 입혔습니다.i7에서 실행되는 테스트 앱은 CPU가 30-40%, RAM이 200-400MB 증가하고 가비지 컬렉터가 거의 매 밀리초마다 실행되는 것을 확인했습니다.
저는 영상 처리를 하고 있기 때문에 훨씬 더 나은 성능이 필요합니다.저는 다음과 같은 것들을 생각해 냈기 때문에 공유하려고 생각했습니다.
재사용 가능한 글로벌 개체
//set up your Bitmap and WritableBitmap as you see fit
Bitmap colorBitmap = new Bitmap(..);
WriteableBitmap colorWB = new WriteableBitmap(..);
//choose appropriate bytes as per your pixel format, I'll cheat here an just pick 4
int bytesPerPixel = 4;
//rectangles will be used to identify what bits change
Rectangle colorBitmapRectangle = new Rectangle(0, 0, colorBitmap.Width, colorBitmap.Height);
Int32Rect colorBitmapInt32Rect = new Int32Rect(0, 0, colorWB.PixelWidth, colorWB.PixelHeight);
변환 코드
private void ConvertBitmapToWritableBitmap()
{
BitmapData data = colorBitmap.LockBits(colorBitmapRectangle, ImageLockMode.WriteOnly, colorBitmap.PixelFormat);
colorWB.WritePixels(colorBitmapInt32Rect, data.Scan0, data.Width * data.Height * bytesPerPixel, data.Stride);
colorBitmap.UnlockBits(data);
}
구현 예
//do stuff to your bitmap
ConvertBitmapToWritableBitmap();
Image.Source = colorWB;
결과적으로 CPU가 10-13%, 70-150%로 안정적으로 유지됩니다.MB RAM과 가비지 수집기는 6분 동안 두 번만 실행되었습니다.
이 글은 좋은(!!) 글입니다. 비록 모든 댓글과 제안이 있었지만, 저는 그 글들을 알아내는 데 한 시간이 걸렸습니다.여기 SafeHandles가 포함된 BitMapSource를 얻고 이를 사용하여 .PNG 이미지 파일을 만드는 예가 있습니다.맨 아래에는 '사용법'과 일부 참조가 있습니다.물론, 신용은 제 것이 아닙니다. 저는 단지 서기일 뿐입니다.
private static BitmapSource CopyScreen()
{
var left = Screen.AllScreens.Min(screen => screen.Bounds.X);
var top = Screen.AllScreens.Min(screen => screen.Bounds.Y);
var right = Screen.AllScreens.Max(screen => screen.Bounds.X + screen.Bounds.Width);
var bottom = Screen.AllScreens.Max(screen => screen.Bounds.Y + screen.Bounds.Height);
var width = right - left;
var height = bottom - top;
using (var screenBmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
BitmapSource bms = null;
using (var bmpGraphics = Graphics.FromImage(screenBmp))
{
IntPtr hBitmap = new IntPtr();
var handleBitmap = new SafeHBitmapHandle(hBitmap, true);
try
{
bmpGraphics.CopyFromScreen(left, top, 0, 0, new System.Drawing.Size(width, height));
hBitmap = screenBmp.GetHbitmap();
using (handleBitmap)
{
bms = Imaging.CreateBitmapSourceFromHBitmap(
hBitmap,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
} // using
return bms;
}
catch (Exception ex)
{
throw new ApplicationException($"Cannot CopyFromScreen. Err={ex}");
}
} // using bmpGraphics
} // using screen bitmap
} // method CopyScreen
다음은 사용법과 "안전 핸들" 클래스입니다.
private void buttonTestScreenCapture_Click(object sender, EventArgs e)
{
try
{
BitmapSource bms = CopyScreen();
BitmapFrame bmf = BitmapFrame.Create(bms);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(bmf);
string filepath = @"e:\(test)\test.png";
using (Stream stm = File.Create(filepath))
{
encoder.Save(stm);
}
}
catch (Exception ex)
{
MessageBox.Show($"Err={ex}");
}
}
public class SafeHBitmapHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern int DeleteObject(IntPtr hObject);
[SecurityCritical]
public SafeHBitmapHandle(IntPtr preexistingHandle, bool ownsHandle)
: base(ownsHandle)
{
SetHandle(preexistingHandle);
}
protected override bool ReleaseHandle()
{
return DeleteObject(handle) > 0;
}
}
그리고 마지막으로 제 '사용법'을 살펴봅니다.
using System;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Media.Imaging;
using System.Windows.Interop;
using System.Windows;
using System.IO;
using Microsoft.Win32.SafeHandles;
using System.Security;
참조된 DLL은 다음과 같습니다. * PresentationCore * System.코어 * 시스템.배포 * 시스템.도면 * WindowsBase
저의 경우에는 이 방법으로 직접 작동하지 않았습니다.나는 깨끗한 쓰레기 수거기를 추가해야 했습니다.
using (PaintMap p = new PaintMap())
{
System.Drawing.Image i = p.AddLineToMap("1");
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(i, 8419, 5953);
IntPtr hBitmap = bmp.GetHbitmap();
var bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
Image2.ImageSource = bitmapSource;
DeleteObject(hBitmap);
System.GC.Collect();
}
메모리 또는 다른 클래스에서 이미지를 로드하려는 사용자를 위한 솔루션이 있습니다.
public static InteropBitmap Bitmap2BitmapImage(Bitmap bitmap)
{
try
{
var source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
return (InteropBitmap)source;
}
catch (Exception e)
{
MessageBox.Show("Convertion exception: " + e.Message + "\n" +e.StackTrace);
return null;
}
}
그런 다음 이미지의 소스 설정을 사용합니다.
CurrentImage.Source = ImageConverter.Bitmap2BitmapImage(cam.Bitmap);
이미지는 다음과 같은 정의입니다.
<Image x:Name="CurrentImage" Margin="5" StretchDirection="Both"
Width="{Binding Width}"
Height="{Binding Height}">
</Image>
언급URL : https://stackoverflow.com/questions/1546091/wpf-createbitmapsourcefromhbitmap-memory-leak
'programing' 카테고리의 다른 글
NameError: 'reload' 이름이 정의되지 않았습니다. (0) | 2023.05.18 |
---|---|
기본 컬렉션 및 데이터로 Mongo Docker 이미지를 만드는 방법은 무엇입니까? (0) | 2023.05.18 |
결과를 제한하면 MongoDB가 생성되지만 여전히 전체 카운트를 얻을 수 있습니까? (0) | 2023.05.18 |
Visual Studio 2010 및 VB.NET에서 글로벌 변수 선언 (0) | 2023.05.18 |
Azure devops 배포 실패: 유형 오류: 정의되지 않은 속성 'scmUri'를 읽을 수 없습니다. (0) | 2023.05.18 |