using System;
using System.Collections.Generic;
using System.Text;
using CPF.Input;
using System.Threading;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.IO;
using CPF.Drawing;
using System.Linq;

namespace CPF.Windows
{
    internal class ClipboardImpl : IClipboard
    {
        static int htmlId;
        static int[] Format;
        static ClipboardImpl()
        {
            //CF_BITMAP 位图的句柄（HBITMAP）。
            //CF_DIB 包含BITMAPINFO结构和位图位的内存对象。

            htmlId = UnmanagedMethods.RegisterClipboardFormat(Html);
            Format = new int[] { (int)UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT, htmlId, (int)UnmanagedMethods.ClipboardFormat.CF_DIB, (int)UnmanagedMethods.ClipboardFormat.CF_HDROP, 0 };
        }
        private void OpenClipboard()
        {
            while (!UnmanagedMethods.OpenClipboard(IntPtr.Zero))
            {
                Thread.Sleep(100);
                //await Task.Delay(100);
            }
        }

        //public void SetText(string text)
        //{
        //    if (text == null)
        //    {
        //        throw new ArgumentNullException(nameof(text));
        //    }

        //    OpenClipboard();

        //    UnmanagedMethods.EmptyClipboard();

        //    try
        //    {
        //        var hGlobal = Marshal.StringToHGlobalUni(text);
        //        UnmanagedMethods.SetClipboardData(UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT, hGlobal);
        //    }
        //    finally
        //    {
        //        UnmanagedMethods.CloseClipboard();
        //    }
        //}

        public void Clear()
        {
            OpenClipboard();
            try
            {
                UnmanagedMethods.EmptyClipboard();
            }
            finally
            {
                UnmanagedMethods.CloseClipboard();
            }
        }

        //List<IntPtr> intPtrs = new List<IntPtr>();

        public void SetData(params ValueTuple<DataFormat, object>[] data)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            //foreach (var item in intPtrs)
            //{
            //    Marshal.FreeHGlobal(item);
            //}
            OpenClipboard();

            UnmanagedMethods.EmptyClipboard();

            try
            {
                IntPtr hGlobal = IntPtr.Zero;
                IntPtr ptr = IntPtr.Zero;
                foreach (var item in data)
                {
                    switch (item.Item1)
                    {
                        case DataFormat.Text:
                            hGlobal = Marshal.StringToHGlobalUni(item.Item2.ToString());
                            UnmanagedMethods.SetClipboardData(UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT, hGlobal);
                            break;
                        case DataFormat.Html:
                            var html = item.Item2.ToString();
                            Encoding enc = Encoding.UTF8;

                            string begin = "Version:0.9\r\nStartHTML:{0:000000}\r\nEndHTML:{1:000000}"
                                           + "\r\nStartFragment:{2:000000}\r\nEndFragment:{3:000000}\r\n";

                            string html_begin = "<html>\r\n<head>\r\n"
                                                + "<meta http-equiv=\"Content-Type\""
                                                + " content=\"text/html; charset=" + enc.WebName + "\">\r\n"
                                                + "<title>HTML clipboard</title>\r\n</head>\r\n<body>\r\n"
                                                + "<!--StartFragment-->";

                            string html_end = "<!--EndFragment-->\r\n</body>\r\n</html>\r\n";

                            string begin_sample = String.Format(begin, 0, 0, 0, 0);

                            int count_begin = enc.GetByteCount(begin_sample);
                            int count_html_begin = enc.GetByteCount(html_begin);
                            int count_html = enc.GetByteCount(html);
                            int count_html_end = enc.GetByteCount(html_end);

                            string html_total = String.Format(
                                begin
                                , count_begin
                                , count_begin + count_html_begin + count_html + count_html_end
                                , count_begin + count_html_begin
                                , count_begin + count_html_begin + count_html
                                                    ) + html_begin + html + html_end;
                            //var h = Marshal.StringToCoTaskMemUTF8(html_total);
                            var l = enc.GetByteCount(html_total);
                            var h = Marshal.AllocHGlobal(l);
                            //intPtrs.Add(h);
                            Marshal.Copy(enc.GetBytes(html_total), 0, h, l);
                            UnmanagedMethods.SetClipboardData(htmlId, h);
                            //Marshal.FreeHGlobal(h);
                            break;
                        case DataFormat.Image:
                            var img = item.Item2 as Image;
                            IntPtr screenDC = UnmanagedMethods.GetDC(IntPtr.Zero);
                            IntPtr memDc = UnmanagedMethods.CreateCompatibleDC(screenDC);
                            UnmanagedMethods.BITMAPINFOHEADER info = new UnmanagedMethods.BITMAPINFOHEADER();
                            info.biSize = (uint)Marshal.SizeOf(typeof(UnmanagedMethods.BITMAPINFOHEADER));
                            info.biBitCount = 24;
                            info.biHeight = img.Height;
                            info.biWidth = img.Width;
                            info.biPlanes = 1;
                            info.biSizeImage = (uint)(img.Width * img.Height * 3);
                            var dibHbitmap = UnmanagedMethods.CreateDIBSection(memDc, ref info, 0, out IntPtr ppvBits, IntPtr.Zero, 0);
                            var oldBits = UnmanagedMethods.SelectObject(memDc, dibHbitmap);

                            IntPtr hBitmap;
                            var stream = img.SaveToStream(ImageFormat.Png);
                            var states = UnmanagedMethods.GdipCreateBitmapFromStream(new GPStream(stream), out IntPtr bitmap);
                            stream.Dispose();
                            UnmanagedMethods.GdipCreateHBITMAPFromBitmap(bitmap, out hBitmap, UnmanagedMethods.ToWin32(Color.White));
                            UnmanagedMethods.GdipDisposeImage(bitmap);
                            UnmanagedMethods.SetClipboardData(UnmanagedMethods.ClipboardFormat.CF_BITMAP, hBitmap);


                            IntPtr sdc = UnmanagedMethods.CreateCompatibleDC(screenDC);
                            var sob = UnmanagedMethods.SelectObject(sdc, hBitmap);

                            UnmanagedMethods.BitBlt(memDc, 0, 0, img.Width, img.Height, sdc, 0, 0, TernaryRasterOperations.SRCCOPY);

                            var dib = UnmanagedMethods.GlobalAlloc(UnmanagedMethods.GMEM_MOVEABLE | UnmanagedMethods.GMEM_ZEROINIT, (int)info.biSize + (int)info.biSizeImage);
                            ptr = UnmanagedMethods.GlobalLock(dib);
                            Marshal.StructureToPtr(info, ptr, true);
                            var d = new byte[info.biSizeImage];
                            Marshal.Copy(ppvBits, d, 0, d.Length);
                            Marshal.Copy(d, 0, ptr + (int)info.biSize, d.Length);
                            UnmanagedMethods.SetClipboardData(UnmanagedMethods.ClipboardFormat.CF_DIB, dib);

                            UnmanagedMethods.GlobalUnlock(dib);
                            UnmanagedMethods.SelectObject(sdc, sob);
                            UnmanagedMethods.DeleteDC(sdc);
                            UnmanagedMethods.SelectObject(memDc, oldBits);
                            UnmanagedMethods.DeleteDC(memDc);
                            UnmanagedMethods.ReleaseDC(IntPtr.Zero, screenDC);
                            //UnmanagedMethods.DeleteObject(hBitmap);
                            break;
                        case DataFormat.FileNames:
                            var files = item.Item2 as IEnumerable<string>;
                            if (files != null)
                            {
                                char[] filesStr = (string.Join("\0", files) + "\0\0").ToCharArray();
                                _DROPFILES df = new _DROPFILES();
                                df.fWide = true;
#if Net4
                                df.pFiles = Marshal.SizeOf(typeof(_DROPFILES));
                                int required = (filesStr.Length * sizeof(char)) + Marshal.SizeOf(typeof(_DROPFILES));
#else
                                df.pFiles = Marshal.SizeOf<_DROPFILES>();
                                int required = (filesStr.Length * sizeof(char)) + Marshal.SizeOf<_DROPFILES>();
#endif
                                hGlobal = UnmanagedMethods.GlobalAlloc(UnmanagedMethods.GMEM_MOVEABLE | UnmanagedMethods.GMEM_ZEROINIT, required);

                                long available = UnmanagedMethods.GlobalSize(hGlobal).ToInt64();
                                if (required > available)
                                { break; }
                                ptr = UnmanagedMethods.GlobalLock(hGlobal);
                                try
                                {
                                    Marshal.StructureToPtr(df, ptr, false);
#if Net4
                                    Marshal.Copy(filesStr, 0, ptr + Marshal.SizeOf(typeof(_DROPFILES)), filesStr.Length);
#else
                                    Marshal.Copy(filesStr, 0, ptr + Marshal.SizeOf<_DROPFILES>(), filesStr.Length);
#endif
                                    UnmanagedMethods.SetClipboardData(UnmanagedMethods.ClipboardFormat.CF_HDROP, hGlobal);
                                }
                                finally
                                {
                                    UnmanagedMethods.GlobalUnlock(hGlobal);
                                }
                            }
                            break;
                    }
                }
            }
            finally
            {
                UnmanagedMethods.CloseClipboard();
            }
        }
        public bool Contains(DataFormat dataFormat)
        {
            OpenClipboard();
            uint LastRetrievedFormat = 0;
            List<uint> list = new List<uint>();
            while (0 != (LastRetrievedFormat = UnmanagedMethods.EnumClipboardFormats(LastRetrievedFormat)))
            {
                list.Add(LastRetrievedFormat);
            }

            UnmanagedMethods.CloseClipboard();
            //var f = Format[(int)dataFormat - 1];
            var f = GetFormatId(dataFormat);
            if (list.IndexOf((uint)f) < 0)
            {
                if (dataFormat == DataFormat.Text)
                {
                    if (list.IndexOf((uint)UnmanagedMethods.ClipboardFormat.CF_TEXT) >= 0)
                    {
                        return true;
                    }
                    if (list.IndexOf((uint)UnmanagedMethods.ClipboardFormat.CF_OEMTEXT) >= 0)
                    {
                        return true;
                    }
                }
                return false;
            }
            return true;
        }

