使用 gdi32.dll DeleteObject 进行 Shell 样式拖放对象处理

发布于 2025-01-03 18:21:25 字数 14260 浏览 2 评论 0原文

所以...我最近一直在 .NET 2.0 中开发一个 Winforms C# 应用程序,该应用程序使用此处精彩教程中描述的 Shell 样式拖放操作: http://blogs.msdn.com/b/adamroot/archive/2008/02/19/shell-style-drag-and-drop-in-net-wpf-and-winforms.aspx< /a>

这用于在拖动操作期间提供所拖动控件的半透明图像。然而,第二个功能是可以将文件拖放到此面板上,并根据文件执行一些操作。

自定义控件必须能够从一个流布局面板拖放到另一个流布局面板中(这一切都已经可以工作,这里没有问题)。 当我拖动第一个面板中的控件并将其放入同一面板中(即取消放置操作)时,就会出现问题。如果随后将文件拖到面板上,则该文件的图像将被前一个控件的图像替换,该控件被拖动但未放下(如果这有任何意义吗?)

我相信这是由图像没有得到正确处理,看来我是对的。我尝试使用 gdi32.dll DeleteObject 方法来处理创建的 HBitmap,但此方法始终返回 false。

下面的代码显示了如何设置 HBitmap 对象以及如何使用它来创建拖动效果。如果在调用 InitializeFromBitmap 之前调用 DisposeImage 方法,则图像将按预期进行处置。但如果在之后调用它,调用将返回 false,并且图像将保留在内存中。似乎有什么东西抓住了 HBitmap,不肯放手。

private ShDragImage m_DragImageInfo = new ShDragImage();
    public void SetDragImage( System.Runtime.InteropServices.ComTypes.IDataObject theDataObject, Control theControl, System.Drawing.Point theCursorPosition )
    {
        theCursorPosition = theControl.PointToClient( theCursorPosition );
        int Width = theControl.Width;
        int Height = theControl.Height;

        // Ensure that the bitmap is disposed to prevent a memory leak
        IntPtr HBitmap = IntPtr.Zero;
        using ( Bitmap ControlImage = new Bitmap( Width, Height ) )
        {
            theControl.DrawToBitmap( ControlImage, new Rectangle( 0, 0, Width, Height ) );
            HBitmap = ControlImage.GetHbitmap();
            SetDragImage( theDataObject, HBitmap, theCursorPosition, ControlImage.Size );
        }
    }

    private void SetDragImage( System.Runtime.InteropServices.ComTypes.IDataObject theDataObject, IntPtr theHImage, System.Drawing.Point theCursorPosition, Size theImageSize )
    {
        m_DragImageInfo = new ShDragImage();

        Win32Size ImageSize;
        ImageSize.m_Width = theImageSize.Width;
        ImageSize.m_Height = theImageSize.Height;
        m_DragImageInfo.m_DragImageSize = ImageSize;

        Win32Point CursorPosition;
        CursorPosition.m_X = theCursorPosition.X;
        CursorPosition.m_Y = theCursorPosition.Y;

        m_DragImageInfo.m_CursorOffset = CursorPosition;
        m_DragImageInfo.m_ColorKey = Color.Magenta.ToArgb();
        m_DragImageInfo.m_DragImageHBitmap = theHImage;

        try
        {
            IDragSourceHelper sourceHelper = (IDragSourceHelper)new CDragDropHelper();
            sourceHelper.InitializeFromBitmap( ref m_DragImageInfo, theDataObject );
        }
        catch ( NotImplementedException theException )
        {
            DisposeImage();
            throw theException;
        }
    }

    public void DisposeImage()
    {
        CExternalFunctions.DeleteObject( m_DragImageInfo.m_DragImageHBitmap );
    }

另外,这里是用于存储数据的类。据我所知,在处置时,数据是在 ClearStorage 方法中释放的。

[ComVisible( true )]
public class CDataObject : System.Runtime.InteropServices.ComTypes.IDataObject, IDisposable
{
    private Dictionary<FORMATETC, STGMEDIUM> m_Storage;
    private EventHandler m_Delegate;

    public CDataObject()
    {
        m_Storage = new Dictionary<FORMATETC, STGMEDIUM>();
    }

