WPF 已知问题 传入错误数据给到 WriteableBitmap 可能导致渲染线程锁住
本文记录一个 WPF 已知问题,此问题已经被我修复。传入错误的数据给到 WriteableBitmap 对象,比如调用 WritePixels 时传入错误的 stride 数值,将可能导致渲染线程进入无限自旋锁
问题描述
应用程序停止渲染,或者是界面未响应。表现是在 渲染 线程卡住,从任务管理器看可以看到有一个 CPU 核在跑,但没跑满。进行本机代码调试可以看到卡在如下调用堆栈
 	wpfgfx_cor3.dll!CWGXBitmapLockState::LockRead() line 1086	C++
 	wpfgfx_cor3.dll!CWGXBitmap::HrLock(const tagRECT & rcLock={...}, MilPixelFormat::Enum pxlFormat=BGRA32bpp, unsigned int cbStride=1200, unsigned int cbBufferSize=1200, void * pvPixels=0x0000028eea8a0000, unsigned long dwFlags=1, IWGXBitmapLock * * ppILock=0x0000009a90b7b268, int) line 743	C++
 	wpfgfx_cor3.dll!CSystemMemoryBitmap::Lock(const WICRect * prcLock, unsigned long dwFlags=1, IWGXBitmapLock * * ppILock=0x0000009a90b7b268) line 176	C++
 	wpfgfx_cor3.dll!CWriteProtectedBitmap::Lock(const WICRect * prcLock, unsigned long dwFlags, IWGXBitmapLock * * ppILock) line 125	C++
 	wpfgfx_cor3.dll!CWGXBitmap::CopyPixels(const WICRect * prc=0x0000009a90b7b4a0, unsigned int cbStride=1200, unsigned int cbBufferSize=1200, unsigned char * pbPixels=0x0000028edfc56540) line 477	C++
 	wpfgfx_cor3.dll!CWGXWrapperBitmap::CopyPixels(const WICRect * prc, unsigned int cbStride, unsigned int cbBufferSize, unsigned char * pbPixels=0x0000028edfc56540) line 411	C++
 	WindowsCodecs.dll!00007ffcaa688759()
 	WindowsCodecs.dll!00007ffcaa6ab5d3()
 	WindowsCodecs.dll!00007ffcaa68f9c6()
 	wpfgfx_cor3.dll!CWGXWrapperBitmap::CopyPixels(const WICRect * prc, unsigned int cbStride, unsigned int cbBufferSize, unsigned char * pbPixels=0x0000028eebe50ad0) line 411	C++
 	wpfgfx_cor3.dll!CWGXWrapperBitmap::CopyPixels(const WICRect * prc, unsigned int cbStride, unsigned int cbBufferSize, unsigned char * pbPixels=0x0000028eebe50ad0) line 411	C++
 	wpfgfx_cor3.dll!CWGXWrapperBitmap::CopyPixels(const WICRect * prc, unsigned int cbStride, unsigned int cbBufferSize, unsigned char * pbPixels=0x0000028eebe50ad0) line 411	C++
 	wpfgfx_cor3.dll!CSystemMemoryBitmap::UnsafeUpdateFromSource(IWGXBitmapSource * pISource=0x0000028edfb110d0, MilRectU & rcSrc, unsigned int uDstLeft, unsigned int uDstTop=0) line 263	C++
 	wpfgfx_cor3.dll!CSwBitmapColorSource::FillTextureWithTransformedSource(IWGXBitmapSource * pIBitmapSource=0x0000028edfb110d0) line 706	C++
 	wpfgfx_cor3.dll!CSwBitmapColorSource::FillTexture() line 584	C++
 	wpfgfx_cor3.dll!CSwBitmapColorSource::Realize() line 882	C++
 	wpfgfx_cor3.dll!CSwBitmapColorSource::DeriveFromBitmapAndContext(IWGXBitmapSource * pIBitmap=0x0000028edfc73650, CMatrix<CoordinateSpace::RealizationSampling,CoordinateSpace::DeviceHPC> * pmatBitmapToSampleSpace=0x0000009a90b7b9f0, const CColorSourceCreator * pCSCreator, bool fPrefilterEnabled, float rPrefilterThreshold=1.41421354, IMILResourceCache * pICacheAlternate=0x0000028eebd59cb0, IWGXBitmap * * ppBitmap=0x0000009a90b7b9e0) line 124	C++
 	wpfgfx_cor3.dll!CColorSourceCreator::GetCS_PrefilterAndResample(IWGXBitmapSource * pIBitmapSource, MilBitmapWrapMode::Enum wrapMode=Extend, const _D3DCOLORVALUE * pBorderColor=0x0000028eebd59d84, const CMatrix<CoordinateSpace::RealizationSampling,CoordinateSpace::DeviceHPC> * pmatTextureHPCToDeviceHPC=0x0000009a90b7bae0, MilBitmapInterpolationMode::Enum interpolationMode=Linear, bool prefilterEnable=false, float prefilterThreshold=1.41421354, IMILResourceCache * pICacheAlternate=0x0000028eebd59cb0, CColorSource * * ppColorSource=0x0000009a90b7bc30) line 1131	C++
 	wpfgfx_cor3.dll!CSoftwareRasterizer::GetCS_Brush(CMILBrush * pBrush=0x0000028eebd59ca8, const CMatrix<CoordinateSpace::BaseSampling,CoordinateSpace::DeviceHPC> & matWorldHPCToDeviceHPC={...}, const CContextState * pContextState=0x0000028edfacdae8, CColorSource * * ppColorSource=0x0000009a90b7bc30) line 866	C++
 	wpfgfx_cor3.dll!CSoftwareRasterizer::FillPath(CSpanSink * pSpanSink=0x0000028eebd7a440, CSpanClipper * pSpanClipper=0x0000009a90b7c278, const CContextState * pContextState=0x0000028edfacdae8, const CShapeBase * pShape=0x0000009a90b7c578, const CMatrix<CoordinateSpace::LocalRenderingHPC,CoordinateSpace::DeviceHPC> * pmatShapeToDevice=0x0000028edfacdc80, CMILBrush * pBrush=0x0000028eebd59ca8, const CMatrix<CoordinateSpace::BaseSampling,CoordinateSpace::DeviceHPC> & matWorldToDevice={...}, IMILEffectList * pIEffect=0x0000000000000000, float rComplementFactor=-1.00000000, const TMilRect_<int,tagRECT,MilPointAndSizeL,RectUniqueness::_CMILSurfaceRect_> * prcComplementBounds=0x0000000000000000) line 680	C++
 	wpfgfx_cor3.dll!CSoftwareRasterizer::FillPathUsingBrushRealizer(CSpanSink * pSpanSink=0x0000028eebd7a440, MilPixelFormat::Enum fmtTarget, DisplayId associatedDisplay, CSpanClipper * pSpanClipper=0x0000009a90b7c278, const CContextState * pContextState=0x0000028edfacdae8, BrushContext * pBrushContext=0x0000000000000000, const CShapeBase * pShape=0x0000009a90b7c578, const CMatrix<CoordinateSpace::LocalRenderingHPC,CoordinateSpace::DeviceHPC> * pmatShapeToDevice=0x0000028edfacdc80, CBrushRealizer * pBrushRealizer=0x0000009a90b7c480, const CMatrix<CoordinateSpace::BaseSampling,CoordinateSpace::DeviceHPC> & matWorldToDevice={...}) line 578	C++
 	wpfgfx_cor3.dll!CSwRenderTargetSurface::DrawPathInternal(CContextState * pContextState=0x0000028edfacdae8, BrushContext * pBrushContext=0x0000000000000000, const CShapeBase * pShape=0x0000009a90b7c578, const CPlainPen * pPen=0x0000000000000000, CBrushRealizer * pStrokeBrush=0x0000000000000000, CBrushRealizer * pFillBrush=0x0000009a90b7c480) line 841	C++
 	wpfgfx_cor3.dll!CDrawingContext::FillShapeWithBitmap(IWGXBitmapSource * pIWGXBitmapSource=0x0000028edfc73650, const CMatrix<CoordinateSpace::BaseSampling,CoordinateSpace::LocalRenderingHPC> * pTextureToLocalTransform=0x0000009a90b7c620, CShapeBase * pShape=0x0000009a90b7c578, IMILEffectList * pEffectList=0x0000000000000000, MilBitmapWrapMode::Enum wrapMode=Extend) line 1617	C++
 	wpfgfx_cor3.dll!CDrawingContext::DrawBitmap(IWGXBitmapSource * pIBitmapSource=0x0000028edfc73650, const MilRectF * prcSource=0x0000009a90b7c728, const MilRectF * prcDest=0x0000009a90b7c6f0, float opacity=1.00000000) line 1479	C++
 	wpfgfx_cor3.dll!CDrawingContext::DrawImage(CMilSlaveResource * pImage=0x0000028eebda14f0, const MilPointAndSizeD * prcDestinationBase, TMilSlaveValue<MilPointAndSizeD,MILCMD_RECTRESOURCE,52> * pDestRectAnimations=0x0000000000000000) line 1345	C++
 	wpfgfx_cor3.dll!CMilSlaveRenderData::Draw(IDrawingContext * pIDC=0x0000028edfacda30) line 766	C++
 	wpfgfx_cor3.dll!CMilVisual::RenderContent(CDrawingContext * pDrawingContext=0x0000028edfacda30) line 1204	C++
 	wpfgfx_cor3.dll!CDrawingContext::PreSubgraph(int * pfVisitChildren=0x0000009a90b7ca60) line 5076	C++
 	wpfgfx_cor3.dll!CGraphIterator::Walk(IGraphNode * pRoot, IGraphIteratorSink * pSink=0x0000028edfacda60) line 308	C++
 	wpfgfx_cor3.dll!CDrawingContext::DrawVisualTree(CMilVisual * pRoot=0x0000028eebd71be0, const _D3DCOLORVALUE * pClearColor=0x0000000000000000, const TMilRect_<float,MilRectF,MilPointAndSizeF,RectUniqueness::NotNeeded> & dirtyRect, bool fDrawingIntoVisualBrush=false) line 4424	C++
 	wpfgfx_cor3.dll!CDrawingContext::Render(CMilVisual * pRoot=0x0000028eebd71be0, IMILRenderTarget * pIRenderTarget, const _D3DCOLORVALUE * pClearColor=0x0000000000000000, const TMilRect_<float,MilRectF,MilPointAndSizeF,RectUniqueness::NotNeeded> & rcSurfaceBounds={...}, int fFullRender=1, unsigned int uNumInvalidTargetRegions=0, const MilRectF * rgInvalidTargetRegions=0x0000000000000000, bool fCanAccelerateScroll=false, int * pfNeedsFullPresent=0x0000009a90b7ccc8) line 6007	C++
 	wpfgfx_cor3.dll!CSlaveGenericRenderTarget::Render(bool * pfPresentNeeded=0x0000000000000000) line 88	C++
 	wpfgfx_cor3.dll!CRenderTargetManager::Render(bool * pfPresentNeeded=0x0000009a90b7ce60) line 339	C++
 	wpfgfx_cor3.dll!CComposition::Render(bool * pfPresentNeeded=0x0000009a90b7ce60) line 859	C++
 	wpfgfx_cor3.dll!CComposition::ProcessComposition(bool * pfPresentNeeded=0x0000009a90b7ce60) line 712	C++
 	wpfgfx_cor3.dll!CComposition::Compose(bool * pfPresentNeeded=0x0000009a90b7ce88) line 805	C++
 	wpfgfx_cor3.dll!CConnectionContext::PresentAllPartitions() line 506	C++
 	wpfgfx_cor3.dll!CMilConnection::PresentAllPartitions() line 485	C++
 	wpfgfx_cor3.dll!WgxConnection_SameThreadPresent(HMIL_CONNECTION__ * hConnection) line 135	C++
 	PresentationCore.dll!System.Windows.Media.Composition.DUCE.UnsafeNativeMethods.WgxConnection_SameThreadPresent(System.IntPtr pConnection)