        public static DataFormat GetFormat(int format)
        {
            ushort f = (ushort)(format & 0xFFFF);
            if (format == 2 || format == 8)
            {
                return DataFormat.Image;
            }
            for (int i = 0; i < Format.Length; i++)
            {
                if (Format[i] == f)
                {
                    if (i == 0)
                    {
                        return DataFormat.Text;
                    }
                    else if (i == 1)
                    {
                        return DataFormat.Html;
                    }
                    else if (i == 2)
                    {
                        return DataFormat.Image;
                    }
                    else if (i == 3)
                    {
                        return DataFormat.FileNames;
                    }
                }
            }
            if (format == (ushort)UnmanagedMethods.ClipboardFormat.CF_TEXT || format == (ushort)UnmanagedMethods.ClipboardFormat.CF_OEMTEXT)
            {
                return DataFormat.Text;
            }
            return DataFormat.Unknown;
        }

        public static int GetFormatId(DataFormat dataFormat)
        {
            //return Format[(int)dataFormat];
            switch (dataFormat)
            {
                case DataFormat.Text:
                    return Format[0];
                case DataFormat.Html:
                    return Format[1];
                case DataFormat.Image:
                    return Format[2];
                case DataFormat.FileNames:
                    return Format[3];
                default:
                    return 0;
            }
        }

        public static Image ImageFormHBitmap(IntPtr hBitmap)
        {
            var r = UnmanagedMethods.GdipCreateBitmapFromHBITMAP(hBitmap, IntPtr.Zero, out IntPtr b);
            if (r != 0)
            {
                return null;
            }
            MemoryStream ms = new MemoryStream();
            Guid png = new Guid("{b96b3caf-0728-11d3-9d7b-0000f81ef32e}");
            Guid jpg = new Guid("{b96b3cae-0728-11d3-9d7b-0000f81ef32e}");

            int numDecoders;

            int status = UnmanagedMethods.GdipGetImageDecodersSize(out numDecoders, out int size);

            if (status != 0)
            {
                return null;
            }

            IntPtr memory = Marshal.AllocHGlobal(size);

            status = UnmanagedMethods.GdipGetImageDecoders(numDecoders, size, memory);

            int index;
            List<ImageCodecInfoPrivate> codes = new List<ImageCodecInfoPrivate>();

            for (index = 0; index < numDecoders; index++)
            {
                IntPtr curcodec = (IntPtr)((long)memory + (int)Marshal.SizeOf(typeof(ImageCodecInfoPrivate)) * index);
                ImageCodecInfoPrivate codecp = new ImageCodecInfoPrivate();
                Marshal.PtrToStructure(curcodec, codecp);

                codes.Add(codecp);
            }

            var pngCode = codes.Find(a => a.FormatID == png);

            UnmanagedMethods.GdipSaveImageToStream(b, new GPStream(ms), ref pngCode.Clsid, IntPtr.Zero);
            ms.Position = 0;
            var img = new Bitmap(ms);
            UnmanagedMethods.GdipDisposeImage(b);
            //ms.Position = 0;
            //File.WriteAllBytes(Path.Combine(CPF.Platform.Application.StartupPath, "test.png"), ms.ToArray());
            ms.Dispose();
            return img;
        }