    private void ClearStorage()
    {
        foreach ( KeyValuePair<FORMATETC, STGMEDIUM> Pair in m_Storage )
        {
            STGMEDIUM Medium = Pair.Value;
            CExternalFunctions.ReleaseStgMedium( ref Medium );
        }
        m_Storage.Clear();
    }

    public void SetDelegate( EventHandler theDelegate )
    {
        m_Delegate = theDelegate;
    }

    public void Dispose()
    {
        Dispose( true );
    }

    private void Dispose( bool isDisposing )
    {
        if ( isDisposing )
        {
            ClearStorage();
        }
    }

    #region COM IDataObject Members

    #region COM constants

    private const int c_AdviseNotSupported = unchecked( (int)0x80040003 );

    private const int c_FormatEtc = unchecked( (int)0x80040064 );
    private const int c_TypeMismatch = unchecked( (int)0x80040069 );
    private const int c_WrongFormat = unchecked( (int)0x8004006A );

    #endregion // COM constants

    #region Unsupported functions

    public int DAdvise( ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection )
    {
        throw Marshal.GetExceptionForHR( c_AdviseNotSupported );
    }

    public void DUnadvise( int connection )
    {
        throw Marshal.GetExceptionForHR( c_AdviseNotSupported );
    }

    public int EnumDAdvise( out IEnumSTATDATA enumAdvise )
    {
        throw Marshal.GetExceptionForHR( c_AdviseNotSupported );
    }

    public int GetCanonicalFormatEtc( ref FORMATETC formatIn, out FORMATETC formatOut )
    {
        formatOut = formatIn;
        return c_FormatEtc;
    }

    public void GetDataHere( ref FORMATETC format, ref STGMEDIUM medium )
    {
        throw new NotSupportedException();
    }

    #endregion // Unsupported functions

    public IEnumFORMATETC EnumFormatEtc( DATADIR theDirection )
    {
        EnumFORMATETC EnumFormat = null;
        // We only support GET
        if ( theDirection == DATADIR.DATADIR_GET )
        {
            EnumFormat = new EnumFORMATETC( m_Storage );
        }
        else
        {
            throw new NotImplementedException( "EnumFormatEtc method is not implemented" );
        }

        return EnumFormat;
    }

    public void GetData( ref FORMATETC theFormat, out STGMEDIUM theMedium )
    {
        theMedium = new STGMEDIUM();
        foreach ( KeyValuePair<FORMATETC, STGMEDIUM> Pair in m_Storage )
        {
            if ( ( Pair.Key.tymed & theFormat.tymed ) > 0
                && Pair.Key.dwAspect == theFormat.dwAspect
                && Pair.Key.cfFormat == theFormat.cfFormat )
            {
                STGMEDIUM Medium = Pair.Value;
                theMedium = CopyMedium( ref Medium );
                break;
            }
        }
    }

    public int QueryGetData( ref FORMATETC format )
    {
        int ReturnValue;

        ReturnValue = c_TypeMismatch;

        // Try to locate the data
        // TODO: The ret, if not S_OK, is only relevant to the last item
        foreach ( FORMATETC FormatEtc in m_Storage.Keys )
        {
            if ( ( FormatEtc.tymed & format.tymed ) > 0 )
            {
                if ( FormatEtc.cfFormat == format.cfFormat )
                {
                    // Found it, return S_OK;
                    ReturnValue = 0;
                    break;
                }
                else
                {
                    // Found the medium type, but wrong format
                    ReturnValue = c_WrongFormat;
                }
            }
            else
            {
                // Mismatch on medium type
                ReturnValue = c_TypeMismatch;
            }
        }

        return ReturnValue;
    }

    public void SetData( ref FORMATETC theFormatIn, ref STGMEDIUM theMedium, bool theRelease )
    {
        // If the format exists in our storage, remove it prior to resetting it
        foreach ( FORMATETC FormatEtc in m_Storage.Keys )
        {
            if ( ( FormatEtc.tymed & theFormatIn.tymed ) > 0
                && FormatEtc.dwAspect == theFormatIn.dwAspect
                && FormatEtc.cfFormat == theFormatIn.cfFormat )
            {
                m_Storage.Remove( FormatEtc );
                break;
            }
        }

        // If release is true, we'll take ownership of the medium.
        // If not, we'll make a copy of it.
        STGMEDIUM Medium = theMedium;
        if ( !theRelease )
        {
            Medium = CopyMedium( ref theMedium );
        }

        m_Delegate( this, new EventArgs() );

        m_Storage.Add( theFormatIn, Medium );
    }