从以上代码可以看到是 WPF 的渲染线程的进入了 CWGXBitmapLockState::LockRead 自旋锁等待 WriteableBitmap 解锁
此问题已经报告给官方(嗯,其实就是我),且我已经修复:https://github.com/dotnet/wpf/issues/8134
复现步骤
- 创建一个 WriteableBitmap 对象且添加到 Image 控件里面
- 调用 WriteableBitmap 的 WritePixels 方法,传入错误的 stride 数值
- 立即调用渲染,如使用 RenderTargetBitmap 对 Image 控件进行截图
最小复现 Demo 代码:https://github.com/lindexi/lindexi_gd/tree/20395cade5a79ed40bdd03acf73320994966c691/HawacearkecallLalarnowhallna
原因
这是因为在 WriteableBitmap 的代码实现没有关注到锁的安全性,导致了传入错误的 stride 数值,抛了异常,让 Unlock 没有被调用,从而让锁没有释放。于是渲染线程等待多久,都等不到锁的释放
问题的代码如下:
        private void WritePixelsImpl(
            Int32Rect sourceRect,
            IntPtr    sourceBuffer,
            int       sourceBufferSize,
            int       sourceBufferStride,
            int       destinationX,
            int       destinationY,
            bool      backwardsCompat
            )
        {
            // 忽略其他代码
                unsafe
                {
                    // 忽略其他代码
                    Lock();
                    MILUtilities.MILCopyPixelBuffer(
                        pDest,
                        outputBufferSize,
                        (uint) _backBufferStride.Value,
                        destBufferBitOffset,
                        pSource,
                        inputBufferSize,
                        (uint) sourceBufferStride,
                        sourceBufferBitOffset,
                        (uint) sourceRect.Height,
                        copyWidthInBits);
                    AddDirtyRect(destinationRect);
                    Unlock();
                }
        }