        public object GetData(DataFormat dataFormat)
        {
            //if (dataFormat != DataFormat.Image)
            {
                OpenClipboard();
                try
                {
                    int formatId = (int)UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT;
                    switch (dataFormat)
                    {
                        case DataFormat.Text:
                            uint LastRetrievedFormat = 0;
                            while (0 != (LastRetrievedFormat = UnmanagedMethods.EnumClipboardFormats(LastRetrievedFormat)))
                            {
                                if (LastRetrievedFormat == (uint)UnmanagedMethods.ClipboardFormat.CF_OEMTEXT)
                                {
                                    formatId = (int)UnmanagedMethods.ClipboardFormat.CF_OEMTEXT;
                                    break;
                                }
                                else if (LastRetrievedFormat == (uint)UnmanagedMethods.ClipboardFormat.CF_TEXT)
                                {
                                    formatId = (int)UnmanagedMethods.ClipboardFormat.CF_TEXT;
                                    break;
                                }
                            }
                            break;
                        case DataFormat.Html:
                            formatId = htmlId;
                            break;
                        case DataFormat.Image:
                            formatId = (int)UnmanagedMethods.ClipboardFormat.CF_DIB;
                            break;
                        case DataFormat.FileNames:
                            formatId = (int)UnmanagedMethods.ClipboardFormat.CF_HDROP;
                            break;
                    }
                    IntPtr hText = UnmanagedMethods.GetClipboardData(formatId);
                    if (hText == IntPtr.Zero)
                    {
                        return null;
                    }

                    string stringData = null;
                    int size;
                    IntPtr ptr = UnmanagedMethods.GlobalLock(hText);
                    try
                    {
                        switch (dataFormat)
                        {
                            case DataFormat.Text:
                                if (formatId == (int)UnmanagedMethods.ClipboardFormat.CF_TEXT)
                                {
                                    try
                                    {
                                        var rv = Marshal.PtrToStringAnsi(hText);//在Unity 里会报错
                                        return rv;
                                    }
                                    catch (Exception e)
                                    {
                                        System.Diagnostics.Debug.WriteLine(e);
                                        Console.WriteLine(e);
                                        var dwBufSize = (int)UnmanagedMethods.GlobalSize(hText);
                                        //var ss = new String((sbyte*)hText, 0, dwBufSize);
                                        byte[] cc = new byte[dwBufSize];
                                        Marshal.Copy(hText, cc, 0, (int)dwBufSize);
                                        string ss = Encoding.GetEncoding("GBK").GetString(cc).TrimEnd('\0');
                                        return ss;
                                    }
                                }
                                else
                                {
                                    var rv = Marshal.PtrToStringUni(hText);
                                    return rv;
                                }
                            case DataFormat.Html:
                                size = (int)UnmanagedMethods.GlobalSize(hText);
                                //if (unicode)
                                //{
                                //stringData = new string((char*)ptr);
                                //}
                                //else
                                //{
                                //    stringData = new string((sbyte*)ptr);
                                //}
                                byte[] bytes = new byte[size];
                                Marshal.Copy(ptr, bytes, 0, size);
                                stringData = Encoding.UTF8.GetString(bytes);
                                var start = stringData.IndexOf("<!--StartFragment");
                                var end = stringData.IndexOf("<!--EndFragment");
                                if (start == -1 || end == -1)
                                {
                                    var s = stringData.IndexOf("StartFragment:");
                                    var e = stringData.IndexOf("EndFragment:");
                                    if (s == -1 || end == -1)
                                    {

                                    }
                                    else
                                    {
                                        start = int.Parse(stringData.Substring(s + 14, 10));
                                        end = int.Parse(stringData.Substring(e + 14, 10));
                                        var by = Encoding.UTF8.GetBytes(stringData);
                                        stringData = Encoding.UTF8.GetString(by, start, end - start);
                                        stringData = stringData.Substring(start, end - start + 1);
                                    }
                                }
                                else
                                {
                                    stringData = stringData.Substring(start + 17, end - start - 17);
                                    stringData = stringData.TrimStart('-', ' ', '>');
                                }
                                return stringData;
                            case DataFormat.Image:
                                if (formatId == (int)UnmanagedMethods.ClipboardFormat.CF_BITMAP)
                                {
                                    var img = ImageFormHBitmap(hText);
                                    return img;
                                }
                                else
                                    unsafe
                                    {
#if Net4
                                        var bmp = (UnmanagedMethods.BITMAPINFOHEADER)Marshal.PtrToStructure(ptr, typeof(UnmanagedMethods.BITMAPINFOHEADER));
#else
                                        var bmp = Marshal.PtrToStructure<UnmanagedMethods.BITMAPINFOHEADER>(ptr);

#endif
                                        IntPtr screenDC = UnmanagedMethods.GetDC(IntPtr.Zero);
                                        IntPtr memDc = UnmanagedMethods.CreateCompatibleDC(screenDC);
                                        UnmanagedMethods.BITMAPINFOHEADER info = new UnmanagedMethods.BITMAPINFOHEADER();
                                        info.biSize = (uint)Marshal.SizeOf(typeof(UnmanagedMethods.BITMAPINFOHEADER));
                                        info.biBitCount = 32;
                                        info.biHeight = bmp.biHeight;
                                        info.biWidth = bmp.biWidth;
                                        info.biPlanes = 1;
                                        var hBitmap = UnmanagedMethods.CreateDIBSection(memDc, ref info, 0, out IntPtr ppvBits, IntPtr.Zero, 0);
                                        var oldBits = UnmanagedMethods.SelectObject(memDc, hBitmap);//将位图载入上下文
                                                                                                    //_ = UnmanagedMethods.GlobalLock(hText);
                                        _ = UnmanagedMethods.StretchDIBits(memDc, 0, 0, bmp.biWidth, Math.Abs(bmp.biHeight), 0, 0, bmp.biWidth, Math.Abs(bmp.biHeight), (ptr + sizeof(UnmanagedMethods.BITMAPINFOHEADER)), ref info, 0, (uint)TernaryRasterOperations.SRCCOPY);
                                        //sizeof(UnmanagedMethods.BITMAPFILEHEADER) +
                                        //var c = sizeof(UnmanagedMethods.BITMAPINFOHEADER);

                                        var img = ImageFormHBitmap(hBitmap);
                                        //var img = new Bitmap(bmp.biWidth, bmp.biHeight, bmp.biWidth * 4, PixelFormat.PRgba, ppvBits).Clone();

                                        UnmanagedMethods.SelectObject(memDc, oldBits);
                                        UnmanagedMethods.ReleaseDC(IntPtr.Zero, screenDC);
                                        UnmanagedMethods.DeleteDC(memDc);
                                        UnmanagedMethods.DeleteObject(hBitmap);
                                        return img;
                                    }
                            case DataFormat.FileNames:
                                string[] files = null;
                                StringBuilder sb = new StringBuilder(260);

                                int count = UnmanagedMethods.DragQueryFile(hText, unchecked((int)0xFFFFFFFF), null, 0);
                                if (count > 0)
                                {
                                    files = new string[count];


                                    for (int i = 0; i < count; i++)
                                    {
                                        int charlen = UnmanagedMethods.DragQueryFileLongPath(hText, i, sb);
                                        if (0 == charlen)
                                            continue;
                                        string s = sb.ToString(0, charlen);

                                        // SECREVIEW : do we really need to do this?
                                        //
                                        //string fullPath = Path.GetFullPath(s);
                                        //Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "FileIO(" + fullPath + ") Demanded");
                                        //new FileIOPermission(FileIOPermissionAccess.PathDiscovery, fullPath).Demand();
                                        files[i] = s;
                                    }
                                }

                                return files;
                            default:
                                return null;
                        }
                    }
                    finally
                    {
                        UnmanagedMethods.GlobalUnlock(hText);
                    }

                }
                finally
                {
                    UnmanagedMethods.CloseClipboard();
                }
            }
            //else
            //{
            //    IDataObject dataObject = null;
            //    int hr, retry = 10;
            //    do
            //    {
            //        hr = UnmanagedMethods.OleGetClipboard(ref dataObject);
            //        if (hr != 0)
            //        {
            //            if (retry == 0)
            //            {
            //                //ThrowIfFailed(hr);
            //                throw new Exception("读取剪贴板失败");
            //            }
            //            retry--;
            //            Thread.Sleep(100 /*ms*/);
            //        }
            //    }
            //    while (hr != 0);