    public void SetDataEx( string theFormat, object theData )
    {
        DataFormats.Format DataFormat = DataFormats.GetFormat( theFormat );

        // Initialize the format structure
        FORMATETC FormatETC = new FORMATETC();
        FormatETC.cfFormat = (short)DataFormat.Id;
        FormatETC.dwAspect = DVASPECT.DVASPECT_CONTENT;
        FormatETC.lindex = -1;
        FormatETC.ptd = IntPtr.Zero;

        // Try to discover the TYMED from the format and data
        TYMED Tymed = TYMED.TYMED_HGLOBAL;
        // If a TYMED was found, we can use the system DataObject
        // to convert our value for us.
        FormatETC.tymed = Tymed;

        // Set data on an empty DataObject instance
        DataObject DataObject = new DataObject();
        DataObject.SetData( theFormat, true, theData );

        // Now retrieve the data, using the COM interface.
        // This will perform a managed to unmanaged conversion for us.
        STGMEDIUM Medium;
        ( (System.Runtime.InteropServices.ComTypes.IDataObject)DataObject ).GetData( ref FormatETC, out Medium );
        try
        {
            // Now set the data on our data object
            SetData( ref FormatETC, ref Medium, true );
        }
        catch( Exception theException )
        {
            // Ensure the Medium is released if there are any problems
            CExternalFunctions.ReleaseStgMedium( ref Medium );
            throw theException;
        }
    }

    private STGMEDIUM CopyMedium( ref STGMEDIUM theMedium )
    {
        STGMEDIUM Medium = new STGMEDIUM();
        int Return = CExternalFunctions.CopyStgMedium( ref theMedium, ref Medium );
        if ( Return != 0 )
        {
            // If the copy operation fails, throw an exception using the HRESULT
            throw Marshal.GetExceptionForHR( Return );
        }

        return Medium;
    }

    #endregion

    [ComVisible( true )]
    private class EnumFORMATETC : IEnumFORMATETC
    {
        // Keep an array of the formats for enumeration
        private FORMATETC[] m_Formats;
        // The index of the next item
        private int m_CurrentIndex = 0;

        private const int c_OK = 0;
        private const int c_Failed = 1;

        internal EnumFORMATETC( Dictionary<FORMATETC, STGMEDIUM> storage )
        {
            // Get the formats from the list
            m_Formats = new FORMATETC[ storage.Count ];
            int Index = 0;
            foreach ( FORMATETC FormatEtc in storage.Keys )
            {
                m_Formats[ Index ] = FormatEtc;
                Index++;
            }
        }

        private EnumFORMATETC( FORMATETC[] theFormats )
        {
            // Get the formats as a copy of the array
            m_Formats = new FORMATETC[ theFormats.Length ];
            theFormats.CopyTo( this.m_Formats, 0 );
        }

        #region IEnumFORMATETC Members

        public void Clone( out IEnumFORMATETC theEnum )
        {
            EnumFORMATETC ReturnEnum = new EnumFORMATETC( m_Formats );
            ReturnEnum.m_CurrentIndex = m_CurrentIndex;
            theEnum = ReturnEnum;
        }

