从终端服务器上的 winforms 访问冲突

本文关键字:winforms 访问冲突 终端 服务器 | 更新日期: 2023-09-27 18:36:56

以下代码崩溃,对象在其他地方使用,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)

从终端服务器上的 winforms 访问冲突

我设法通过打开进程资源管理器并打开主线程的堆栈跟踪来找出发生了什么。我很幸运它陷入了僵局,所以它停留在正确的位置(如果发生访问违规,我没有看到任何异常)。

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

开始图像

FileOpenScreenHook <-- 'ullo 'ullo what 'ave we 'ere

GdiDrawImage

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

显然,安装 pdf 插件时屏幕钩子包含一个错误,要么损坏我以前制作的位图,要么做类似的事情在不同的线程上渲染或其他东西。

而且由于屏幕挂钩被注入到每个进程中,一旦 FileOpen 服务中出现争用条件,就没有一个使用 Bitmap 的进程正常运行(现在,这是一个拥有 20+ 用户的终端服务器,所以你可以想象我们得到的大量调用)。

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

所以是的,如果你突然出现随机GDI错误,并且软件或Windows更新方面没有任何变化,那么gdiplus或相关的dll被损坏了(sfc/scannow表示一切正常(https://i.stack.imgur.com/wujvq.jpg))或一些第三方软件决定钩接到每个进程并像rootkit一样劫持GDI渲染api是个好主意。

在这里发布这个是因为这个提示在 4 天前问题开始时会非常方便。