VB.NET/C#;使用 BitBlt;在两者中使用相同的代码,内存泄漏出现在 VB.NET 而不是 C#
VB.NET/C#; Using BitBlt; Using same code in both, memory leak appearing in VB.NET but not C#
为了将 BitBlt 用于应用程序,我在 BitBlt code not working 找到了 C# 参考并将其转换为 VB.net。我主要使用 VB.net 这就是我转换它并尝试这样使用它的原因。它在 C# 中工作正常,但在 VB.net 中它有内存泄漏,我不确定如何修复它。
代码:
C# 版本(从上面的源代码修改了一点)。
打开新项目,添加1个按钮和1个图片框,修改lstPics.Add():
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
[DllImport("gdi32.dll", EntryPoint = "SelectObject")]
public static extern System.IntPtr SelectObject(
[In()] System.IntPtr hdc,
[In()] System.IntPtr h);
[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteObject(
[In()] System.IntPtr ho);
[DllImport("gdi32.dll", EntryPoint = "BitBlt")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool BitBlt(
[In()] System.IntPtr hdc, int x, int y, int cx, int cy,
[In()] System.IntPtr hdcSrc, int x1, int y1, uint rop);
public Form1()
{
InitializeComponent();
}
public Int16 lstLoc = 0;
private void button1_Click(object sender, EventArgs e)
{
List<string> lstPics = new List<string>();
lstPics.Add("C:\1.jpg");
lstPics.Add("C:\2.jpg");
lstPics.Add("C:\3.jpg");
if ((lstLoc == lstPics.Count))
{
lstLoc = 0;
}
string strLoc = lstPics[lstLoc];
lstLoc++;
using (Bitmap bmp = (Bitmap)Bitmap.FromFile(strLoc))
using (Graphics grDest = Graphics.FromHwnd(pictureBox1.Handle))
using (Graphics grSrc = Graphics.FromImage(bmp))
{
IntPtr hdcDest = IntPtr.Zero;
IntPtr hdcSrc = IntPtr.Zero;
IntPtr hBitmap = IntPtr.Zero;
IntPtr hOldObject = IntPtr.Zero;
try
{
hdcDest = grDest.GetHdc();
hdcSrc = grSrc.GetHdc();
hBitmap = bmp.GetHbitmap();
hOldObject = SelectObject(hdcSrc, hBitmap);
if (hOldObject == IntPtr.Zero)
throw new Win32Exception();
if (!BitBlt(hdcDest, 0, 0, pictureBox1.Width, pictureBox1.Height,
hdcSrc, 0, 0, 0x00CC0020U))
throw new Win32Exception();
}
finally
{
if (hOldObject != IntPtr.Zero) SelectObject(hdcSrc, hOldObject);
if (hBitmap != IntPtr.Zero) DeleteObject(hBitmap);
if (hdcDest != IntPtr.Zero) grDest.ReleaseHdc(hdcDest);
if (hdcSrc != IntPtr.Zero) grSrc.ReleaseHdc(hdcSrc);
}
}
}
}
}
VB.net 版本(由我转换)。
打开新项目,添加1个按钮和1个图片框,修改lstPics.Add():
Imports System.ComponentModel
Public Class Form1
Public Declare Function SelectObject Lib "gdi32.dll" Alias "SelectObject" (ByVal hdc As System.IntPtr, ByVal h As System.IntPtr) As System.IntPtr
Public Declare Function DeleteObject Lib "gdi32.dll" Alias "DeleteObject" (ByVal ho As System.IntPtr) As Boolean
Public Declare Function BitBlt Lib "gdi32.dll" Alias "BitBlt" (ByVal hdc As System.IntPtr, ByVal x As Integer, ByVal y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal hdcSrc As System.IntPtr, ByVal x1 As Integer, ByVal y1 As Integer, ByVal rop As UInteger) As Boolean
Public lstLoc As Int16 = 0
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim mil1 As Int64 = 0
Dim mil2 As Int64 = 0
Dim mil3 As Int64 = 0
Dim lstPics As New List(Of String)
lstPics.Add("C:.jpg")
lstPics.Add("C:.jpg")
lstPics.Add("C:.jpg")
If lstLoc = lstPics.Count Then lstLoc = 0
Dim strLoc As String = lstPics(lstLoc)
lstLoc += 1
Using bmp As Bitmap = Bitmap.FromFile(strLoc),
grDest As Graphics = Graphics.FromHwnd(PictureBox1.Handle),
grSrc As Graphics = Graphics.FromImage(bmp)
Dim hdcDest As IntPtr = IntPtr.Zero
Dim hdcSrc As IntPtr = IntPtr.Zero
Dim hBitmap As IntPtr = IntPtr.Zero
Dim hOldObject As IntPtr = IntPtr.Zero
Try
hdcDest = grDest.GetHdc()
hdcSrc = grSrc.GetHdc()
hBitmap = bmp.GetHbitmap()
hOldObject = SelectObject(hdcSrc, bmp.GetHbitmap)
If (hOldObject = IntPtr.Zero) Then Throw New Win32Exception()
If Not BitBlt(hdcDest, 0, 0, PictureBox1.Width, PictureBox1.Height, hdcSrc, 0, 0, 13369376) Then Throw New Win32Exception()
Catch ex As Exception
MessageBox.Show(ex.Message.ToString & vbNewLine & vbNewLine & ex.ToString)
Finally
If (hOldObject <> IntPtr.Zero) Then SelectObject(hdcSrc, hOldObject)
If (hBitmap <> IntPtr.Zero) Then DeleteObject(hBitmap)
If (hdcDest <> IntPtr.Zero) Then grDest.ReleaseHdc(hdcDest)
If (hdcSrc <> IntPtr.Zero) Then grSrc.ReleaseHdc(hdcSrc)
End Try
End Using
End Sub
End Class
当您按下按钮并循环浏览不同的图片时,C# 版本的内存使用量会激增,但随后又会下降。然而,VB.net 版本的内存使用量会不断增加。这种泄漏是从哪里来的,为什么只发生在 VB.net?
我知道我还有其他可用的选项,例如 DrawImage 或直接在图片框中显示。我想使用 BitBlt 来提高速度。并想在将其放入主应用程序(处理图像)之前对其进行测试并使其正常工作。
感谢您的帮助。
还有一个额外的问题,有没有办法在上面的代码中降低内存使用量(主要是在它达到峰值时)。只是想确保我没有浪费。谢谢。
VB版本调用bmp.GetHbitmap()
两次:
hBitmap = bmp.GetHbitmap()
hOldObject = SelectObject(hdcSrc, bmp.GetHbitmap)
' ^^^ Here it is again
而C#版本只调用一次:
hBitmap = bmp.GetHbitmap();
hOldObject = SelectObject(hdcSrc, hBitmap);
// ^^^ uses the handle from the previous line
请注意 documentation on GetHbitmap 中的这些摘录:
Creates a GDI bitmap object from this Bitmap.
...
Remarks:
You are responsible for calling the GDI DeleteObject method to free the memory used by the GDI bitmap object.
因此 GetHbitmap()
方法的命名令人困惑,因为它不仅为您提供已经存在的句柄,而且实际上创建了一个您必须清理的新 GDI 资源。要使 VB 代码等同于 C#,请执行以下操作:
hBitmap = bmp.GetHbitmap()
hOldObject = SelectObject(hdcSrc, hBitmap)
为了将 BitBlt 用于应用程序,我在 BitBlt code not working 找到了 C# 参考并将其转换为 VB.net。我主要使用 VB.net 这就是我转换它并尝试这样使用它的原因。它在 C# 中工作正常,但在 VB.net 中它有内存泄漏,我不确定如何修复它。
代码:
C# 版本(从上面的源代码修改了一点)。 打开新项目,添加1个按钮和1个图片框,修改lstPics.Add():
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
[DllImport("gdi32.dll", EntryPoint = "SelectObject")]
public static extern System.IntPtr SelectObject(
[In()] System.IntPtr hdc,
[In()] System.IntPtr h);
[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteObject(
[In()] System.IntPtr ho);
[DllImport("gdi32.dll", EntryPoint = "BitBlt")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool BitBlt(
[In()] System.IntPtr hdc, int x, int y, int cx, int cy,
[In()] System.IntPtr hdcSrc, int x1, int y1, uint rop);
public Form1()
{
InitializeComponent();
}
public Int16 lstLoc = 0;
private void button1_Click(object sender, EventArgs e)
{
List<string> lstPics = new List<string>();
lstPics.Add("C:\1.jpg");
lstPics.Add("C:\2.jpg");
lstPics.Add("C:\3.jpg");
if ((lstLoc == lstPics.Count))
{
lstLoc = 0;
}
string strLoc = lstPics[lstLoc];
lstLoc++;
using (Bitmap bmp = (Bitmap)Bitmap.FromFile(strLoc))
using (Graphics grDest = Graphics.FromHwnd(pictureBox1.Handle))
using (Graphics grSrc = Graphics.FromImage(bmp))
{
IntPtr hdcDest = IntPtr.Zero;
IntPtr hdcSrc = IntPtr.Zero;
IntPtr hBitmap = IntPtr.Zero;
IntPtr hOldObject = IntPtr.Zero;
try
{
hdcDest = grDest.GetHdc();
hdcSrc = grSrc.GetHdc();
hBitmap = bmp.GetHbitmap();
hOldObject = SelectObject(hdcSrc, hBitmap);
if (hOldObject == IntPtr.Zero)
throw new Win32Exception();
if (!BitBlt(hdcDest, 0, 0, pictureBox1.Width, pictureBox1.Height,
hdcSrc, 0, 0, 0x00CC0020U))
throw new Win32Exception();
}
finally
{
if (hOldObject != IntPtr.Zero) SelectObject(hdcSrc, hOldObject);
if (hBitmap != IntPtr.Zero) DeleteObject(hBitmap);
if (hdcDest != IntPtr.Zero) grDest.ReleaseHdc(hdcDest);
if (hdcSrc != IntPtr.Zero) grSrc.ReleaseHdc(hdcSrc);
}
}
}
}
}
VB.net 版本(由我转换)。 打开新项目,添加1个按钮和1个图片框,修改lstPics.Add():
Imports System.ComponentModel
Public Class Form1
Public Declare Function SelectObject Lib "gdi32.dll" Alias "SelectObject" (ByVal hdc As System.IntPtr, ByVal h As System.IntPtr) As System.IntPtr
Public Declare Function DeleteObject Lib "gdi32.dll" Alias "DeleteObject" (ByVal ho As System.IntPtr) As Boolean
Public Declare Function BitBlt Lib "gdi32.dll" Alias "BitBlt" (ByVal hdc As System.IntPtr, ByVal x As Integer, ByVal y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal hdcSrc As System.IntPtr, ByVal x1 As Integer, ByVal y1 As Integer, ByVal rop As UInteger) As Boolean
Public lstLoc As Int16 = 0
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim mil1 As Int64 = 0
Dim mil2 As Int64 = 0
Dim mil3 As Int64 = 0
Dim lstPics As New List(Of String)
lstPics.Add("C:.jpg")
lstPics.Add("C:.jpg")
lstPics.Add("C:.jpg")
If lstLoc = lstPics.Count Then lstLoc = 0
Dim strLoc As String = lstPics(lstLoc)
lstLoc += 1
Using bmp As Bitmap = Bitmap.FromFile(strLoc),
grDest As Graphics = Graphics.FromHwnd(PictureBox1.Handle),
grSrc As Graphics = Graphics.FromImage(bmp)
Dim hdcDest As IntPtr = IntPtr.Zero
Dim hdcSrc As IntPtr = IntPtr.Zero
Dim hBitmap As IntPtr = IntPtr.Zero
Dim hOldObject As IntPtr = IntPtr.Zero
Try
hdcDest = grDest.GetHdc()
hdcSrc = grSrc.GetHdc()
hBitmap = bmp.GetHbitmap()
hOldObject = SelectObject(hdcSrc, bmp.GetHbitmap)
If (hOldObject = IntPtr.Zero) Then Throw New Win32Exception()
If Not BitBlt(hdcDest, 0, 0, PictureBox1.Width, PictureBox1.Height, hdcSrc, 0, 0, 13369376) Then Throw New Win32Exception()
Catch ex As Exception
MessageBox.Show(ex.Message.ToString & vbNewLine & vbNewLine & ex.ToString)
Finally
If (hOldObject <> IntPtr.Zero) Then SelectObject(hdcSrc, hOldObject)
If (hBitmap <> IntPtr.Zero) Then DeleteObject(hBitmap)
If (hdcDest <> IntPtr.Zero) Then grDest.ReleaseHdc(hdcDest)
If (hdcSrc <> IntPtr.Zero) Then grSrc.ReleaseHdc(hdcSrc)
End Try
End Using
End Sub
End Class
当您按下按钮并循环浏览不同的图片时,C# 版本的内存使用量会激增,但随后又会下降。然而,VB.net 版本的内存使用量会不断增加。这种泄漏是从哪里来的,为什么只发生在 VB.net?
我知道我还有其他可用的选项,例如 DrawImage 或直接在图片框中显示。我想使用 BitBlt 来提高速度。并想在将其放入主应用程序(处理图像)之前对其进行测试并使其正常工作。
感谢您的帮助。
还有一个额外的问题,有没有办法在上面的代码中降低内存使用量(主要是在它达到峰值时)。只是想确保我没有浪费。谢谢。
VB版本调用bmp.GetHbitmap()
两次:
hBitmap = bmp.GetHbitmap()
hOldObject = SelectObject(hdcSrc, bmp.GetHbitmap)
' ^^^ Here it is again
而C#版本只调用一次:
hBitmap = bmp.GetHbitmap();
hOldObject = SelectObject(hdcSrc, hBitmap);
// ^^^ uses the handle from the previous line
请注意 documentation on GetHbitmap 中的这些摘录:
Creates a GDI bitmap object from this Bitmap.
...Remarks:
You are responsible for calling the GDI DeleteObject method to free the memory used by the GDI bitmap object.
因此 GetHbitmap()
方法的命名令人困惑,因为它不仅为您提供已经存在的句柄,而且实际上创建了一个您必须清理的新 GDI 资源。要使 VB 代码等同于 C#,请执行以下操作:
hBitmap = bmp.GetHbitmap()
hOldObject = SelectObject(hdcSrc, hBitmap)