        public int Next( int theNumberOfElements, FORMATETC[] theRequestedFormats, int[] theNumberOfRequests )
        {
            // Start with zero fetched, in case we return early
            if ( theNumberOfRequests != null && theNumberOfRequests.Length > 0 )
            {
                theNumberOfRequests[ 0 ] = 0;
            }

            // This will count down as we fetch elements
            int ReturnCount = theNumberOfElements;

            int ReturnValue = c_OK;

            // Short circuit if they didn't request any elements, or didn't
            // provide room in the return array, or there are not more elements
            // to enumerate.
            if ( theNumberOfElements <= 0 || theRequestedFormats == null || m_CurrentIndex >= m_Formats.Length )
            {
                ReturnValue = c_Failed;
            }

            // If the number of requested elements is not one, then we must
            // be able to tell the caller how many elements were fetched.
            if ( ( theNumberOfRequests == null || theNumberOfRequests.Length < 1 ) && theNumberOfElements != 1 )
            {
                ReturnValue = c_Failed;
            }

            // If the number of elements in the return array is too small, we
            // throw. This is not a likely scenario, hence the exception.
            if ( theRequestedFormats.Length < theNumberOfElements )
            {
                throw new ArgumentException( "The number of elements in the return array is less than the number of elements requested" );
            }

            // Fetch the elements.
            for ( int i = 0; m_CurrentIndex < m_Formats.Length && ReturnCount > 0; i++ )
            {
                theRequestedFormats[ i ] = m_Formats[ m_CurrentIndex ];
                ReturnCount--;
                m_CurrentIndex++;
            }

            // Return the number of elements fetched
            if ( theNumberOfRequests != null && theNumberOfRequests.Length > 0 )
            {
                theNumberOfRequests[ 0 ] = theNumberOfElements - ReturnCount;
            }

            if ( ReturnCount != 0 )
            {
                ReturnValue = c_Failed;
            }

            return ReturnValue;
        }

        public int Reset()
        {
            m_CurrentIndex = 0;
            return c_OK;
        }

        /// <summary>
        /// Skips the number of elements requested.
        /// </summary>
        /// <param name="celt">The number of elements to skip.</param>
        /// <returns>If there are not enough remaining elements to skip, returns S_FALSE. Otherwise, S_OK is returned.</returns>
        public int Skip( int theNumberOfElementsToSkip )
        {
            int Success = c_OK;
            if ( m_CurrentIndex + theNumberOfElementsToSkip > m_Formats.Length )
            {
                Success = c_Failed;
            }

            m_CurrentIndex += theNumberOfElementsToSkip;
            return Success;
        }

        #endregion
    }

有什么我错过的吗?有没有办法强制释放此 HBitmap,以便我可以正确处理它? 对此的任何帮助将不胜感激。

编辑: 在这个问题上获得任何形式的帮助真的会很棒。我已尝试确保 HDC 已释放,但这仍然导致对 DeleteObject 的调用返回 false。

So... I have recently been developing a Winforms C# application in .NET 2.0 that uses the Shell Style drag and drop described in the great tutorial here:
http://blogs.msdn.com/b/adamroot/archive/2008/02/19/shell-style-drag-and-drop-in-net-wpf-and-winforms.aspx

This was used to give a semi-transparent image of the dragged control during the drag action. However, a secondary functionality is that files can be dropped onto this panel, and some action carried out based on the file(s).

A custom control has to be able to be dragged from one flow layout panel and dropped into another (this all already works, no problems here).
My problem occurs when I drag the control in the first panel, and drop it into the same panel (i.e. cancelling the drop action). If a file is then dragged onto the panel, the image of the file will be replaced by the image of the former control, the one that was dragged but not dropped (If that makes any sense?)

I believed this to be caused by the Image not being disposed of properly, and it appears I was right. I am attempting to use the gdi32.dll DeleteObject method to dispose of the HBitmap created, but this method always returns false.

The code below shows how the HBitmap object is being set, and is being used to create the drag effect. If the DisposeImage method is called before the call to InitializeFromBitmap, the image will be disposed as expected. But if it is called after, the call will return false, and the image will remain in memory. It seems to be that something is holding onto the HBitmap and won't let go.

private ShDragImage m_DragImageInfo = new ShDragImage();
    public void SetDragImage( System.Runtime.InteropServices.ComTypes.IDataObject theDataObject, Control theControl, System.Drawing.Point theCursorPosition )
    {
        theCursorPosition = theControl.PointToClient( theCursorPosition );
        int Width = theControl.Width;
        int Height = theControl.Height;

        // Ensure that the bitmap is disposed to prevent a memory leak
        IntPtr HBitmap = IntPtr.Zero;
        using ( Bitmap ControlImage = new Bitmap( Width, Height ) )
        {
            theControl.DrawToBitmap( ControlImage, new Rectangle( 0, 0, Width, Height ) );
            HBitmap = ControlImage.GetHbitmap();
            SetDragImage( theDataObject, HBitmap, theCursorPosition, ControlImage.Size );
        }
    }

