playing with SL5 P/I clipboard IntPtr

Sep 25, 2011 at 1:32 PM

Hi guys,

I've been digging around for information on how to get a IntPtr device bitmap dumped back into a WritableBitmap file.  Going to keep working on the problem, but if there's any suggestions throw them here.  Here's a bit of scaffolding explaining what I want to do.

    internal class Native    {

        [DllImport("user32.dll", EntryPoint = "CloseClipboard", SetLastError = true)]        [return: MarshalAs(UnmanagedType.Bool)]        public static extern bool CloseClipboard();
        [DllImport("user32.dll", EntryPoint = "GetClipboardData", SetLastError = true)]        public static extern IntPtr GetClipboardData(ClipboardFormat uFormat);
        [DllImport("user32.dll", EntryPoint = "IsClipboardFormatAvailable", SetLastError = true)]        [return: MarshalAs(UnmanagedType.Bool)]        public static extern bool IsClipboardFormatAvailable(ClipboardFormat format);
        [DllImport("user32.dll", EntryPoint = "OpenClipboard", SetLastError = true)]        [return: MarshalAs(UnmanagedType.Bool)]        public static extern bool OpenClipboard([In] IntPtr hWndNewOwner);

}

public enum ClipboardFormat : uint    {       

/// <summary>
        /// A handle to a bitmap (HBITMAP).
        /// </summary>
        CF_BITMAP = 2

}

 

code.

IntPtr p = IntPtr.Zero;

try{
Native.OpenClipboard(p);
if (Native.IsClipboardFormatAvailable(ClipboardFormat.CF_BITMAP))

{

        IntPtr p = Native.GetClipboardData(ClipboardFormat.CF_BITMAP);
        if (p != IntPtr.Zero)
        {

// stuck here - how to convert IntPtr to a useful stream
// .NET has a Image.FromHbitmap(IntPtr)

ImageTools.IO.Decoders.AddDecoder<BmpDecoder>();
ImageTools.IO.Decoders.AddDecoder<PngDecoder>();
                BmpDecoder decoder = new BmpDecoder();
ImageTools.ExtendedImage image = new ImageTools.ExtendedImage();
decoder.Decode(image, stream);

        }
}
finally
{
Native.CloseClipboard();
}

Dec 30, 2011 at 7:02 AM

Hello,

We have a SL project that requires this kind of feature, and I just got it working with GDI+ library and SL5.

Here is the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Media.Imaging;
using System.Windows.Controls;
using System.Windows;
using System.IO;

namespace ImageTest
{    
    public static class Clipboard 
    {
        private struct GdiplusStartupInput
        {
            public GdiplusStartupInput(int version)
            {
                GdiplusVersion = version;
                SuppressBackgroundThread = false;
                SuppressExternalCodecs = false;
            }
            public int GdiplusVersion;
            public bool SuppressBackgroundThread;
            public bool SuppressExternalCodecs;
        } 

        [DllImport("user32.dll")]         
        static extern IntPtr GetClipboardData(uint uFormat);         
        [DllImport("user32.dll")] 
        static extern bool IsClipboardFormatAvailable(uint format);         
        [DllImport("user32.dll", SetLastError = true)] 
        static extern bool OpenClipboard(IntPtr hWndNewOwner);         
        [DllImport("user32.dll", SetLastError = true)] 
        static extern bool CloseClipboard();         
        [DllImport("gdiplus.dll", CharSet = CharSet.Unicode)]
        internal static extern int GdipCreateBitmapFromHBITMAP(IntPtr hbitmap, IntPtr hpalette, out IntPtr bitmap);
        [DllImport("gdiplus.dll", CharSet = CharSet.Unicode)]
        internal static extern int GdipSaveImageToFile(IntPtr image, string filename, ref Guid classId, IntPtr encoderParams);
        [DllImport("gdiplus.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
        private static extern long GdiplusStartup(out IntPtr token, ref GdiplusStartupInput gdiplusStartupInput, out IntPtr gdiplusStartupOutput);
        [DllImport("gdiplus.dll")]
        private static extern void GdiplusShutdown(IntPtr token);

        const uint CF_BITMAP = 2;
    
        public static byte[] GetBytes()
        {
            byte[] data = null;
            IntPtr gdipToken = IntPtr.Zero; ;
            string fileName = string.Empty;

            try
            {
                if (!IsClipboardFormatAvailable(CF_BITMAP))
                    return null;

                if (!OpenClipboard(IntPtr.Zero))
                    return null;

                IntPtr hGlobal = GetClipboardData(CF_BITMAP);
                if (hGlobal != IntPtr.Zero)
                {
                    
                    IntPtr gdiplusStartupOutput;
                    GdiplusStartupInput input = new GdiplusStartupInput(1);
                    long num0 = GdiplusStartup(out gdipToken, ref input, out gdiplusStartupOutput); 

                    IntPtr zero = IntPtr.Zero;
                    IntPtr palette = IntPtr.Zero;

                    int num = GdipCreateBitmapFromHBITMAP(hGlobal, palette, out zero);
                    if (num != 0)
                    {
                        return null;
                    }

                    Guid classId = Guid.Parse("{557CF401-1A04-11D3-9A73-0000F81EF32E}");

                    fileName = System.IO.Path.GetTempFileName();

                    int img = GdipSaveImageToFile(zero, fileName, ref classId, palette);

                    if (img != 0)
                    {
                        return null;
                    }
                    else
                    {
                        FileStream fs = File.OpenRead(fileName);
                        try
                        {
                            byte[] bytes = new byte[fs.Length];
                            fs.Read(bytes, 0, Convert.ToInt32(fs.Length));
                            fs.Close();
                            return bytes;
                        }
                        finally
                        {
                            fs.Close();
                        }
                    }
                }
            }
            catch (Exception exp)
            {
                if (System.Diagnostics.Debugger.IsAttached)
                    System.Diagnostics.Debug.WriteLine(exp.ToString());
            }
            finally
            {               
                GdiplusShutdown(gdipToken);  
                CloseClipboard();

                if (!string.IsNullOrEmpty(fileName) && System.IO.File.Exists(fileName))
                    System.IO.File.Delete(fileName);
            }

            return data;
        }
    }
}

This will save image to temporary file and then read it to byte array return value.

Works fine when running in localhost. More info about enabling "in browser" mode can be found here:

http://msdn.microsoft.com/en-us/library/gg192793(v=VS.95).aspx

Next I will try to use GdipSaveImageToStream, so that there is no need for temporary files. 

- Jari -

Dec 30, 2011 at 11:07 AM

Bravo!

I believe this is a very significant code for SL5.  I'm already testing this in my environment and it's working like a charm.

 

Imagine: CTRL-V in the browser will finally upload the image to the server, and link it back in the HTML content.

Jan 9, 2012 at 4:13 PM
Edited Jan 9, 2012 at 4:14 PM

I wrote out what I set out to do in Beta 2 of Silverlight 5, but got stuck on the pointer, in its entirety in a blog post.

http://johnliu.net/blog/2012/1/9/pasting-pictures-from-clipboard-to-sharepoint-in-browser-via.html

Thanks :-)

Feb 29, 2012 at 8:19 AM

You can copy a writeable bitmap to the clipboard easy without needing to create a file and involve lots ot gdi+.

Just use the CreateBitmap funktion and pass in the dimensions and the writableBitmap's Pixels array.
The you use SetClipboard and pass in the handle to the newly created (native) bitmap.

I guess it should be equally easy the other way around by just getting access the HBITMAPS pixcels and use Buffer.Copy to copy the information into a WriteableBitmap.