VB.NET/C #;使用BitBlt;在两者中使用相同的代码,内存泄漏出现在VB.. NET而不是c#

本文关键字:VB NET 泄漏 内存 代码 使用 BitBlt | 更新日期: 2023-09-27 18:14:18

希望在应用程序中使用BitBlt,我在BitBlt代码中发现了一个c#参考,它不起作用,并将其转换为VB.net。我主要使用VB.net,这就是为什么我转换它,并试图使用它。它在c#中工作得很好,但在VB.net中它有内存泄漏,我不确定如何修复它。

代码:

c#版本(从上面的源代码稍微修改)。打开新项目,添加1个按钮和1个图片框,修改lstpic . 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个图片框,修改lstpic . 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:'1.jpg")
        lstPics.Add("C:'2.jpg")
        lstPics.Add("C:'3.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.NET/C #;使用BitBlt;在两者中使用相同的代码,内存泄漏出现在VB.. NET而不是c#

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

请注意以下摘自GetHbitmap的文档:

从这个位图创建一个GDI位图对象。…

备注:

你负责调用GDI DeleteObject方法来释放GDI位图对象所使用的内存。

因此,GetHbitmap()方法的名称令人困惑,因为它不仅给您已经存在的句柄,而且实际上创建了一个必须清理的新GDI资源。要使VB代码等同于c#,可以这样做:

hBitmap = bmp.GetHbitmap()
hOldObject = SelectObject(hdcSrc, hBitmap)