    private void SetDragImage( System.Runtime.InteropServices.ComTypes.IDataObject theDataObject, IntPtr theHImage, System.Drawing.Point theCursorPosition, Size theImageSize )
    {
        m_DragImageInfo = new ShDragImage();

        Win32Size ImageSize;
        ImageSize.m_Width = theImageSize.Width;
        ImageSize.m_Height = theImageSize.Height;
        m_DragImageInfo.m_DragImageSize = ImageSize;

        Win32Point CursorPosition;
        CursorPosition.m_X = theCursorPosition.X;
        CursorPosition.m_Y = theCursorPosition.Y;

        m_DragImageInfo.m_CursorOffset = CursorPosition;
        m_DragImageInfo.m_ColorKey = Color.Magenta.ToArgb();
        m_DragImageInfo.m_DragImageHBitmap = theHImage;

        try
        {
            IDragSourceHelper sourceHelper = (IDragSourceHelper)new CDragDropHelper();
            sourceHelper.InitializeFromBitmap( ref m_DragImageInfo, theDataObject );
        }
        catch ( NotImplementedException theException )
        {
            DisposeImage();
            throw theException;
        }
    }

    public void DisposeImage()
    {
        CExternalFunctions.DeleteObject( m_DragImageInfo.m_DragImageHBitmap );
    }

Also, here is the class that is being used to store the data. As far as I can see, on disposal the data is being released in the ClearStorage method.

[ComVisible( true )]
public class CDataObject : System.Runtime.InteropServices.ComTypes.IDataObject, IDisposable
{
    private Dictionary<FORMATETC, STGMEDIUM> m_Storage;
    private EventHandler m_Delegate;

    public CDataObject()
    {
        m_Storage = new Dictionary<FORMATETC, STGMEDIUM>();
    }

    private void ClearStorage()
    {
        foreach ( KeyValuePair<FORMATETC, STGMEDIUM> Pair in m_Storage )
        {
            STGMEDIUM Medium = Pair.Value;
            CExternalFunctions.ReleaseStgMedium( ref Medium );
        }
        m_Storage.Clear();
    }

    public void SetDelegate( EventHandler theDelegate )
    {
        m_Delegate = theDelegate;
    }

    public void Dispose()
    {
        Dispose( true );
    }

    private void Dispose( bool isDisposing )
    {
        if ( isDisposing )
        {
            ClearStorage();
        }
    }

    #region COM IDataObject Members

    #region COM constants

    private const int c_AdviseNotSupported = unchecked( (int)0x80040003 );

    private const int c_FormatEtc = unchecked( (int)0x80040064 );
    private const int c_TypeMismatch = unchecked( (int)0x80040069 );
    private const int c_WrongFormat = unchecked( (int)0x8004006A );

    #endregion // COM constants

    #region Unsupported functions

    public int DAdvise( ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection )
    {
        throw Marshal.GetExceptionForHR( c_AdviseNotSupported );
    }

    public void DUnadvise( int connection )
    {
        throw Marshal.GetExceptionForHR( c_AdviseNotSupported );
    }

    public int EnumDAdvise( out IEnumSTATDATA enumAdvise )
    {
        throw Marshal.GetExceptionForHR( c_AdviseNotSupported );
    }

    public int GetCanonicalFormatEtc( ref FORMATETC formatIn, out FORMATETC formatOut )
    {
        formatOut = formatIn;
        return c_FormatEtc;
    }

    public void GetDataHere( ref FORMATETC format, ref STGMEDIUM medium )
    {
        throw new NotSupportedException();
    }

    #endregion // Unsupported functions

    public IEnumFORMATETC EnumFormatEtc( DATADIR theDirection )
    {
        EnumFORMATETC EnumFormat = null;
        // We only support GET
        if ( theDirection == DATADIR.DATADIR_GET )
        {
            EnumFormat = new EnumFORMATETC( m_Storage );
        }
        else
        {
            throw new NotImplementedException( "EnumFormatEtc method is not implemented" );
        }

        return EnumFormat;
    }