当传入错误的 stride 数值,将会导致 MILUtilities.MILCopyPixelBuffer 抛出异常,从而导致 Unlock 函数没有被正确调用
渲染线程需要等待 WriteableBitmap 的锁释放,但是由于 WriteableBitmap 的 Unlock 因为异常而没有被正确调用,因此渲染线程进入无限等待
WPF 已知问题 传入错误数据给到 WriteableBitmap 可能导致渲染线程锁住的更多相关文章
- 对象布局已知时 C++ 对象指针的转换时地址调整
		在我调试和研究 netscape 系浏览器插件开发时,注意到了这个问题.即,在对象布局已知(即对象之间具有继承关系)时,不同类型对象的指针进行转换(不管是隐式的从下向上转换,还是强制的从上到下转换)时 ... 
- 风口之下,猪都能飞。当今中国股市牛市,真可谓“错过等七年”。 给你一个回顾历史的机会,已知一支股票连续n天的价格走势,以长度为n的整数数组表示,
		转自:http://www.cnblogs.com/ranranblog/p/5845010.html 风口之下,猪都能飞.当今中国股市牛市,真可谓“错过等七年”. 给你一个回顾历史的机会,已知一支股 ... 
- Delphi 查找标题已知的窗口句柄,遍历窗口控件句柄(转)
		用我的方法来控制其他程序窗体上的窗口控件,必须先了解什么是 回调函数.我的理解是这样的: 回 调函数写出来不是自己的程序去调用的,反而是让其他的东西去调用,比如windows操作系统,比如其他的程序等 ... 
