My code enumerates a metafile:
private void Parse()
{
Graphics graphics = Graphics.FromHwnd(IntPtr.Zero);
PointF pointf = new PointF();
graphics.EnumerateMetafile(_metafile, pointf, ParseCallback);
}
private bool ParseCallback(EmfPlusRecordType recordType,
int flags, int dataSize, IntPtr data, PlayRecordCallback callbackData)
{
// do stuff
}
My development machine is Windows 7 VirtualBox guest on Ubuntu host.
The code used to work fine. However, when I turned off Aero, the code stopped working: The ParseCallback would never be called.
Only when I turned Aero back on, ParseCallback was executed again.
Why and how can I make this code work on non-Aero-enabled machines?
I don't have a full answer to the "why?" question, but it does not work because you're getting the Graphics GDI+ object from the Window handle. Instead, you want to get it from a GDI DC, like this:
private void Parse()
{
IntPtr hdc = GetDC(IntPtr.Zero); // get entire screen dc
Graphics graphics = Graphics.FromHdc(hdc));
PointF pointf = new PointF();
graphics.EnumerateMetafile(_metafile, pointf, ParseCallback);
ReleaseDC(IntPtr.Zero, hdc);
}
[DllImport("user32.dll")]
static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll")]
static extern IntPtr ReleaseDC(IntPtr hwnd, IntPtr hdc);
Note you could also use the Graphics object from the Form OnPaint(PaintEventArgs e) method, it should also work, just like in the official sample code for EnumerateMetafile method here: Graphics.EnumerateMetafile Method
Related
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.
I need to take a screenshot of a non-active external application, for example, TeamSpeak or Skype.
I have searched and i didn't find much, i know that it is not possible to screenshot a minimised application, but i think it should be possible to screenshot a non-active application.
PS : I want to screenshot just the application, so if another application is on top of the one i want, would it be a problem?
I have no code right now, i have found a user32 API that can do what i want but i forgot the name..
Thanks for the help.
The API you're after is PrintWindow:
void Example()
{
IntPtr hwnd = FindWindow(null, "Example.txt - Notepad2");
CaptureWindow(hwnd);
}
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);
[DllImport("user32.dll")]
static extern bool GetWindowRect(IntPtr handle, ref Rectangle rect);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
public void CaptureWindow(IntPtr handle)
{
// Get the size of the window to capture
Rectangle rect = new Rectangle();
GetWindowRect(handle, ref rect);
// GetWindowRect returns Top/Left and Bottom/Right, so fix it
rect.Width = rect.Width - rect.X;
rect.Height = rect.Height - rect.Y;
// Create a bitmap to draw the capture into
using (Bitmap bitmap = new Bitmap(rect.Width, rect.Height))
{
// Use PrintWindow to draw the window into our bitmap
using (Graphics g = Graphics.FromImage(bitmap))
{
IntPtr hdc = g.GetHdc();
if (!PrintWindow(handle, hdc, 0))
{
int error = Marshal.GetLastWin32Error();
var exception = new System.ComponentModel.Win32Exception(error);
Debug.WriteLine("ERROR: " + error + ": " + exception.Message);
// TODO: Throw the exception?
}
g.ReleaseHdc(hdc);
}
// Save it as a .png just to demo this
bitmap.Save("Example.png");
}
}
Using GetWindowRect coupled with PrintWindow from user32 API should be all you need to implement the feature. PrintWindow will properly capture the contents of a specific application even if it's obscured by another window on top of it.
It's worth noting that this might not work for capturing contents of DirectX windows.
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);
}
}
I am trying to a get a window's height and width using this :
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr handle, out Rectangle rect);
private void timer1_Tick(object sender, EventArgs e)
{
Rectangle bonds = new Rectangle();
GetWindowRect(GetForegroundWindow(), out bonds);
Bitmap bmp = new Bitmap(bonds.Width, bonds.Height);
Graphics memoryGraphics = Graphics.FromImage(bmp);
IntPtr dc = memoryGraphics.GetHdc();
PrintWindow(GetForegroundWindow(), dc, 0x1);
memoryGraphics.ReleaseHdc(dc);
bmp.Save("C:\\Test.gif", ImageFormat.Gif);
}
but bonds.width and height are more than the real window.
Example : My window is 100x150, bond.height is like 400 and bonds.width is like 500. I get a picture with the size of 400x500 that consist of the window in the corner of the picture (which is good) and the rest is black since the picture is way bigger than the window (which is bad)
NOTE : I don't care that the window is aeroless, it's good for me.
So any suggestion, or maybe a better way of getting a region?
You need to use ScreenToClient and translate the coordinates.
GetWindowRect returns the window area relative to the top-left of the screen ( 0, 0 ).
This is the code I am using to take a screenshot:
[DllImport("gdi32.dll",EntryPoint="DeleteDC")]
public static extern IntPtr DeleteDC(IntPtr hDc);
[DllImport("gdi32.dll",EntryPoint="DeleteObject")]
public static extern IntPtr DeleteObject(IntPtr hDc);
[DllImport("gdi32.dll",EntryPoint="BitBlt")]
public static extern bool BitBlt(IntPtr hdcDest,int xDest,
int yDest,int wDest,int hDest,IntPtr hdcSource,
int xSrc,int ySrc,int RasterOp);
[DllImport ("gdi32.dll",EntryPoint="CreateCompatibleBitmap")]
public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc,
int nWidth, int nHeight);
[DllImport ("gdi32.dll",EntryPoint="CreateCompatibleDC")]
public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport ("gdi32.dll",EntryPoint="SelectObject")]
public static extern IntPtr SelectObject(IntPtr hdc,IntPtr bmp);
[DllImport("user32.dll", EntryPoint="GetDesktopWindow")]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll",EntryPoint="GetDC")]
public static extern IntPtr GetDC(IntPtr ptr);
[DllImport("user32.dll",EntryPoint="GetSystemMetrics")]
public static extern int GetSystemMetrics(int abc);
[DllImport("user32.dll",EntryPoint="GetWindowDC")]
public static extern IntPtr GetWindowDC(Int32 ptr);
[DllImport("user32.dll",EntryPoint="ReleaseDC")]
public static extern IntPtr ReleaseDC(IntPtr hWnd,IntPtr hDc);
public static Bitmap GetDesktopImage()
{
//In size variable we shall keep the size of the screen.
SIZE size;
//Variable to keep the handle to bitmap.
IntPtr hBitmap;
//Here we get the handle to the desktop device context.
IntPtr hDC = GetDC(GetDesktopWindow());
//Here we make a compatible device context in memory for screen
//device context.
IntPtr hMemDC = CreateCompatibleDC(hDC);
//We pass SM_CXSCREEN constant to GetSystemMetrics to get the
//X coordinates of the screen.
size.cx = GetSystemMetrics(SM_CXSCREEN);
//We pass SM_CYSCREEN constant to GetSystemMetrics to get the
//Y coordinates of the screen.
size.cy = GetSystemMetrics(SM_CYSCREEN);
//We create a compatible bitmap of the screen size and using
//the screen device context.
hBitmap = CreateCompatibleBitmap
(hDC, size.cx, size.cy);
//As hBitmap is IntPtr, we cannot check it against null.
//For this purpose, IntPtr.Zero is used.
if (hBitmap!=IntPtr.Zero)
{
//Here we select the compatible bitmap in the memeory device
//context and keep the refrence to the old bitmap.
IntPtr hOld = (IntPtr)SelectObject
(hMemDC, hBitmap);
//We copy the Bitmap to the memory device context.
BitBlt(hMemDC, 0, 0,size.cx,size.cy, hDC,0, 0,SRCCOPY);
//We select the old bitmap back to the memory device context.
SelectObject(hMemDC, hOld);
//We delete the memory device context.
DeleteDC(hMemDC);
//We release the screen device context.
ReleaseDC(GetDesktopWindow(), hDC);
//Image is created by Image bitmap handle and stored in
//local variable.
Bitmap bmp = System.Drawing.Image.FromHbitmap(hBitmap);
//Release the memory to avoid memory leaks.
DeleteObject(hBitmap);
//This statement runs the garbage collector manually.
GC.Collect();
//Return the bitmap
return bmp;
}
//If hBitmap is null, retun null.
return null;
}
It does capture a screenshot, but I want to take the screenshot with the tooltip visible. What do I have to add to take screenshot with the tooltip and cursor visible?
Vista has a snipping Tool to easily do so as given in http://blog.falafel.com/2008/08/12/CaptureScreenShotsWithToolTipsAndPopupsUsingVistaSnippingTool.aspx.
There is also an application via Silverlight: http://blogs.msdn.com/swick/archive/2007/12/18/snipping-pictures-with-silverlight.aspx
I don't know if source code is available for the snipping tool. You can try taking a video, then taking a snapshot of a part of the video as shown here.