    public void GetData( ref FORMATETC theFormat, out STGMEDIUM theMedium )
    {
        theMedium = new STGMEDIUM();
        foreach ( KeyValuePair<FORMATETC, STGMEDIUM> Pair in m_Storage )
        {
            if ( ( Pair.Key.tymed & theFormat.tymed ) > 0
                && Pair.Key.dwAspect == theFormat.dwAspect
                && Pair.Key.cfFormat == theFormat.cfFormat )
            {
                STGMEDIUM Medium = Pair.Value;
                theMedium = CopyMedium( ref Medium );
                break;
            }
        }
    }

    public int QueryGetData( ref FORMATETC format )
    {
        int ReturnValue;

        ReturnValue = c_TypeMismatch;

        // Try to locate the data
        // TODO: The ret, if not S_OK, is only relevant to the last item
        foreach ( FORMATETC FormatEtc in m_Storage.Keys )
        {
            if ( ( FormatEtc.tymed & format.tymed ) > 0 )
            {
                if ( FormatEtc.cfFormat == format.cfFormat )
                {
                    // Found it, return S_OK;
                    ReturnValue = 0;
                    break;
                }
                else
                {
                    // Found the medium type, but wrong format
                    ReturnValue = c_WrongFormat;
                }
            }
            else
            {
                // Mismatch on medium type
                ReturnValue = c_TypeMismatch;
            }
        }

        return ReturnValue;
    }

    public void SetData( ref FORMATETC theFormatIn, ref STGMEDIUM theMedium, bool theRelease )
    {
        // If the format exists in our storage, remove it prior to resetting it
        foreach ( FORMATETC FormatEtc in m_Storage.Keys )
        {
            if ( ( FormatEtc.tymed & theFormatIn.tymed ) > 0
                && FormatEtc.dwAspect == theFormatIn.dwAspect
                && FormatEtc.cfFormat == theFormatIn.cfFormat )
            {
                m_Storage.Remove( FormatEtc );
                break;
            }
        }

        // If release is true, we'll take ownership of the medium.
        // If not, we'll make a copy of it.
        STGMEDIUM Medium = theMedium;
        if ( !theRelease )
        {
            Medium = CopyMedium( ref theMedium );
        }

        m_Delegate( this, new EventArgs() );

        m_Storage.Add( theFormatIn, Medium );
    }

    public void SetDataEx( string theFormat, object theData )
    {
        DataFormats.Format DataFormat = DataFormats.GetFormat( theFormat );

        // Initialize the format structure
        FORMATETC FormatETC = new FORMATETC();
        FormatETC.cfFormat = (short)DataFormat.Id;
        FormatETC.dwAspect = DVASPECT.DVASPECT_CONTENT;
        FormatETC.lindex = -1;
        FormatETC.ptd = IntPtr.Zero;

        // Try to discover the TYMED from the format and data
        TYMED Tymed = TYMED.TYMED_HGLOBAL;
        // If a TYMED was found, we can use the system DataObject
        // to convert our value for us.
        FormatETC.tymed = Tymed;

        // Set data on an empty DataObject instance
        DataObject DataObject = new DataObject();
        DataObject.SetData( theFormat, true, theData );

        // Now retrieve the data, using the COM interface.
        // This will perform a managed to unmanaged conversion for us.
        STGMEDIUM Medium;
        ( (System.Runtime.InteropServices.ComTypes.IDataObject)DataObject ).GetData( ref FormatETC, out Medium );
        try
        {
            // Now set the data on our data object
            SetData( ref FormatETC, ref Medium, true );
        }
        catch( Exception theException )
        {
            // Ensure the Medium is released if there are any problems
            CExternalFunctions.ReleaseStgMedium( ref Medium );
            throw theException;
        }
    }

    private STGMEDIUM CopyMedium( ref STGMEDIUM theMedium )
    {
        STGMEDIUM Medium = new STGMEDIUM();
        int Return = CExternalFunctions.CopyStgMedium( ref theMedium, ref Medium );
        if ( Return != 0 )
        {
            // If the copy operation fails, throw an exception using the HRESULT
            throw Marshal.GetExceptionForHR( Return );
        }

        return Medium;
    }

    #endregion

