GetWindowRect returning 0 - c#

I am trying to find the window size of a new process that I am opening, but it is returning 0 for the height and width. Here is my code:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(IntPtr hWnd, ref RECT rect);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left; // x position of upper-left corner
public int Top; // y position of upper-left corner
public int Right; // x position of lower-right corner
public int Bottom; // y position of lower-right corner
}
static void Main(string[] args)
{
Process process = new Process();
process.StartInfo.FileName = #"C:\Program Files (x86)\FirstClass\fcc32.exe";
//C:\Program Files (x86)\FirstClass\fcc32.exe
process.Start();
Console.WriteLine(process.HandleCount);
IntPtr hWnd = process.MainWindowHandle;
RECT rect = new RECT();
process.WaitForInputIdle();
GetWindowRect(hWnd, ref rect);
int width = rect.Right - rect.Left;
int height = rect.Bottom - rect.Top;
Console.WriteLine("Height: " + height + ", Width: " + width);
Console.ReadLine();
}

Thanks for the answers everybody, but the actual problem was that I was doing
IntPtr hWnd = process.MainWindowHandle;
before the process window had actually had a chance to open.

The signature is wrong it should be:
[DllImport("user32.dll", SetLastError=true)]
static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
change calling code accordingly:
RECT rect;
process.WaitForInputIdle();
GetWindowRect(hWnd, out rect);

Related

Why does my recorder not allow me to move the window while recording?

I am making a recorder that records a specific window the user chooses. While I am recording, the program forces the window to be active all the time. That means that I can not move the window at all or even to change the size of the window. I was not able to stop the recording. Is there a way to fix that?
Combobox code:
private void Handlebox_SelectedIndexChanged(object sender, EventArgs e)
{
if (Handlebox.SelectedIndex == -1) return;
handle_name = Handlebox.SelectedItem.ToString();
}
The way I add all the processes in the combobox:
foreach (Process process in processlist)
{
if (!String.IsNullOrEmpty(process.MainWindowTitle))
{
Handlebox.Items.Add(process.ProcessName);
}
}
The script that the whole recording takes place:
// Record video:
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll")]
private static extern int SetForegroundWindow(IntPtr hWnd);
private const int SW_RESTORE = 9;
[DllImport("user32.dll")]
private static extern IntPtr ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);
public Bitmap CaptureApplication(string procName)
{
Process proc;
// Cater for cases when the process can't be located.
try
{
proc = Process.GetProcessesByName(procName)[0];
}
catch (IndexOutOfRangeException e)
{
return null;
}
// You need to focus on the application
SetForegroundWindow(proc.MainWindowHandle);
ShowWindow(proc.MainWindowHandle, SW_RESTORE);
// Delay
Thread.Sleep(1000);
Rect rect = new Rect();
IntPtr error = GetWindowRect(proc.MainWindowHandle, ref rect);
// sometimes it gives error.
while (error == (IntPtr)0)
{
error = GetWindowRect(proc.MainWindowHandle, ref rect);
}
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
Graphics.FromImage(bmp).CopyFromScreen(rect.left,rect.top,0,0,new Size(width, height),CopyPixelOperation.SourceCopy);
return bmp;
}
public void RecordVideo()
{
// Keep track of time:
watch.Start();
// Save screenshot:
string name = tempPath + "//screenshot-" + fileCount + ".png";
CaptureApplication(handle_name).Save(name, ImageFormat.Png);
inputImageSequence.Add(name);
fileCount++;
// Dispose of bitmap:
CaptureApplication(handle_name).Dispose();
}

Screenshot with BitBlt results in black image on Windows 10

