How to convert System.Windows.Input.Cursor to ImageSource? - c#

I have a .cur file path ("%SystemRoot%\cursors\aero_arrow.cur") witch I want to display in an image control. So I need to convert Cursor to ImageSource. I tried both CursorConverter and ImageSourceConverter but had no luck. I also tried creating Graphics from the cursor and then converting it to Bitmap, But that didn't work either.
This thread says:
it is complicated to convert Cursor to Icon direcetly because Cursor
doesn't expose the imagesource it use.
and
If you really want to bind an image to cursor, there is an approach
you might want to try.
Since WindowForm is capable of drawing the cursor, we can use
WindowForm to draw cursor on a bitmap. After that we could find a way
to copy that bitmap to something WPF support.
Now the funny thing is that I can't create a new instance of System.Windows.Forms.Cursor with neither the file path nor the stream since It throws the following exception:
System.Runtime.InteropServices.COMException (0x800A01E1):
Exception from HRESULT: 0x800A01E1 (CTL_E_INVALIDPICTURE)
at System.Windows.Forms.UnsafeNativeMethods.IPersistStream.Load(IStream pstm)
at System.Windows.Forms.Cursor.LoadPicture(IStream stream)
So can anybody tell me the best way to convert System.Windows.Input.Cursor to ImageSource?
And what about .ani cursors? If I remember correctly System.Windows.Input.Cursor does not support animated cursors, So how can I show them to the user? Converting them to gif then using the 3d party gif libraries?