    [ComVisible( true )]
    private class EnumFORMATETC : IEnumFORMATETC
    {
        // Keep an array of the formats for enumeration
        private FORMATETC[] m_Formats;
        // The index of the next item
        private int m_CurrentIndex = 0;

        private const int c_OK = 0;
        private const int c_Failed = 1;

        internal EnumFORMATETC( Dictionary<FORMATETC, STGMEDIUM> storage )
        {
            // Get the formats from the list
            m_Formats = new FORMATETC[ storage.Count ];
            int Index = 0;
            foreach ( FORMATETC FormatEtc in storage.Keys )
            {
                m_Formats[ Index ] = FormatEtc;
                Index++;
            }
        }

        private EnumFORMATETC( FORMATETC[] theFormats )
        {
            // Get the formats as a copy of the array
            m_Formats = new FORMATETC[ theFormats.Length ];
            theFormats.CopyTo( this.m_Formats, 0 );
        }

        #region IEnumFORMATETC Members

        public void Clone( out IEnumFORMATETC theEnum )
        {
            EnumFORMATETC ReturnEnum = new EnumFORMATETC( m_Formats );
            ReturnEnum.m_CurrentIndex = m_CurrentIndex;
            theEnum = ReturnEnum;
        }

        public int Next( int theNumberOfElements, FORMATETC[] theRequestedFormats, int[] theNumberOfRequests )
        {
            // Start with zero fetched, in case we return early
            if ( theNumberOfRequests != null && theNumberOfRequests.Length > 0 )
            {
                theNumberOfRequests[ 0 ] = 0;
            }

            // This will count down as we fetch elements
            int ReturnCount = theNumberOfElements;

            int ReturnValue = c_OK;

            // Short circuit if they didn't request any elements, or didn't
            // provide room in the return array, or there are not more elements
            // to enumerate.
            if ( theNumberOfElements <= 0 || theRequestedFormats == null || m_CurrentIndex >= m_Formats.Length )
            {
                ReturnValue = c_Failed;
            }

            // If the number of requested elements is not one, then we must
            // be able to tell the caller how many elements were fetched.
            if ( ( theNumberOfRequests == null || theNumberOfRequests.Length < 1 ) && theNumberOfElements != 1 )
            {
                ReturnValue = c_Failed;
            }

            // If the number of elements in the return array is too small, we
            // throw. This is not a likely scenario, hence the exception.
            if ( theRequestedFormats.Length < theNumberOfElements )
            {
                throw new ArgumentException( "The number of elements in the return array is less than the number of elements requested" );
            }

            // Fetch the elements.
            for ( int i = 0; m_CurrentIndex < m_Formats.Length && ReturnCount > 0; i++ )
            {
                theRequestedFormats[ i ] = m_Formats[ m_CurrentIndex ];
                ReturnCount--;
                m_CurrentIndex++;
            }

            // Return the number of elements fetched
            if ( theNumberOfRequests != null && theNumberOfRequests.Length > 0 )
            {
                theNumberOfRequests[ 0 ] = theNumberOfElements - ReturnCount;
            }

            if ( ReturnCount != 0 )
            {
                ReturnValue = c_Failed;
            }

            return ReturnValue;
        }

        public int Reset()
        {
            m_CurrentIndex = 0;
            return c_OK;
        }

        /// <summary>
        /// Skips the number of elements requested.
        /// </summary>
        /// <param name="celt">The number of elements to skip.</param>
        /// <returns>If there are not enough remaining elements to skip, returns S_FALSE. Otherwise, S_OK is returned.</returns>
        public int Skip( int theNumberOfElementsToSkip )
        {
            int Success = c_OK;
            if ( m_CurrentIndex + theNumberOfElementsToSkip > m_Formats.Length )
            {
                Success = c_Failed;
            }

            m_CurrentIndex += theNumberOfElementsToSkip;
            return Success;
        }

        #endregion
    }

Is there something I've missed? Is there a way to force this HBitmap to be released so that I can dispose of it properly?
Any help on this would be greatly appreciated.

Edit:
Really would be great to get any sort of assistance in this problem. I've tried ensuring that the HDC is released, and this still resulted in the call to DeleteObject returning false.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文