C# Wpf&x27;s InteropBitmap与GDI+;:高cpu使用率

C# Wpf&x27;s InteropBitmap与GDI+;:高cpu使用率,c#,wpf,interop,gdi+,C#,Wpf,Interop,Gdi+,我创建了一个小的wpf测试应用程序,它使用System.Drawing.Graphics和wpf的InteropBitmap每隔30毫秒渲染一些随机矩形。我认为InteropBitmap会比WriteableBitmap快:它能够从内存部分更新自己 在执行应用程序时(屏幕尺寸1600*1200),应用程序的cpu使用率仅为2-10%,双核3GHz。但总体cpu使用率约为80-90%,因为进程“系统(NT内核和系统)”上升到70%EDIT:我注意到RAM的使用在15秒内周期性地增加超过1GB,然后

我创建了一个小的wpf测试应用程序,它使用System.Drawing.Graphics和wpf的InteropBitmap每隔30毫秒渲染一些随机矩形。我认为InteropBitmap会比WriteableBitmap快:它能够从内存部分更新自己

在执行应用程序时(屏幕尺寸1600*1200),应用程序的cpu使用率仅为2-10%,双核3GHz。但总体cpu使用率约为80-90%,因为进程“系统(NT内核和系统)”上升到70%EDIT:我注意到RAM的使用在15秒内周期性地增加超过1GB,然后突然下降到正常水平,以此类推

也许可以优化以下代码:

namespace InteropBitmapTest{

 using System;
 using System.Drawing;
 using System.Runtime.InteropServices;
 using System.Windows;
 using System.Windows.Interop;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using Color = System.Drawing.Color;

public partial class Window1 : Window
{

    private System.Drawing.Bitmap gdiBitmap;
    private Graphics graphics;


    InteropBitmap interopBitmap;

    const uint FILE_MAP_ALL_ACCESS = 0xF001F;
    const uint PAGE_READWRITE = 0x04;

    private int bpp = PixelFormats.Bgr32.BitsPerPixel / 8;

    private Random random;
    private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();


    SolidBrush[] brushes = new SolidBrush[] { new SolidBrush(Color.Lime), new SolidBrush(Color.White) };


    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr CreateFileMapping(IntPtr hFile,
    IntPtr lpFileMappingAttributes,
    uint flProtect,
    uint dwMaximumSizeHigh,
    uint dwMaximumSizeLow,
    string lpName);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject,
    uint dwDesiredAccess,
    uint dwFileOffsetHigh,
    uint dwFileOffsetLow,
    uint dwNumberOfBytesToMap);

    public Window1()
    {
        InitializeComponent();

        Loaded += Window1_Loaded;

        WindowState = WindowState.Maximized;

        timer.Tick += timer_Tick;
        timer.Interval = 30;

        random = new Random();

    }

    void Window1_Loaded(object sender, RoutedEventArgs e)
    {
        // create interopbitmap, gdi bitmap, get Graphics object
        CreateBitmaps();


        // start drawing 100 gdi+ rectangles every 30 msec:
        timer.Start();
    }


    void timer_Tick(object sender, EventArgs e)
    {
        int width = 50;


        // Draw 100 gdi+ rectangles :


        for (int i = 0; i < 100; i++)
        {
            int left = random.Next((int)(ActualWidth - width));
            int top = random.Next((int)(ActualHeight - width));


            graphics.FillRectangle(brushes[left % 2], left, top, width, width);

        }


        interopBitmap.Invalidate(); // should only update video memory (and not copy the whole bitmap to video memory before)

    }