I found the solution in this thread: How to Render a Transparent Cursor to Bitmap preserving alpha channel?
So here is the code:
[StructLayout(LayoutKind.Sequential)]
private struct ICONINFO
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
[DllImport("user32")]
private static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO pIconInfo);
[DllImport("user32.dll")]
private static extern IntPtr LoadCursorFromFile(string lpFileName);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern bool DeleteObject(IntPtr hObject);
private Bitmap BitmapFromCursor(Cursor cur)
{
ICONINFO ii;
GetIconInfo(cur.Handle, out ii);
Bitmap bmp = Bitmap.FromHbitmap(ii.hbmColor);
DeleteObject(ii.hbmColor);
DeleteObject(ii.hbmMask);
BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
Bitmap dstBitmap = new Bitmap(bmData.Width, bmData.Height, bmData.Stride, PixelFormat.Format32bppArgb, bmData.Scan0);
bmp.UnlockBits(bmData);
return new Bitmap(dstBitmap);
}
private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
//Using LoadCursorFromFile from user32.dll, get a handle to the icon
IntPtr hCursor = LoadCursorFromFile("C:\\Windows\\Cursors\\Windows Aero\\aero_busy.ani");
//Create a Cursor object from that handle
Cursor cursor = new Cursor(hCursor);
//Convert that cursor into a bitmap
using (Bitmap cursorBitmap = BitmapFromCursor(cursor))
{
//Draw that cursor bitmap directly to the form canvas
e.Graphics.DrawImage(cursorBitmap, 50, 50);
}
}
It's written for Win Forms and draws an image. But can be used in wpf as well with referencing to System.Windows.Forms. and then you can convert that bitmap to bitmap source and show it in an image control...
The reason I'm using System.Windows.Forms.Cursor instead of System.Windows.Input.Cursor is that I can't get to create a new instance of cursor using the IntPtr handle...
Edit: The above method does NOT work with cursors having low color bits.
An alternative is to use Icon.ExtractAssociatedIcon instead:
System.Drawing.Icon i = System.Drawing.Icon.ExtractAssociatedIcon(#"C:\Windows\Cursors\arrow_rl.cur");
System.Drawing.Bitmap b = i.ToBitmap();
Hope that helps someone ...

Related

How to convert a Bitmap from memory to an ImageSource then set it as Background for a canvas to draw over it?

I'm trying to take a screenshot of the entire desktop, save it into a bitmap, convert the bitmap into a ImageSource and set that ImageSource as a background to a Canvas on which I am going to draw transparent rectangles.
I tried adding the image directly to the Canvas.
I tried adding the image as background for the Canvas.
I even tried adding the image as background to the form and making the canvas transparent, nothing worked.
I started by taking a Desktop snip and converting the image:
Bitmap bitmapImage = new Bitmap(SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height);
myNewImageSource = Miscellaneous.ImageSourceFromBitmap(bitmapImage);
And this is the conversion function, found on another thread:
[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteObject([In] IntPtr hObject);
public static System.Windows.Media.ImageSource ImageSourceFromBitmap(Bitmap bmp)
{
var handle = bmp.GetHbitmap();
try
{
return Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
}
finally { DeleteObject(handle); }
}
Setting the image directly:
AddImageToCanvas(0, 0, new System.Windows.Controls.Image() { Source = myNewImageSource });
public void AddImageToCanvas(Int32 x, Int32 y, System.Windows.Controls.Image z)
{
imageHolder.Children.Add(z);
Canvas.SetLeft(z, x);
Canvas.SetTop(z, y);
}
Adding the image as background for the canvas:
imageHolder.Background = new ImageBrush(){ ImageSource = myNewImageSource };
Setting the image as form background and making the canvas transparent:
Background = new ImageBrush(){ ImageSource = myNewImageSource }
imageHolder.Background = new SolidColorBrush(System.Windows.Media.Color.FromRgb(0, 0, 0)) { Opacity = 0 }
//I tried making the background transparent by both setting the Opacity to 0 and trying with argb to set the alpha value to 0, neither worked.
I'm expecting to see a snip of my entire desktop as background for the canvas, but nothing is happening and I'm getting no error. Sometime I just get an entirely black Window.
For those that need this, I forgot to actually take the screenshot. Here's my code for it:
Graphics g = Graphics.FromImage(bitmapImage);
g.CopyFromScreen(0, 0, 0, 0, bitmapImage.Size);

this.Region Path data leads to a tiny window

So I created a simple form to test out using an SVG image to draw a custom shaped window. Inspiration found here
It seems to work fine, but no matter what I do my window size is too small to put any controls on.
Reasons for doing this: It's cool? Windows needs better themeing support. I was bored!
I am using Svg from nuget.com from within Visual Studio
Code:
using Svg;
public const int WM_NCLBUTTONDOWN = 0xA1; public const int HT_CAPTION = 0x2;
internal class NativeMethods
{
// Allows forms with Toolbox property set to false to be moved
[System.Runtime.InteropServices.DllImportAttribute("user32.dll", CharSet = CharSet.Unicode)]
internal static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[System.Runtime.InteropServices.DllImportAttribute("user32.dll", CharSet = CharSet.Unicode)]
internal static extern bool ReleaseCapture();
}
public Form1()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
System.Drawing.Drawing2D.GraphicsPath frmshp = new System.Drawing.Drawing2D.GraphicsPath();
//frmshp.AddEllipse(0, 0, this.Width, this.Height);
//SvgDocument.Open(#"TestWindowsshape.svg");
SvgDocument newshp = SvgDocument.Open(#"TestWindowsshape.svg");
frmshp = (newshp.Path);
this.Region = new Region(frmshp);
}
private void Form1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
// Make settings window movable without a titlebar
if (e.Button == MouseButtons.Left)
{
NativeMethods.ReleaseCapture();
NativeMethods.SendMessage(Handle, WM_NCLBUTTONDOWN (IntPtr)HT_CAPTION, (IntPtr)0);
}
}
I have tried to increase the svg size, played with the code some, but nothing I do will make the drawn window bigger. I know I can do this with a BMP, and the TransparancyKey option, but I would like to not do it that way, since the BMP & transparency method has the drawback of not being able to use one color in the bitmap itself. Any advice would be appreciated
Edit:
Matrix m = new Matrix();
m.Scale(100, 100, MatrixOrder.Append);
m.Translate(100, 100, MatrixOrder.Append);
newshp.Path.Transform(m);
Has been tried, with no effect. I would assume that this should have worked does that mean the problem is within my SVG?
The problem seems to be in the SVG file, i adjusted the size of a Rectangle in Notepad++ and got a bigger windows, however more complex shapes will be a hassle. It seems Inkscape cannot create SVGs of a reliable size... I have the "Document properties" set to my screen resolution, but the vectors all turn out too small. Perhaps Illustrator can do this properly.

Drawing and clearing on screen with Graphics.FromHwnd

I am trying to create a program which gets the handle of the window under your cursor, show's some data about it and draws a filled rectangle (with very low alpha) on top of the whole window. I am using C# and winforms.
I have succeeded in doing so, but the problem is my draw method is in a BackgroundWorker's loop and it keeps making more and more rectangles (-> rectangle with higher alpha) on the window or when moving mouse to another window the old one still exists.
I haven't managed to find a method to clear the drawn rectangle as it just "is" on the screen and isn't bound to the graphics object or anything.
I have tried using certain native methods such as
[DllImport("User32.dll")]
public static extern Int64 SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern bool InvalidateRect(IntPtr hWnd, IntPtr lpRect, bool bErase);
[DllImport("user32.dll")]
public static extern bool UpdateWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, RedrawWindowFlags flags);
but none of the above has worked correctly. Some of them do work but as the messages get in the queue the redrawing doesn't occur immediately or is very slow and glitched (flickering etc).
So, the question is, how would I "remove" the rectangle I have drawn using Graphics.FromHwnd(handleOfWindowUnderCursor)? I actually think it doesn't matter that it is drawn on other window as I have had the very same problem earlier when trying to get rid of the drawings on my own form too (never got that fixed either!).
Alternatively, any suggestions on how I could accomplish drawing and removing the rectangle on the window under cursor without using the methods I am now?
I noticed that drawing using
Graphics g = Graphics.FromHwnd(form.Handle);
draws on the form background, under its controls. Is it whatyou want to acomplish?
// draw the rectangle
Brush b = new SolidBrush(Color.FromArgb(20, 0, 0, 255));
g.FillRectangle(b, new Rectangle(5, 5, 200, 200));
// clear the rectangle
g.Clear(this.BackColor);
If I draw on the screen directly, with this:
Graphics g = Graphics.FromHwnd(IntPtr.Zero);
the rectangle disapears immediately after Windows refreshes the screen.
There is a third option, which is not realy strightforward.
Instead of drawing a rectangle, create a form with lowered opacity, TopMost property set to true and without borders. Then make it transparent to events:
protected override void WndProc(ref Message m)
{
const int WM_NCHITTEST = 0x0084;
const int HTTRANSPARENT = (-1);
if (m.Msg == WM_NCHITTEST)
{
m.Result = (IntPtr)HTTRANSPARENT;
}
else
{
base.WndProc(ref m);
}
}
The only things you have to take care after that is this form's Visible, Location and Size properties.
bool change = false;
private void timer1_Tick(object sender, EventArgs e)
{
try
{
if (change)
{
InvalidateRect(IntPtr.Zero, IntPtr.Zero, true);
change = false;
}
else
{
PaintRectangleToScreen();
change = true;
}
}
catch (System.Exception caught)
{
MessageBox.Show(caught.Message);
}
}