- ARCgis已知线裁剪已知面
		经常遇到需要在ArcGIS中,根据已知线图层(要素)切分已知面图层(要素).经过研究,利用topology拓扑菜单中的construct features可以实现.具体如下 现有用线图层A.面图层B, ... 
- Java集合-5. (List)已知有一个Worker 类如下: 完成下面的要求  1)	创建一个List,在List 中增加三个工人,基本信息如下: 姓名 年龄 工资  zhang3 18 3000  li4 25 3500  wang5 22 3200  2) 在li4 之前插入一个工人,信息为:姓名:zhao6,年龄:24,工资3300  3) 删除wang5 的信息  4) 利用for 循
		第六题 5. (List)已知有一个Worker 类如下: public class Worker { private int age; private String name; private do ... 
- JAVA-集合作业-已知有十六支男子足球队参加2008 北京奥运会。写一个程序,把这16 支球队随机分为4 个组。采用List集合和随机数
		第二题 已知有十六支男子足球队参加2008 北京奥运会.写一个程序,把这16 支球队随机分为4 个组.采用List集合和随机数 2008 北京奥运会男足参赛国家: 科特迪瓦,阿根廷,澳大利亚,塞尔维亚 ... 
- WCF 已知类型和泛型解析程序 KnownType
		数据协定继承 已知类型和泛型解析程序 Juval Lowy 下载代码示例 自首次发布以来,Windows Communication Foundation (WCF) 开发人员便必须处理数据协定继承方 ... 
- 【编程题目】n 支队伍比赛,分别编号为 0,1,2。。。。n-1,已知它们之间的实力对比关系,
		36.引用自网友:longzuo(运算)谷歌笔试: 19n 支队伍比赛,分别编号为 0,1,2....n-1,已知它们之间的实力对比关系,存储在一个二维数组 w[n][n]中,w[i][j] 的值代表 ... 
- 已知树的前序、中序,求后序的java实现&已知树的后序、中序,求前序的java实现
		public class Order { int findPosInInOrder(String str,String in,int position){ char c = str.charAt(po ... 
- DS Tree 已知后序、中序 => 建树 => 求先序
		注意点: 和上一篇的DS Tree 已知先序.中序 => 建树 => 求后序差不多,注意的地方是在aftorder中找根节点的时候,是从右往左找,因此递归的时候注意参数,最好是拿纸和笔模拟 ... 
随机推荐
- 记录--从AI到美颜全流程讲解
			这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 美颜和短视频 美颜相关APP可以说是现在手机上的必备的软件,例如抖音,快手,拍出的"照骗"和视频不加美颜效果,估计没有 ... 
- pandas 自动化处理Excel数据
			需求: 如下一份这样的Excel数据 现在需要把学生的学号.姓名分离出来到单独的一列 ,将 测验.讨论.成绩三列转换成数值,并把讨论这列的"-"转换成 0 显示 最后把处理好的内 ... 
- 慎用django orm的update_or_create方法
			公司一个线上招聘项目,后端采用Django开发,数据库使用MySQL.最近一次线上招聘会活动,因短时间大量用户涌入,被吐槽服务响应时间过长.后端和运维人员经排查,定位到MySQL数据库有死锁 根据错误 ... 
- KingbaseES V8R3集群运维案例之---流复制异步同步及全同步模式配置
			案例说明: 通过案例描述KingbaseES V8R3集群异步.同步及全同步强一致性配置,本案例为一主二备的架构. 适用版本: KingbaseES V8R3 集群架构: 集群复制配置参数说明: 1) ... 
- 内核模块的makefile规则详解
			内核中的Makefile 这里主要对内核源码各子集目录中的Kbulid(内核的编译系统)Makfile进行简单介绍 这部分式内核驱动或设备驱动开发这最长接触到的 Makefile 的语法包括几个方面 ... 
- mybatis一次执行多条SQL语句报错
			如果这样来写一个 mapper 1 <update id="createTable3" parameterType="map"> 2 drop ta ... 
- 简单的Git/GitHub
			什么是Git/GitHub Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目. 版本控制(Revision control)是一种在开发的过程中用于管理我们对文件.目录或工 ... 
- 5W1H聊开源之Who和How——谁、如何参与开源?
			上次Who的主体是谁"发明"了开源,这一次主体转换,来看看开源发明之后,还有哪些人为开源做贡献?作为普通程序员的我们,又能以怎样的形式参与到开源项目中? 很多人都以为参与开源是一件 ... 
- 17 JavaScript 中的call和apply
			17 JavaScript 中的call和apply 对于咱们逆向工程师而言. 并不需要深入的理解call和apply的本质作用. 只需要知道这玩意执行起来的逻辑顺序是什么即可 在运行时. 正常的js ... 
- 12 JavaScript 关于eval函数
			12 eval函数 eval本身在js里面正常情况下使用的并不多. 但是很多网站会利用eval的特性来完成反爬操作. 我们来看看eval是个什么鬼? 从功能上讲, eval非常简单. 它和python ... 
