来自终端服务器上的winforms的OnPaint访问冲突

时间:2015-01-08 12:30:27

标签: c# .net winforms

以下代码在其他地方使用的对象崩溃,gdiplus.dll中的访问冲突或各种令人讨厌的错误。

我立刻想到了Bitmap对象上线程之间的竞争条件(因为gdiplus不是线程安全的),除了我根本不使用线程。此外,GDI计数在极限范围内(我在创建新的之前删除了bmp.Dispose()以查看它是否与它有任何关系。)

它在我的计算机上完全正常,但在一段时间后在我们的一台终端服务器上崩溃。

  public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        Random rnd = new Random();
        vx = 0.01f * rnd.NextDouble();
        vy = 0.01f * rnd.NextDouble();
    }

    private double x = 0;
    private double y = 0;

    private double vx = 0.01f;
    private double vy = 0.01f;

    private Bitmap bmp;
    private void timer1_Tick(object sender, EventArgs e)
    {
        this.label2.Text = "Thread from timer : " + System.Threading.Thread.CurrentThread.ManagedThreadId;
        bmp = new Bitmap(this.Width, this.Height);
        using (Graphics g = Graphics.FromImage(bmp))
        {
            g.Clear(Color.Black);
        }

        x += vx;
        y += vy;

        if (x > 1) { vx = -vx; x = 1; }
        if (y > 1) { vy = -vy; y = 1; }
        if (x < 0) { vx = -vx; x = 0; }
        if (y < 0) { vy = -vy; y = 0; }

        this.Refresh();

    }
    protected override void OnPaint(PaintEventArgs e)
    {

        try
        {
            this.label1.Text = "Thread from paint : " + System.Threading.Thread.CurrentThread.ManagedThreadId;

            if (bmp != null)
            {
                System.Threading.Thread.Sleep(1000);
                Graphics g = e.Graphics;
                g.Clear(SystemColors.Control);
                g.DrawImage(bmp, new Rectangle((int)(x * bmp.Width - 50), (int)(y * bmp.Height - 50), 50, 50));
            }

        }
        catch (Exception ex)
        {
            MessageBox.Show("Error: " + ex.GetType().FullName + " - " + ex.Message);
        }
    }
}

发生的事情:

Exception: System.InvalidOperationException
- Message: Object is currently in use elsewhere.
- Stacktrace: 
at System.Drawing.Image.get_Width()

Exception: System.InvalidOperationException
- Message: Object is currently in use elsewhere.
- Stacktrace: 
at System.Drawing.Image.get_RawFormat()

Faulting application name: TestBitmapRendering.exe, version: 1.0.0.0, time stamp: 0x54ae5fce
Faulting module name: gdiplus.dll, version: 6.1.7601.18455, time stamp: 0x535b1ac9

Exception Info: System.AccessViolationException
Stack:
   at System.Drawing.SafeNativeMethods+Gdip.GdipDrawImageRectI(System.Runtime.InteropServices.HandleRef, System.Runtime.InteropServices.HandleRef, Int32, Int32, Int32, Int32)

1 个答案:

答案 0 :(得分:0)

我设法通过打开进程资源管理器并打开主线程的堆栈跟踪来了解发生了什么。我很幸运它陷入了僵局,因此它保持在正确的位置(如果访问违规我没有看到任何与众不同的东西)。

在那里,我看到一些奇怪的东西,堆栈的顶部是

  

BeginImage

     

FileOpenScreenHook&lt; - &#39; ullo&#39; ullo我们&#39; ave

     

GdiDrawImage

原来安装了一个名为FileOpen(http://www.fileopen.com)的DRM软件,该软件运行FileOpen服务并在所有进程中注入屏幕挂钩。

显然,当安装pdf插件时,屏幕挂钩包含一个错误,要么破坏我以前制作的Bitmap,要么做一些类似于在不同线程上渲染的东西。

并且因为屏幕挂钩被注入到每个进程中,一旦在FileOpen服务中发生竞争条件,使用Bitmaps的单个进程没有正确运行(现在,这是一个拥有20多个用户的终端服务器,所以你可以想象我们得到了很多电话。

停止服务并终止所有FileOpen进程后,问题就消失了。

所以,是的,如果你突然有随机的GDI错误并且没有任何改变软件或Windows更新明智,那么gdiplus或相关的dll已被破坏(sfc / scannow表示一切都很好( http://i.imgur.com/1nbVW8N.jpg))或某些第三方软件决定挂钩每个进程并劫持GDI呈现api就像rootkit一样。

在此发布此信息是因为这个提示在4天前出现问题时会非常方便。