Is it possible to get a reference to the raw image buffer for system.drawing.graphics class?

I'm trying to render an image from an unmanaged control on to a WPF window. So far I'm able to get a working graphics object (because I'm able to overlay image to the unmanaged control). What I'm hoping to do is the opposite, which is to capture image from graphics object and save as a imagesource for another control.
var graphics = Graphics.FromHwnd(hwndPtr);//From image unmanaged source
graphics.(??) // save to bitmap or any image format
If not possible to save an image with graphics object directly, would it be possible to get a raw reference to the image buffer of the graphics object? (For use with code like below)
var bmp = (InteropBitmap)Imaging.CreateBitmapSourceFromMemorySection(
hwntPtr,120,120,format,stride,0);
Thanks in Advance..
This is slightly modified code from an MSDN forum article but it should do the trick as far as getting a System.Drawing.Bitmap from a System.Drawing.Graphics object.
[DllImport("coredll.dll", SetLastError = true)]
internal static extern bool BitBlt(IntPtr hDest, int nDstX, int nDstY, int nWidth, int nHeight, IntPtr hSrc, int nSrcX, int nSrcY, uint BitBltOperation);
Bitmap SaveAsBitmap(Graphics gxSrc, int sizeX, int SizeY)
{
Bitmap bm = new Bitmap(sizeX, sizeY);
Graphics gxDst = Graphics.FromImage(bm);
IntPtr hSrc = gxSrc.GetHdc();
IntPtr hDst = gxDst.GetHdc();
BitBlt(hDst, 0, 0, sizeX, sizeY, hSrc, 0, 0, 0xcc0020); // SourceCopy operation
gxDst.ReleaseHdc(hDst);
gxSrc.ReleaseHdc(hSrc);
gxDst.Dispose();
return bm;
}
Original article

PrintScreen Source Code/Simulation

I would like to take a picture of a fullscreen direct3D game. I know I need to create an "overlay" which is harder in c# and I found out that using printscreen (and than pasting it in mspaint) does capture the game window.
I ended up with this unstable code :
try
{
SendKeys.SendWait("{PRTSC}");
Thread.Sleep(100);
if (Clipboard.ContainsImage())
return Clipboard.GetImage();
}
catch (Exception)
{
throw;
}
Bitmap bitmap = new Bitmap(1, 1);
return bitmap;
This code sometimes work, sometimes it throws an ExternalException and sometimes and Clipboard.ContainsImage() returns false and it simply returns a 1,1 sized image.
I would like to try and improve that code so I won't have to rely on the time it takes to copy something to the clipboard (with thread.sleep(20000), it'll work but this code is just a part of a larger code that executes every 800ms).
So I need ideas on how to send keys more reliably or get the that method printscreen uses.
public static class Win32
{
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);
}
public Bitmap CaptureWindow()
{
Bitmap bmp = new Bitmap(this.Width,this.Height);
using (Graphics g = Graphics.FromImage(bmp))
{
Win32.PrintWindow(this.Handle, g.GetHdc(), 0);
g.ReleaseHdc();
}
return bmp;
}

Categories

Resources