            //    FORMATETC formatetc = new FORMATETC();
            //    STGMEDIUM medium = new STGMEDIUM();
            //    var tymed = TYMED.TYMED_GDI;
            //    formatetc.cfFormat = (short)UnmanagedMethods.ClipboardFormat.CF_BITMAP;
            //    formatetc.dwAspect = DVASPECT.DVASPECT_CONTENT;
            //    formatetc.lindex = -1;
            //    formatetc.tymed = tymed;
            //    medium.tymed = tymed;

            //    Object data = null;
            //    if (0 == dataObject.QueryGetData(ref formatetc))
            //    {
            //        try
            //        {
            //            //IntSecurity.UnmanagedCode.Assert();
            //            try
            //            {
            //                dataObject.GetData(ref formatetc, out medium);
            //            }
            //            finally
            //            {
            //                //CodeAccessPermission.RevertAssert();
            //            }

            //            if (medium.unionmember != IntPtr.Zero)
            //            {
            //                var r = UnmanagedMethods.GdipCreateBitmapFromHBITMAP(medium.unionmember, IntPtr.Zero, out IntPtr b);
            //                if (r != 0)
            //                {
            //                    return null;
            //                }
            //                //UnmanagedMethods.BITMAP BITMAP = new UnmanagedMethods.BITMAP();
            //                //UnmanagedMethods.GetObject(medium.unionmember, Marshal.SizeOf(typeof(UnmanagedMethods.BITMAP)), BITMAP);
            //                MemoryStream ms = new MemoryStream();
            //                Guid png = new Guid("{b96b3caf-0728-11d3-9d7b-0000f81ef32e}");

            //                int numDecoders;
            //                int size;

            //                int status = UnmanagedMethods.GdipGetImageDecodersSize(out numDecoders, out size);

            //                if (status != 0)
            //                {
            //                    return null;
            //                }

            //                IntPtr memory = Marshal.AllocHGlobal(size);

            //                status = UnmanagedMethods.GdipGetImageDecoders(numDecoders, size, memory);

            //                int index;
            //                List<ImageCodecInfoPrivate> codes = new List<ImageCodecInfoPrivate>();

            //                for (index = 0; index < numDecoders; index++)
            //                {
            //                    IntPtr curcodec = (IntPtr)((long)memory + (int)Marshal.SizeOf(typeof(ImageCodecInfoPrivate)) * index);
            //                    ImageCodecInfoPrivate codecp = new ImageCodecInfoPrivate();
            //                    Marshal.PtrToStructure(curcodec, codecp);

            //                    codes.Add(codecp);
            //                }

            //                var pngCode = codes.Find(a => a.FormatID == png);

            //                UnmanagedMethods.GdipSaveImageToStream(b, new ComStreamFromDataStream(ms), ref pngCode.Clsid, IntPtr.Zero);
            //                ms.Position = 0;
            //                var img = new CPF.Bitmap(ms);
            //                UnmanagedMethods.GdipDisposeImage(b);
            //                ms.Dispose();
            //                return img;
            //            }
            //        }
            //        catch
            //        {
            //        }
            //    }
            //    return data;
            //}

        }

        /// <para>Specifies the standard ANSI text format. This <see langword='static '/> 
        /// field is read-only.</para>
        /// </devdoc>
        public static readonly string Text = "Text";

        ///    <para>Specifies the standard Windows Unicode text format. This 
        ///    <see langword='static '/>
        ///    field is read-only.</para>
        /// </devdoc>
        public static readonly string UnicodeText = "UnicodeText";

        ///    <para>Specifies the Windows Device Independent Bitmap (DIB) 
        ///       format. This <see langword='static '/>
        ///       field is read-only.</para>
        /// </devdoc>
        public static readonly string Dib = "DeviceIndependentBitmap";

        /// <devdoc>
        /// <para>Specifies a Windows bitmap format. This <see langword='static '/> field is read-only.</para>
        /// </devdoc>
        public static readonly string Bitmap = "Bitmap";

        /// <devdoc>
        ///    <para>Specifies the Windows enhanced metafile format. This 
        ///    <see langword='static '/> field is read-only.</para>
        /// </devdoc>
        public static readonly string EnhancedMetafile = "EnhancedMetafile";