I am using the code below to capture a screenshot of the currently active window. This code comes from Capture screenshot Including Semitransparent windows in .NET, with a few small additions, i.e. it uses GetForegroundWindow and also a timer so that I can select the desired window.
On Windows 10 (x64) this works fine for Firefox browser, but it does not work with Chrome or Edge.
I find it strange that Screenshot captured using BitBlt in C# results a black image on Windows 10 [duplicate] is marked as a duplicate, because the answer from above (first link) does not solve this problem.
Any ideas why it does not work for Chrome or Edge?
Code:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace App1
{
/// <summary>
/// Description of MainForm.
/// </summary>
public partial class MainForm : Form
{
public MainForm()
{
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
//
// TODO: Add constructor code after the InitializeComponent() call.
//
}
void DoCapture()
{
label1.Text = "Capturing...";
try
{
IntPtr hDesk = GetForegroundWindow();
//IntPtr hDesk = GetDesktopWindow();
var windowRect = new RECT();
GetWindowRect(hDesk, out windowRect);
int width = (int)(windowRect.Right - windowRect.Left);
int height = (int)(windowRect.Bottom - windowRect.Top);
Size sz = new Size(width, height);
sz.Width = (int)(sz.Width * 1.25); // this is just an adjustment for the Windows zoom factor of 125%
sz.Height = (int)(sz.Height * 1.25);
IntPtr hSrce = GetWindowDC(hDesk);
IntPtr hDest = CreateCompatibleDC(hSrce);
IntPtr hBmp = CreateCompatibleBitmap(hSrce, sz.Width, sz.Height);
IntPtr hOldBmp = SelectObject(hDest, hBmp);
bool b = BitBlt(hDest, 0,0, sz.Width, sz.Height, hSrce,
0, 0, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
Bitmap bmp = Bitmap.FromHbitmap(hBmp);
SelectObject(hDest, hOldBmp);
DeleteObject(hBmp);
DeleteDC(hDest);
ReleaseDC(hDesk, hSrce);
bmp.Save(#"c:\temp\test.png");
bmp.Dispose();
label1.Text = "Done";
}
catch (Exception e){
label1.Text = "Exception Occurred";
textBox1.Text = e.ToString();
}
}
void Button1Click(object sender, EventArgs e)
{
timer1.Enabled = true;
}
// P/Invoke declarations
[DllImport("gdi32.dll")]
static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int
wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, CopyPixelOperation rop);
[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr DeleteDC(IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr DeleteObject(IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
[DllImport("gdi32.dll")]
static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll")]
static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp);
[DllImport("user32.dll")]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr ptr);
[DllImport("user32.dll")]
static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left; // x position of upper-left corner
public int Top; // y position of upper-left corner
public int Right; // x position of lower-right corner
public int Bottom; // y position of lower-right corner
}
void Timer1Tick(object sender, EventArgs e)
{
timer1.Enabled = false;
DoCapture();
}
}
}
Running this in SharpDevelop 5.1
Likely it doesn't work for these 2 browsers because they use a hardware-accelerated graphics context (OpenGL- or DirectX-based) to render their content, and this is incompatible with GDI-based calls.
I can suggest a simple workaround:
First use GetForegroundWindow to get the active window rect. Then call GetDesktopWindow and use that handle with the call to BitBlt:
Just add a single line to above code:
IntPtr hDesk = GetForegroundWindow();
... // get dimensions of active window
hDesk = GetDesktopWindow(); // add this line
IntPtr hSrce = GetWindowDC(hDesk);
IntPtr hDest = CreateCompatibleDC(hSrce);
Works fine.

c# Mouse click simulation to hwnd at x/y position

at the last 3 Days I found a lot of code for simulating mouse clicks to other windows. but none of them has worked.
I have the window handle hwnd and the X/Y position (pixel) where the click has to be.
The X/Y position is the position inside the window.
The best code I've found is this:
public void click(IntPtr hWnd, int x, int y)
{
RECT rec = new RECT();
GetWindowRect(hWnd, ref rec);
int newx = x - rec.Left;
int newy = y - rec.Top;
SendMessage(hWnd, WM_LBUTTONDOWN, 1, ((newy << 0x10) | newx));
SendMessage(hWnd, WM_LBUTTONUP, 0, ((newy << 0x10) | newx));
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr A_0, int A_1, int A_2, int A_3);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
But it does not work 100% correctly.
It simulates a mouseclick at the correct window, but at the position of my real cursor.
And after this click my mouse jumps to a random position on the screen.
I hope someone has a working code and a small description how it works.
Thanks
Use this code to simulate the left mouse click in a certain position
//This is a replacement for Cursor.Position in WinForms
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern bool SetCursorPos(int x, int y);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
public const int MOUSEEVENTF_LEFTDOWN = 0x02;
public const int MOUSEEVENTF_LEFTUP = 0x04;
//This simulates a left mouse click
public static void LeftMouseClick(int xpos, int ypos)
{
SetCursorPos(xpos, ypos);
mouse_event(MOUSEEVENTF_LEFTDOWN, xpos, ypos, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, xpos, ypos, 0, 0);
}

Taking Screenshot of a Particular Application

I am trying to take the screenshot of a particular area (application) in the desktop. Please, check this code...
try
{
//Process p = Process.Start("notepad");
//Process p = Process.GetProcessById(11152);
if (p == null)
Console.WriteLine("Got Null");
else
{
IntPtr h = p.Handle;
SetForegroundWindow(h);
ShowWindow(h, 9);
Rect rect = new Rect();
IntPtr error = GetWindowRect(p.MainWindowHandle, ref rect);
while (error == (IntPtr)0)
{
error = GetWindowRect(p.MainWindowHandle, ref rect);
}
Thread.Sleep(2000);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
System.IO.FileStream fs = System.IO.File.Create(#"D:\snapshot.jpg");
Bitmap bitmap = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Graphics.FromImage(bitmap).CopyFromScreen(rect.left, rect.top, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy);
bitmap.Save(fs, System.Drawing.Imaging.ImageFormat.Jpeg);
fs.Close();
bitmap.Dispose();
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
and I have this outside this method..
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool IsIconic(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern IntPtr ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int left;
public int top;
public int right;
public int bottom;
}
When I try to open a new application and try to take screenshot (like notepad in this eg) it works like a charm. But when I try to take the screenshot of a currently running application (like task manager etc..) It does not work (I still get an image of small black rectangle).. Please help me..
Change
IntPtr h = p.Handle;
to
IntPtr h = p.MainWindowHandle;
and check.

Why do I get a height and Width of 0?

Why do I get a heigh and Width of 0 with the below:
static void Main(string[] args)
{
Process notePad = new Process();
notePad.StartInfo.FileName = "notepad.exe";
notePad.Start();
IntPtr handle = notePad.Handle;
RECT windowRect = new RECT();
GetWindowRect(handle, ref windowRect);
int width = windowRect.Right - windowRect.Left;
int height = windowRect.Bottom - windowRect.Top;
Console.WriteLine("Height: " + height + ", Width: " + width);
Console.ReadLine();
}
Here is my definition of GetWindowRect:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
This is my definition for RECT:
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left; // x position of upper-left corner
public int Top; // y position of upper-left corner
public int Right; // x position of lower-right corner
public int Bottom; // y position of lower-right corner
}
Thanks all for any help.
You are passing a process handle to a function, GetWindowRect, that expects a window handle. Naturally, this fails. You should send Notepad.MainWindowHandle instead.
You may be querying the size before notepad has fully started up. Try this:
notePad.Start();
notePad.WaitForInputIdle(); // Waits for notepad to finish startup
IntPtr handle = notePad.Handle;
I like using pinvoke.net to sanity check all my PInvokes. GetWindowRect is described well at: http://pinvoke.net/default.aspx/user32/GetWindowRect.html

Categories

Resources