    void CreateBitmaps()
    {

        uint byteCount = (uint) (ActualWidth * ActualHeight * bpp);


        //Allocate/reserve memory to write to

        var sectionPointer = CreateFileMapping(new IntPtr(-1), IntPtr.Zero, PAGE_READWRITE, 0, byteCount, null);

        var mapPointer = MapViewOfFile(sectionPointer, FILE_MAP_ALL_ACCESS, 0, 0, byteCount);

        var format = PixelFormats.Bgr32;

        //create the InteropBitmap

        interopBitmap = Imaging.CreateBitmapSourceFromMemorySection(sectionPointer, (int)ActualWidth, (int)ActualHeight, format,
            (int)(ActualWidth * format.BitsPerPixel / 8), 0) as InteropBitmap;


        //create the GDI Bitmap

        gdiBitmap = new System.Drawing.Bitmap((int)ActualWidth, (int)ActualHeight,
                                    (int)ActualWidth * bpp,
                                     System.Drawing.Imaging.PixelFormat.Format32bppPArgb,
                                    mapPointer);

        // Get good old GDI Graphics

        graphics = Graphics.FromImage(gdiBitmap);


        // set the interopbitmap as Source to the wpf image defined in XAML 

        wpfImage.Source = (BitmapSource) interopBitmap; 

    }





}}
namespace interopbitmapptest{
使用制度;
使用系统图;
使用System.Runtime.InteropServices;
使用System.Windows;
使用System.Windows.Interop;
使用System.Windows.Media;
使用System.Windows.Media.Imaging;
使用颜色=System.Drawing.Color;
公共部分类Window1:Window
{
专用System.Drawing.Bitmap gdiBitmap;
私有图形;
InteropBitmap InteropBitmap;
consuint FILE\u MAP\u ALL\u ACCESS=0xF001F;
consuint PAGE_READWRITE=0x04;
private int bpp=PixelFormats.Bgr32.BitsPerPixel/8;
私有随机;
private System.Windows.Forms.Timer Timer=new System.Windows.Forms.Timer();
SolidBrush[]画笔=新的SolidBrush[]{新的SolidBrush(Color.Lime),新的SolidBrush(Color.White)};
[DllImport(“kernel32.dll”,SetLastError=true)]
静态外部IntPtr CreateFileMapping(IntPtr hFile,
IntPtr lpFileMappingAttributes,
保护,保护,
uint DW最大尺寸高,
uint dwMaximumSizeLow,
字符串(名称);
[DllImport(“kernel32.dll”,SetLastError=true)]
静态外部IntPtr MapViewOfFile(IntPtr hFileMappingObject,
uint DWD期望访问,
uint dwFileOffsetHigh,
uint dwFileOffsetLow,
uint dwNumberOfBytesToMap);
公共窗口1()
{
初始化组件();
加载+=窗口1u加载;
WindowState=WindowState.Maximized;
timer.Tick+=定时器_Tick;
时间间隔=30;
随机=新随机();
}
无效窗口1_已加载(对象发送器,路由目标e)
{
//创建interopbitmap、gdi位图、获取图形对象
创建位图();
//开始每30毫秒绘制100个gdi+矩形:
timer.Start();
}
无效计时器勾号(对象发送方,事件参数e)
{
整数宽度=50;
//绘制100个gdi+矩形:
对于(int i=0;i<100;i++)
{
int left=random.Next((int)(实际宽度-宽度));
int top=random.Next((int)(实际高度-宽度));
graphics.FillRectangle(画笔[左%2],左,上,宽,宽);
}
interopBitmap.Invalidate();//应该只更新视频内存(之前不要将整个位图复制到视频内存)
}
void CreateBitmaps()
{
uint字节数=(uint)(实际宽度*实际高度*bpp);
//分配/保留要写入的内存
var sectionPointer=CreateFileMapping(新的IntPtr(-1),IntPtr.Zero,PAGE_READWRITE,0,byteCount,null);
var mapPointer=MapViewOfFile(sectionPointer,FILE\u MAP\u ALL\u ACCESS,0,0,byteCount);
var format=PixelFormats.Bgr32;
//创建InteropBitmap
interopBitmap=Imaging.CreateBitmapSourceFromMemorySection(节指针,(int)实际宽度,(int)实际高度,格式,
(int)(实际宽度*format.BitsPerPixel/8),0)作为InteropBitmap;
//创建GDI位图
gdiBitmap=new System.Drawing.Bitmap((int)实际宽度,(int)实际高度,
(int)实际宽度*bpp,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb,
地图指针);
//得到好的老GDI图形
graphics=graphics.FromImage(gdiBitmap);
//将interopbitmap设置为XAML中定义的wpf映像的源
wpfImage.Source=(BitmapSource)interopBitmap;
}
}}
XAML:


WTF:这是一个InteropBitmap内存泄漏,它是.NET 4.0中新增的

将目标框架设置为3.5,一切正常


另一个选项是在每次“interopBitmap.Invalidate();”调用之后加上“GC.Collect();”。

当您不运行应用程序时,系统进程的外观如何?或者当你的应用程序遇到断点时?我的第一个想法是进程是某种恶意软件。然后它不会消耗任何cpu。但我注意到的另一件事是,RAM使用在15秒内周期性地增加到3GB,然后下降到正常水平,依此类推。因此,高cpu使用率似乎是每30毫秒分配一次内存(调用interopbitmap.Invalidate()的计时器事件)的结果。
<Window x:Class="InteropBitmapTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
  <Image Name="wpfImage" Stretch="None" />
</Window>