        /// <devdoc>
        ///    <para>Specifies the Windows metafile format, which Win Forms 
        ///       does not directly use. This <see langword='static '/>
        ///       field is read-only.</para>
        /// </devdoc>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] // Would be a breaking change to rename this
        public static readonly string MetafilePict = "MetaFilePict";

        /// <devdoc>
        ///    <para>Specifies the Windows symbolic link format, which Win 
        ///       Forms does not directly use. This <see langword='static '/>
        ///       field is read-only.</para>
        /// </devdoc>
        public static readonly string SymbolicLink = "SymbolicLink";

        /// <devdoc>
        ///    <para>Specifies the Windows data interchange format, which Win 
        ///       Forms does not directly use. This <see langword='static '/>
        ///       field is read-only.</para>
        /// </devdoc>
        public static readonly string Dif = "DataInterchangeFormat";

        /// <devdoc>
        ///    <para>Specifies the Tagged Image File Format (TIFF), which Win 
        ///       Forms does not directly use. This <see langword='static '/>
        ///       field is read-only.</para>
        /// </devdoc>
        public static readonly string Tiff = "TaggedImageFileFormat";

        /// <devdoc>
        ///    <para>Specifies the standard Windows original equipment 
        ///       manufacturer (OEM) text format. This <see langword='static '/> field is read-only.</para>
        /// </devdoc>
        public static readonly string OemText = "OEMText";
        /// <devdoc>
        /// <para>Specifies the Windows palette format. This <see langword='static '/> 
        /// field is read-only.</para>
        /// </devdoc>
        public static readonly string Palette = "Palette";

        /// <devdoc>
        ///    <para>Specifies the Windows pen data format, which consists of 
        ///       pen strokes for handwriting software; Win Forms does not use this format. This
        ///    <see langword='static '/> 
        ///    field is read-only.</para>
        /// </devdoc>
        public static readonly string PenData = "PenData";

        /// <devdoc>
        ///    <para>Specifies the Resource Interchange File Format (RIFF) 
        ///       audio format, which Win Forms does not directly use. This <see langword='static '/> field is read-only.</para>
        /// </devdoc>
        public static readonly string Riff = "RiffAudio";

        /// <devdoc>
        ///    <para>Specifies the wave audio format, which Win Forms does not 
        ///       directly use. This <see langword='static '/> field is read-only.</para>
        /// </devdoc>
        public static readonly string WaveAudio = "WaveAudio";

        /// <devdoc>
        ///    <para>Specifies the Windows file drop format, which Win Forms 
        ///       does not directly use. This <see langword='static '/>
        ///       field is read-only.</para>
        /// </devdoc>
        public static readonly string FileDrop = "FileDrop";

        /// <devdoc>
        ///    <para>Specifies the Windows culture format, which Win Forms does 
        ///       not directly use. This <see langword='static '/> field is read-only.</para>
        /// </devdoc>
        public static readonly string Locale = "Locale";

        /// <devdoc>
        ///    <para>Specifies text consisting of HTML data. This 
        ///    <see langword='static '/> field is read-only.</para>
        /// </devdoc>
        public static readonly string Html = "HTML Format";

        /// <devdoc>
        ///    <para>Specifies text consisting of Rich Text Format (RTF) data. This 
        ///    <see langword='static '/> field is read-only.</para>
        /// </devdoc>
        public static readonly string Rtf = "Rich Text Format";

