WPF输入框Paste时出错,IDataObject的GetData抛出OutOfMemoryException
Posted muzizongheng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF输入框Paste时出错,IDataObject的GetData抛出OutOfMemoryException相关的知识,希望对你有一定的参考价值。
现象:客户在使用过程中,在输入框粘贴时,程序崩溃
分析:
从dump和log看到是:
Insufficient memory to continue the execution of the program.
at System.Runtime.InteropServices.ComTypes.IDataObject.GetData(FORMATETC& format, STGMEDIUM& medium)
at System.Windows.DataObject.OleConverter.GetDataInner(FORMATETC& formatetc, STGMEDIUM& medium)
at System.Windows.DataObject.OleConverter.GetDataFromOleHGLOBAL(String format, DVASPECT aspect, Int32 index)
at System.Windows.DataObject.OleConverter.GetDataFromBoundOleDataObject(String format, DVASPECT aspect, Int32 index)
at System.Windows.DataObject.OleConverter.GetData(String format, Boole 1448086091814
at System.Windows.DataObject.OleConverter.GetDataInner(FORMATETC& formatetc, STGMEDIUM& medium)
at System.Windows.DataObject.OleConverter.GetDataFromOleHGLOBAL(String format, DVASPECT aspect, Int32 index)
at System.Windows.DataObject.OleConverter.GetDataFromBoundOleDataObject(String format, DVASPECT aspect, Int32 index)
at System.Windows.DataObject.OleConverter.GetData(String format, Boole 1448086091814
调查:
我们的TextBox实现了Behavior,其中需要对粘贴的东西进行校验, 因此注册了Paste事件
OnClipboardPaste方法中获取剪贴板内容如下
DataObject.AddPastingHandler (this. AssociatedObject, OnClipboardPaste );
if ( e.SourceDataObject .GetDataPresent( DataFormats.UnicodeText , false))
{
text = e .SourceDataObject. GetData(DataFormats .UnicodeText) as string ;
}
查看各种资料,有人说是SetData的类没有加[Serializable]标记, 如:http://stackoverflow.com/questions/6999142/wpf-insufficient-memory-when-doing-copy-paste-vs-drag-drop-with-view-model-dat
但我们用的就是普通的TextBox,没有添加特性,只是把剪贴板的内容获取到转为字符串。 看来只能从别的地方着手。
刚才在Excel表中测试了下, 如果把excel中的某个表头Copy后, Paste到TextBox,报出同样的错误。
下面是相关源码:
[SecurityCritical] void System.Runtime.InteropServices.ComTypes.IDataObject.GetData(ref FORMATETC formatetc, out STGMEDIUM medium) { if (this ._innerData is DataObject.OleConverter) { ((DataObject.OleConverter) this._innerData).OleDataObject.GetData(ref formatetc, out medium); } else { int num = -2147221399; medium = new STGMEDIUM(); if (this .GetTymedUseable(formatetc.tymed)) { if ((formatetc.tymed & TYMED.TYMED_HGLOBAL) != TYMED.TYMED_NULL) { medium.tymed = TYMED.TYMED_HGLOBAL; medium.unionmember = DataObject.Win32GlobalAlloc(8258, (IntPtr) 1); num = this.OleGetDataUnrestricted(ref formatetc, ref medium, false); if (MS.Win32.NativeMethods.Failed(num)) DataObject.Win32GlobalFree( new HandleRef((object ) this, medium.unionmember)); } else if ((formatetc.tymed & TYMED.TYMED_ISTREAM) != TYMED.TYMED_NULL) { if (SecurityHelper.CheckUnmanagedCodePermission()) { medium.tymed = TYMED.TYMED_ISTREAM; System.Runtime.InteropServices.ComTypes.IStream istream = (System.Runtime.InteropServices.ComTypes.IStream) null ; num = DataObject.Win32CreateStreamOnHGlobal(IntPtr.Zero, true, ref istream); if (MS.Win32.NativeMethods.Succeeded(num)) { medium.unionmember = Marshal.GetComInterfaceForObject(( object) istream, typeof (System.Runtime.InteropServices.ComTypes.IStream)); Marshal.ReleaseComObject(( object) istream); num = this.OleGetDataUnrestricted(ref formatetc, ref medium, false); if (MS.Win32.NativeMethods.Failed(num)) Marshal.Release(medium.unionmember); } } else num = -2147467259; } else { medium.tymed = formatetc.tymed; num = this.OleGetDataUnrestricted(ref formatetc, ref medium, false); } } if (!MS.Win32.NativeMethods.Failed(num)) return; medium.unionmember = IntPtr.Zero; Marshal.ThrowExceptionForHR(num); } } [SecurityCritical] private void GetDataInner(ref FORMATETC formatetc, out STGMEDIUM medium) { new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); try { this._innerData.GetData(ref formatetc, out medium); } finally { CodeAccessPermission.RevertAssert(); } } [SecurityCritical] private object GetDataFromOleHGLOBAL(string format, DVASPECT aspect, int index) { FORMATETC formatetc = new FORMATETC(); formatetc.cfFormat = ( short) DataFormats.GetDataFormat(format).Id; formatetc.dwAspect = aspect; formatetc.lindex = index; formatetc.tymed = TYMED.TYMED_HGLOBAL; object obj = (object ) null; if (this .QueryGetDataInner(ref formatetc) == 0) { STGMEDIUM medium; this.GetDataInner(ref formatetc, out medium); try { if (medium.unionmember != IntPtr.Zero) { if (medium.tymed == TYMED.TYMED_HGLOBAL) obj = this.GetDataFromHGLOBAL(format, medium.unionmember); } } finally { MS.Win32.UnsafeNativeMethods.ReleaseStgMedium( ref medium); } } return obj; } [SecurityCritical] private int OleGetDataUnrestricted(ref FORMATETC formatetc, ref STGMEDIUM medium, bool doNotReallocate) { if (!(this ._innerData is DataObject.OleConverter)) return this .GetDataIntoOleStructs(ref formatetc, ref medium, doNotReallocate); ((DataObject.OleConverter) this._innerData).OleDataObject.GetDataHere(ref formatetc, ref medium); return 0; } [SecurityCritical] private int GetDataIntoOleStructs(ref FORMATETC formatetc, ref STGMEDIUM medium, bool doNotReallocate) { int num = -2147221399; if (this .GetTymedUseable(formatetc.tymed) && this.GetTymedUseable(medium.tymed)) { string name = DataFormats.GetDataFormat((int) formatetc.cfFormat).Name; num = -2147221404; if (this .GetDataPresent(name)) { object data = this .GetData(name); num = -2147221399; if ((formatetc.tymed & TYMED.TYMED_HGLOBAL) != TYMED.TYMED_NULL) num = this.GetDataIntoOleStructsByTypeMedimHGlobal(name, data, ref medium, doNotReallocate); else if ((formatetc.tymed & TYMED.TYMED_GDI) != TYMED.TYMED_NULL) num = this.GetDataIntoOleStructsByTypeMediumGDI(name, data, ref medium); else if ((formatetc.tymed & TYMED.TYMED_ENHMF) != TYMED.TYMED_NULL) num = this.GetDataIntoOleStructsByTypeMediumEnhancedMetaFile(name, data, ref medium); else if ((formatetc.tymed & TYMED.TYMED_ISTREAM) != TYMED.TYMED_NULL) num = this.GetDataIntoOleStructsByTypeMedimIStream(name, data, ref medium); } } return num; } [SecurityCritical] private int GetDataIntoOleStructsByTypeMedimHGlobal(string format, object data, ref STGMEDIUM medium, bool doNotReallocate) { int num; if (data is Stream) num = this.SaveStreamToHandle(medium.unionmember, (Stream) data, doNotReallocate); else if (DataObject.IsFormatEqual(format, DataFormats.html) || DataObject.IsFormatEqual(format, DataFormats.Xaml)) num = this.SaveStringToHandleAsUtf8(medium.unionmember, data.ToString(), doNotReallocate); else if (DataObject.IsFormatEqual(format, DataFormats.Text) || DataObject.IsFormatEqual(format, DataFormats.Rtf) || (DataObject.IsFormatEqual(format, DataFormats.OemText) || DataObject.IsFormatEqual(format, DataFormats.CommaSeparatedValue))) num = this.SaveStringToHandle(medium.unionmember, data.ToString(), false, doNotReallocate); else if (DataObject.IsFormatEqual(format, DataFormats.UnicodeText) || DataObject.IsFormatEqual(format, DataFormats.ApplicationTrust)) num = this.SaveStringToHandle(medium.unionmember, data.ToString(), true, doNotReallocate); else if (DataObject.IsFormatEqual(format, DataFormats.FileDrop)) num = this.SaveFileListToHandle(medium.unionmember, (string[]) data, doNotReallocate); else if (DataObject.IsFormatEqual(format, DataFormats.FileName)) { string[] strArray = (string []) data; num = this.SaveStringToHandle(medium.unionmember, strArray[0], false, doNotReallocate); } else if (DataObject.IsFormatEqual(format, DataFormats.FileNameW)) { string[] strArray = (string []) data; num = this.SaveStringToHandle(medium.unionmember, strArray[0], true, doNotReallocate); } else num = !DataObject.IsFormatEqual(format, DataFormats.Dib) || !SystemDrawingHelper.IsImage(data) ? (!DataObject.IsFormatEqual(format, typeof (BitmapSource).FullName) ? (!DataObject.IsFormatEqual(format, "System.Drawing.Bitmap" ) ? (DataObject.IsFormatEqual(format, DataFormats.EnhancedMetafile) || SystemDrawingHelper.IsMetafile(data) ? -2147221399 : (DataObject.IsFormatEqual(format, DataFormats.Serializable) || data is ISerializable || data != null && data.GetType().IsSerializable ? this.SaveObjectToHandle(medium.unionmember, data, doNotReallocate) : -2147221399)) : this.SaveSystemDrawingBitmapToHandle(medium.unionmember, data, doNotReallocate)) : this.SaveSystemBitmapSourceToHandle(medium.unionmember, data, doNotReallocate)) : -2147221399; if (num == 0) medium.tymed = TYMED.TYMED_HGLOBAL; return num; } [SecurityCritical] private int SaveStringToHandle(IntPtr handle, string str, bool unicode, bool doNotReallocate) { if (handle == IntPtr.Zero) return -2147024809; if (unicode) { int minimumByteCount = str.Length * 2 + 2; int hr = this .EnsureMemoryCapacity(ref handle, minimumByteCount, doNotReallocate); if (MS.Win32.NativeMethods.Failed(hr)) return hr; IntPtr pdst = DataObject.Win32GlobalLock( new HandleRef((object ) this, handle)); try { char[] psrc = str.ToCharArray(0, str.Length); MS.Win32.UnsafeNativeMethods.CopyMemoryW(pdst, psrc, psrc.Length * 2); Marshal.Copy( new char [1], 0, (IntPtr) ((long) pdst + ( long) psrc.Length * 2L), 1); } finally { DataObject.Win32GlobalUnlock( new HandleRef((object ) this, handle)); } } else { int cb = str.Length <= 0 ? 0 : DataObject.Win32WideCharToMultiByte(str, str.Length, (byte[]) null, 0); byte[] numArray = new byte[cb]; if (cb > 0) DataObject.Win32WideCharToMultiByte(str, str.Length, numArray, numArray.Length); int hr = this .EnsureMemoryCapacity(ref handle, cb + 1, doNotReallocate); if (MS.Win32.NativeMethods.Failed(hr)) return hr; IntPtr pdst = DataObject.Win32GlobalLock( new HandleRef((object ) this, handle)); try { MS.Win32.UnsafeNativeMethods.CopyMemory(pdst, numArray, cb); Marshal.Copy( new byte [1], 0, (IntPtr) ((long) pdst + ( long) cb), 1); } finally { DataObject.Win32GlobalUnlock( new HandleRef((object ) this, handle)); } } return 0; } [SecurityCritical] private int EnsureMemoryCapacity(ref IntPtr handle, int minimumByteCount, bool doNotReallocate) { int num = 0; if (doNotReallocate) { if (MS.Win32.NativeMethods.IntPtrToInt32(DataObject.Win32GlobalSize(new HandleRef(( object) this , handle))) < minimumByteCount) { handle = IntPtr.Zero; num = -2147286928; } } else { handle = DataObject.Win32GlobalReAlloc( new HandleRef((object ) this, handle), (IntPtr) minimumByteCount, 8258); if (handle == IntPtr.Zero) num = -2147024882; } return num; } [SecurityCritical] internal static IntPtr Win32GlobalReAlloc(HandleRef handle, IntPtr bytes, int flags) { IntPtr num = MS.Win32.UnsafeNativeMethods.GlobalReAlloc(handle, bytes, flags); int lastWin32Error = Marshal.GetLastWin32Error(); if (num == IntPtr.Zero) throw new Win32Exception(lastWin32Error); return num; }
结论:
源码中很清晰的看出
1. 执行DataObject.Win32GlobalReAlloc失败,抛出了 -2147024882即内存不足的异常。
2.而它失败的原因就是minimumByteCount太长,minimumByteCount是data.ToString()转换后的长度计算出来的
SaveStringToHandle(medium.unionmember, data.ToString(), true, doNotReallocate);
int minimumByteCount = str.Length * 2 + 2;
3.剪贴板中的data这个object转为string的length太长,导致申请堆失败,抛出OutOfMemoryException
(win32中用剪贴板复制一般都会用到GlobalAlloc,SetClipboardData,GlobalFree等api,有兴趣的自己去了解。)
解决方法:
从源码中看,目前只能在调用GetData方法时try-catch,捕获OutOfMemoryException异常。
我在PresentationFramework.dll中的System.Windows.Documents.TextEditorCopyPaste中发现微软也是这样处理的, 如下:
参考:
以上是关于WPF输入框Paste时出错,IDataObject的GetData抛出OutOfMemoryException的主要内容,如果未能解决你的问题,请参考以下文章