        /// <devdoc>
        ///    <para>Specifies a comma-separated value (CSV) format, which is a 
        ///       common interchange format used by spreadsheets. This format is not used directly
        ///       by Win Forms. This <see langword='static '/>
        ///       field is read-only.</para>
        /// </devdoc>
        public static readonly string CommaSeparatedValue = "Csv";
    }

    //[ComImport()]
    //[Guid("0000010E-0000-0000-C000-000000000046")]
    //[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    //public interface IDataObject
    //{

    //    /// <devdoc>
    //    ///     Called by a data consumer to obtain data from a source data object. 
    //    ///     The GetData method renders the data described in the specified FORMATETC 
    //    ///     structure and transfers it through the specified STGMEDIUM structure. 
    //    ///     The caller then assumes responsibility for releasing the STGMEDIUM structure.
    //    /// </devdoc>
    //    void GetData([In] ref FORMATETC format, out STGMEDIUM medium);

    //    /// <devdoc>
    //    ///     Called by a data consumer to obtain data from a source data object. 
    //    ///     This method differs from the GetData method in that the caller must 
    //    ///     allocate and free the specified storage medium.
    //    /// </devdoc>
    //    void GetDataHere([In] ref FORMATETC format, ref STGMEDIUM medium);

    //    /// <devdoc>
    //    ///     Determines whether the data object is capable of rendering the data 
    //    ///     described in the FORMATETC structure. Objects attempting a paste or 
    //    ///     drop operation can call this method before calling IDataObject::GetData 
    //    ///     to get an indication of whether the operation may be successful.
    //    /// </devdoc>
    //    [PreserveSig]
    //    int QueryGetData([In] ref FORMATETC format);

    //    /// <devdoc>
    //    ///     Provides a standard FORMATETC structure that is logically equivalent to one that is more 
    //    ///     complex. You use this method to determine whether two different 
    //    ///     FORMATETC structures would return the same data, removing the need 
    //    ///     for duplicate rendering.
    //    /// </devdoc>
    //    [PreserveSig]
    //    int GetCanonicalFormatEtc([In] ref FORMATETC formatIn, out FORMATETC formatOut);

    //    /// <devdoc>
    //    ///     Called by an object containing a data source to transfer data to 
    //    ///     the object that implements this method.
    //    /// </devdoc>
    //    void SetData([In] ref FORMATETC formatIn, [In] ref STGMEDIUM medium, [MarshalAs(UnmanagedType.Bool)] bool release);

    //    /// <devdoc>
    //    ///     Creates an object for enumerating the FORMATETC structures for a 
    //    ///     data object. These structures are used in calls to IDataObject::GetData 
    //    ///     or IDataObject::SetData. 
    //    /// </devdoc>
    //    IEnumFORMATETC EnumFormatEtc(DATADIR direction);

    //    /// <devdoc>
    //    ///     Called by an object supporting an advise sink to create a connection between 
    //    ///     a data object and the advise sink. This enables the advise sink to be 
    //    ///     notified of changes in the data of the object.
    //    /// </devdoc>
    //    [PreserveSig]
    //    int DAdvise([In] ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection);

    //    /// <devdoc>
    //    ///     Destroys a notification connection that had been previously set up.
    //    /// </devdoc>
    //    void DUnadvise(int connection);

    //    /// <devdoc>
    //    ///     Creates an object that can be used to enumerate the current advisory connections.
    //    /// </devdoc>
    //    [PreserveSig]
    //    int EnumDAdvise(out IEnumSTATDATA enumAdvise);
    //}

    //public struct FORMATETC
    //{
    //    [MarshalAs(UnmanagedType.U2)]
    //    public short cfFormat;
    //    public IntPtr ptd;
    //    [MarshalAs(UnmanagedType.U4)]
    //    public DVASPECT dwAspect;
    //    public int lindex;
    //    [MarshalAs(UnmanagedType.U4)]
    //    public TYMED tymed;
    //}

    //public enum DATADIR
    //{
    //    DATADIR_GET = 1,
    //    DATADIR_SET = 2
    //}

    ///// <devdoc>
    /////     The IEnumFORMATETC interface is used to enumerate an array of FORMATETC 
    /////     structures. IEnumFORMATETC has the same methods as all enumerator interfaces: 
    /////     Next, Skip, Reset, and Clone.
    ///// </devdoc>
    //[ComImport()]
    //[Guid("00000103-0000-0000-C000-000000000046")]
    //[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    //public interface IEnumFORMATETC
    //{

    //    /// <devdoc>
    //    ///     Retrieves the next celt items in the enumeration sequence. If there are 
    //    ///     fewer than the requested number of elements left in the sequence, it 
    //    ///     retrieves the remaining elements. The number of elements actually 
    //    ///     retrieved is returned through pceltFetched (unless the caller passed 
    //    ///     in NULL for that parameter).
    //    /// </devdoc>
    //    [PreserveSig]
    //    int Next(int celt, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] FORMATETC[] rgelt, [Out, MarshalAs(UnmanagedType.LPArray)] int[] pceltFetched);

    //    /// <devdoc>
    //    ///     Skips over the next specified number of elements in the enumeration sequence.
    //    /// </devdoc>
    //    [PreserveSig]
    //    int Skip(int celt);

    //    /// <devdoc>
    //    ///     Resets the enumeration sequence to the beginning.
    //    /// </devdoc>
    //    [PreserveSig]
    //    int Reset();

    //    /// <devdoc>
    //    ///     Creates another enumerator that contains the same enumeration state as 
    //    ///     the current one. Using this function, a client can record a particular 
    //    ///     point in the enumeration sequence and then return to that point at a 
    //    ///     later time. The new enumerator supports the same interface as the original one.
    //    /// </devdoc>
    //    void Clone(out IEnumFORMATETC newEnum);
    //}

    //[Flags]
    //public enum DVASPECT
    //{
    //    DVASPECT_CONTENT = 1,
    //    DVASPECT_THUMBNAIL = 2,
    //    DVASPECT_ICON = 4,
    //    DVASPECT_DOCPRINT = 8
    //}

    //[Flags]
    //public enum TYMED
    //{
    //    TYMED_HGLOBAL = 1,
    //    TYMED_FILE = 2,
    //    TYMED_ISTREAM = 4,
    //    TYMED_ISTORAGE = 8,
    //    TYMED_GDI = 16,
    //    TYMED_MFPICT = 32,
    //    TYMED_ENHMF = 64,
    //    TYMED_NULL = 0
    //}

    //[Flags]
    //[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1712:DoNotPrefixEnumValuesWithTypeName")]
    //public enum ADVF
    //{
    //    ADVF_NODATA = 1,
    //    ADVF_PRIMEFIRST = 2,
    //    ADVF_ONLYONCE = 4,
    //    ADVF_DATAONSTOP = 64,
    //    ADVFCACHE_NOHANDLER = 8,
    //    ADVFCACHE_FORCEBUILTIN = 16,
    //    ADVFCACHE_ONSAVE = 32
    //}

    ///// <devdoc>
    /////     The IAdviseSink interface enables containers and other objects to 
    /////     receive notifications of data changes, view changes, and compound-document 
    /////     changes occurring in objects of interest. Container applications, for 
    /////     example, require such notifications to keep cached presentations of their 
    /////     linked and embedded objects up-to-date. Calls to IAdviseSink methods are 
    /////     asynchronous, so the call is sent and then the next instruction is executed 
    /////     without waiting for the call's return.
    ///// </devdoc>
    //[ComImport()]
    //[Guid("0000010F-0000-0000-C000-000000000046")]
    //[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    //public interface IAdviseSink
    //{

    //    /// <devdoc>
    //    ///     Called by the server to notify a data object's currently registered 
    //    ///     advise sinks that data in the object has changed.
    //    /// </devdoc>
    //    [PreserveSig]
    //    void OnDataChange([In] ref FORMATETC format, [In] ref STGMEDIUM stgmedium);

    //    /// <devdoc>
    //    ///     Notifies an object's registered advise sinks that its view has changed.
    //    /// </devdoc>
    //    [PreserveSig]
    //    void OnViewChange(int aspect, int index);

    //    /// <devdoc>
    //    ///     Called by the server to notify all registered advisory sinks that 
    //    ///     the object has been renamed.
    //    /// </devdoc>
    //    [PreserveSig]
    //    void OnRename(IMoniker moniker);

    //    /// <devdoc>
    //    ///     Called by the server to notify all registered advisory sinks that 
    //    ///     the object has been saved.
    //    /// </devdoc>
    //    [PreserveSig]
    //    void OnSave();

    //    /// <devdoc>
    //    ///     Called by the server to notify all registered advisory sinks that the 
    //    ///     object has changed from the running to the loaded state.
    //    /// </devdoc>
    //    [PreserveSig]
    //    void OnClose();
    //}

    //public struct STGMEDIUM
    //{
    //    public TYMED tymed;
    //    public IntPtr unionmember;
    //    [MarshalAs(UnmanagedType.IUnknown)]
    //    public object pUnkForRelease;
    //}

    [StructLayout(LayoutKind.Sequential)]

    public struct FILETIME
    {
        public int dwLowDateTime;
        public int dwHighDateTime;
    }

    //[Guid("0000000f-0000-0000-C000-000000000046")]
    //[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    //[ComImport]
    //public interface IMoniker
    //{
    //    // IPersist portion
    //    void GetClassID(out Guid pClassID);

    //    // IPersistStream portion
    //    [PreserveSig]
    //    int IsDirty();
    //    void Load(IStream pStm);
    //    void Save(IStream pStm, [MarshalAs(UnmanagedType.Bool)] bool fClearDirty);
    //    void GetSizeMax(out Int64 pcbSize);

    //    // IMoniker portion
    //    void BindToObject(IBindCtx pbc, IMoniker pmkToLeft, [In()] ref Guid riidResult, [MarshalAs(UnmanagedType.Interface)] out Object ppvResult);
    //    void BindToStorage(IBindCtx pbc, IMoniker pmkToLeft, [In()] ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out Object ppvObj);
    //    void Reduce(IBindCtx pbc, int dwReduceHowFar, ref IMoniker ppmkToLeft, out IMoniker ppmkReduced);
    //    void ComposeWith(IMoniker pmkRight, [MarshalAs(UnmanagedType.Bool)] bool fOnlyIfNotGeneric, out IMoniker ppmkComposite);
    //    void Enum([MarshalAs(UnmanagedType.Bool)] bool fForward, out IEnumMoniker ppenumMoniker);
    //    [PreserveSig]
    //    int IsEqual(IMoniker pmkOtherMoniker);
    //    void Hash(out int pdwHash);
    //    [PreserveSig]
    //    int IsRunning(IBindCtx pbc, IMoniker pmkToLeft, IMoniker pmkNewlyRunning);
    //    void GetTimeOfLastChange(IBindCtx pbc, IMoniker pmkToLeft, out FILETIME pFileTime);
    //    void Inverse(out IMoniker ppmk);
    //    void CommonPrefixWith(IMoniker pmkOther, out IMoniker ppmkPrefix);
    //    void RelativePathTo(IMoniker pmkOther, out IMoniker ppmkRelPath);
    //    void GetDisplayName(IBindCtx pbc, IMoniker pmkToLeft, [MarshalAs(UnmanagedType.LPWStr)] out String ppszDisplayName);
    //    void ParseDisplayName(IBindCtx pbc, IMoniker pmkToLeft, [MarshalAs(UnmanagedType.LPWStr)] String pszDisplayName, out int pchEaten, out IMoniker ppmkOut);
    //    [PreserveSig]
    //    int IsSystemMoniker(out int pdwMksys);
    //}

    //[StructLayout(LayoutKind.Sequential)]

    //public struct BIND_OPTS
    //{
    //    public int cbStruct;
    //    public int grfFlags;
    //    public int grfMode;
    //    public int dwTickCountDeadline;
    //}

    //[Guid("0000000e-0000-0000-C000-000000000046")]
    //[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    //[ComImport]
    //public interface IBindCtx
    //{
    //    void RegisterObjectBound([MarshalAs(UnmanagedType.Interface)] Object punk);
    //    void RevokeObjectBound([MarshalAs(UnmanagedType.Interface)] Object punk);
    //    void ReleaseBoundObjects();
    //    void SetBindOptions([In()] ref BIND_OPTS pbindopts);
    //    void GetBindOptions(ref BIND_OPTS pbindopts);
    //    void GetRunningObjectTable(out IRunningObjectTable pprot);
    //    void RegisterObjectParam([MarshalAs(UnmanagedType.LPWStr)] String pszKey, [MarshalAs(UnmanagedType.Interface)] Object punk);
    //    void GetObjectParam([MarshalAs(UnmanagedType.LPWStr)] String pszKey, [MarshalAs(UnmanagedType.Interface)] out Object ppunk);
    //    void EnumObjectParam(out IEnumString ppenum);
    //    [PreserveSig]
    //    int RevokeObjectParam([MarshalAs(UnmanagedType.LPWStr)] String pszKey);
    //}

    //[Guid("00000101-0000-0000-C000-000000000046")]
    //[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    //[ComImport]
    //public interface IEnumString
    //{
    //    [PreserveSig]
    //    int Next(int celt, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 0), Out] String[] rgelt, IntPtr pceltFetched);
    //    [PreserveSig]
    //    int Skip(int celt);
    //    void Reset();
    //    void Clone(out IEnumString ppenum);
    //}

    //[Guid("00000010-0000-0000-C000-000000000046")]
    //[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    //[ComImport]
    //public interface IRunningObjectTable
    //{
    //    int Register(int grfFlags, [MarshalAs(UnmanagedType.Interface)] Object punkObject, IMoniker pmkObjectName);
    //    void Revoke(int dwRegister);
    //    [PreserveSig]
    //    int IsRunning(IMoniker pmkObjectName);
    //    [PreserveSig]
    //    int GetObject(IMoniker pmkObjectName, [MarshalAs(UnmanagedType.Interface)] out Object ppunkObject);
    //    void NoteChangeTime(int dwRegister, ref FILETIME pfiletime);
    //    [PreserveSig]
    //    int GetTimeOfLastChange(IMoniker pmkObjectName, out FILETIME pfiletime);
    //    void EnumRunning(out IEnumMoniker ppenumMoniker);
    //}

    //[Guid("00000102-0000-0000-C000-000000000046")]
    //[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    //[ComImport]
    //public interface IEnumMoniker
    //{
    //    [PreserveSig]
    //    int Next(int celt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0), Out] IMoniker[] rgelt, IntPtr pceltFetched);
    //    [PreserveSig]
    //    int Skip(int celt);
    //    void Reset();
    //    void Clone(out IEnumMoniker ppenum);
    //}

    //[ComImport()]
    //[Guid("00000103-0000-0000-C000-000000000046")]
    //[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    //public interface IEnumSTATDATA
    //{

    //    /// <devdoc>
    //    ///     Retrieves the next celt items in the enumeration sequence. If there are 
    //    ///     fewer than the requested number of elements left in the sequence, it 
    //    ///     retrieves the remaining elements. The number of elements actually 
    //    ///     retrieved is returned through pceltFetched (unless the caller passed 
    //    ///     in NULL for that parameter).
    //    /// </devdoc>
    //    [PreserveSig]
    //    int Next(int celt, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] STATDATA[] rgelt, [Out, MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] int[] pceltFetched);

    //    /// <devdoc>
    //    ///     Skips over the next specified number of elements in the enumeration sequence.
    //    /// </devdoc>
    //    [PreserveSig]
    //    int Skip(int celt);

    //    /// <devdoc>
    //    ///     Resets the enumeration sequence to the beginning.
    //    /// </devdoc>
    //    [PreserveSig]
    //    int Reset();

    //    /// <devdoc>
    //    ///     Creates another enumerator that contains the same enumeration state as 
    //    ///     the current one. Using this function, a client can record a particular 
    //    ///     point in the enumeration sequence and then return to that point at a 
    //    ///     later time. The new enumerator supports the same interface as the original one.
    //    /// </devdoc>
    //    void Clone(out IEnumSTATDATA newEnum);
    //}

    //public struct STATDATA
    //{
    //    public FORMATETC formatetc;
    //    public ADVF advf;
    //    public IAdviseSink advSink;
    //    public int connection;
    //}

    //internal class ComStreamFromDataStream : IStream
    //{
    //    protected Stream dataStream;

    //    // to support seeking ahead of the stream length...
    //    long virtualPosition = -1;

    //    internal ComStreamFromDataStream(Stream dataStream)
    //    {
    //        if (dataStream == null) throw new ArgumentNullException("dataStream");
    //        this.dataStream = dataStream;
    //    }

    //    private void ActualizeVirtualPosition()
    //    {
    //        if (virtualPosition == -1) return;

    //        if (virtualPosition > dataStream.Length)
    //            dataStream.SetLength(virtualPosition);

    //        dataStream.Position = virtualPosition;

    //        virtualPosition = -1;
    //    }

    //    public virtual IStream Clone()
    //    {
    //        NotImplemented();
    //        return null;
    //    }

    //    public virtual void Commit(int grfCommitFlags)
    //    {
    //        dataStream.Flush();
    //        // Extend the length of the file if needed.
    //        ActualizeVirtualPosition();
    //    }

    //    public virtual long CopyTo(IStream pstm, long cb, long[] pcbRead)
    //    {
    //        int bufsize = 4096; // one page
    //        IntPtr buffer = Marshal.AllocHGlobal(bufsize);
    //        if (buffer == IntPtr.Zero) throw new OutOfMemoryException();
    //        long written = 0;
    //        try
    //        {
    //            while (written < cb)
    //            {
    //                int toRead = bufsize;
    //                if (written + toRead > cb) toRead = (int)(cb - written);
    //                int read = Read(buffer, toRead);
    //                if (read == 0) break;
    //                if (pstm.Write(buffer, read) != read)
    //                {
    //                    throw EFail("Wrote an incorrect number of bytes");
    //                }
    //                written += read;
    //            }
    //        }
    //        finally
    //        {
    //            Marshal.FreeHGlobal(buffer);
    //        }
    //        if (pcbRead != null && pcbRead.Length > 0)
    //        {
    //            pcbRead[0] = written;
    //        }

    //        return written;
    //    }

    //    public virtual Stream GetDataStream()
    //    {
    //        return dataStream;
    //    }

    //    public virtual void LockRegion(long libOffset, long cb, int dwLockType)
    //    {
    //    }

    //    protected static ExternalException EFail(string msg)
    //    {
    //        throw new ExternalException(msg);
    //    }

    //    protected static void NotImplemented()
    //    {
    //        throw new ExternalException("未实现");
    //    }

    //    public virtual int Read(IntPtr buf, /* cpr: int offset,*/  int length)
    //    {
    //        //        System.Text.Out.WriteLine("IStream::Read(" + length + ")");
    //        byte[] buffer = new byte[length];
    //        int count = Read(buffer, length);
    //        Marshal.Copy(buffer, 0, buf, length);
    //        return count;
    //    }

    //    public virtual int Read(byte[] buffer, /* cpr: int offset,*/  int length)
    //    {
    //        ActualizeVirtualPosition();
    //        return dataStream.Read(buffer, 0, length);
    //    }

    //    public virtual void Revert()
    //    {
    //        NotImplemented();
    //    }

    //    public virtual long Seek(long offset, int origin)
    //    {
    //        // Console.WriteLine("IStream::Seek("+ offset + ", " + origin + ")");
    //        long pos = virtualPosition;
    //        if (virtualPosition == -1)
    //        {
    //            pos = dataStream.Position;
    //        }
    //        long len = dataStream.Length;
    //        switch (origin)
    //        {
    //            case GPStream.STREAM_SEEK_SET:
    //                if (offset <= len)
    //                {
    //                    dataStream.Position = offset;
    //                    virtualPosition = -1;
    //                }
    //                else
    //                {
    //                    virtualPosition = offset;
    //                }
    //                break;
    //            case GPStream.STREAM_SEEK_END:
    //                if (offset <= 0)
    //                {
    //                    dataStream.Position = len + offset;
    //                    virtualPosition = -1;
    //                }
    //                else
    //                {
    //                    virtualPosition = len + offset;
    //                }
    //                break;
    //            case GPStream.STREAM_SEEK_CUR:
    //                if (offset + pos <= len)
    //                {
    //                    dataStream.Position = pos + offset;
    //                    virtualPosition = -1;
    //                }
    //                else
    //                {
    //                    virtualPosition = offset + pos;
    //                }
    //                break;
    //        }
    //        if (virtualPosition != -1)
    //        {
    //            return virtualPosition;
    //        }
    //        else
    //        {
    //            return dataStream.Position;
    //        }
    //    }

    //    public virtual void SetSize(long value)
    //    {
    //        dataStream.SetLength(value);
    //    }

    //    public virtual void Stat(IntPtr pstatstg, int grfStatFlag)
    //    {
    //        // GpStream has a partial implementation, but it's so partial rather 
    //        // restrict it to use with GDI+
    //        NotImplemented();
    //    }

    //    public virtual void UnlockRegion(long libOffset, long cb, int dwLockType)
    //    {
    //    }

    //    public virtual int Write(IntPtr buf, /* cpr: int offset,*/ int length)
    //    {
    //        byte[] buffer = new byte[length];
    //        Marshal.Copy(buf, buffer, 0, length);
    //        return Write(buffer, length);
    //    }

    //    public virtual int Write(byte[] buffer, /* cpr: int offset,*/ int length)
    //    {
    //        ActualizeVirtualPosition();
    //        dataStream.Write(buffer, 0, length);
    //        return length;
    //    }
    //}

    [StructLayout(LayoutKind.Sequential, Pack = 8)]
    internal class ImageCodecInfoPrivate
    {
        [MarshalAs(UnmanagedType.Struct)]
        public Guid Clsid;
        [MarshalAs(UnmanagedType.Struct)]
        public Guid FormatID;

        public IntPtr CodecName = IntPtr.Zero;
        public IntPtr DllName = IntPtr.Zero;
        public IntPtr FormatDescription = IntPtr.Zero;
        public IntPtr FilenameExtension = IntPtr.Zero;
        public IntPtr MimeType = IntPtr.Zero;

        public int Flags;
        public int Version;
        public int SigCount;
        public int SigSize;

        public IntPtr SigPattern = IntPtr.Zero;
        public IntPtr SigMask = IntPtr.Zero;
    }
}
