I'm trying to do screen captures by capturing a specific window and in order to accurately figure out the size of the window to capture I want to use DwmGetWindowAttribute(). When I call this function with PInvoke on Windows 10 the Rect structure is always empty even though the result value is 0 (success). The Window handle passed in is valid as well because there is fallback code that calls GetWindowRect() which works (albeit with border problems).
I'm a bit at a loss. I used this same code a while back (perhaps on Windows 8.1?) and the same code seemed to be working but now no matter what I do the call to the function always returns an empty structure.
Here's the relevant code.
Definitions:
[DllImport("dwmapi.dll")]
static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out Rect pvAttribute, int cbAttribute);
[Flags]
public enum DwmWindowAttribute : uint
{
DWMWA_NCRENDERING_ENABLED = 1,
DWMWA_NCRENDERING_POLICY,
DWMWA_TRANSITIONS_FORCEDISABLED,
DWMWA_ALLOW_NCPAINT,
DWMWA_CAPTION_BUTTON_BOUNDS,
DWMWA_NONCLIENT_RTL_LAYOUT,
DWMWA_FORCE_ICONIC_REPRESENTATION,
DWMWA_FLIP3D_POLICY,
DWMWA_EXTENDED_FRAME_BOUNDS,
DWMWA_HAS_ICONIC_BITMAP,
DWMWA_DISALLOW_PEEK,
DWMWA_EXCLUDED_FROM_PEEK,
DWMWA_CLOAK,
DWMWA_CLOAKED,
DWMWA_FREEZE_REPRESENTATION,
DWMWA_LAST
}
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public Rectangle ToRectangle()
{
return Rectangle.FromLTRB(Left, Top, Right, Bottom);
}
}
Code to do the capture:
public static Rectangle GetWindowRectangle(IntPtr handle)
{
Rectangle rected = Rectangle.Empty;
Rect rect = new Rect();
if (Environment.OSVersion.Version.Major < 6)
{
GetWindowRect(handle, out rect);
rected = rect.ToRectangle();
}
else
{
int size = Marshal.SizeOf(typeof(Rect));
int res = DwmGetWindowAttribute(handle, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out rect, size);
Debug.WriteLine(res.ToString("x") + " " + size + " " + handle + " " + (int) DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS);
// allow returning of desktop and aero windows
if (rected.Width == 0)
{
GetWindowRect(handle, out rect);
rected = rect.ToRectangle();
Debug.WriteLine("Using GetWindowRect");
}
}
Debug.WriteLine(rected.ToString());
return rected;
}
It feels like something simple is missing here. Any ideas?
Based on the Rick Strahl original code as well as Hans Passant correction I created a more compact version of GetWindowsRectangle. I tested it on Windows 10, here's the code in case it helps someone in the future:
public static Rectangle GetWindowRectangle(IntPtr handle)
{
Rect rect = new Rect();
if (Environment.OSVersion.Version.Major >= 6)
{
int size = Marshal.SizeOf(typeof(Rect));
DwmGetWindowAttribute(handle, (int)DwmWindowAttribute.DWMWA_EXTENDED_FRAME_BOUNDS, out rect, size);
}
else if (Environment.OSVersion.Version.Major < 6 || rect.ToRectangle().Width == 0)
{
GetWindowRect(handle, out rect);
}
return rect.ToRectangle();
}
Use GetWindowRect instead of DwmGetWindowAttribute to receive RECT of the window.
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
Related
So right now I am taking a screenshot of an application by doing this:
using (var bitmap = new Bitmap(Screen.PrimaryScreen.WorkingArea.Width, Screen.PrimaryScreen.WorkingArea.Height))
using (var graphics = Graphics.FromImage(bitmap))
{
graphics.CopyFromScreen(new Point(Screen.PrimaryScreen.WorkingArea.Left, Screen.PrimaryScreen.WorkingArea.Top), new Point(0, 0), Screen.PrimaryScreen.WorkingArea.Size);
bitmap.Save(#"C:\Users\Neoray\Desktop\Test.png", ImageFormat.Bmp);
}
This works fine enough but the problem is that I need to get rid of the Title Bar that is in there (doesn't matter if its afterwards by altering the image or when taking the screenshot itself).
Hope someone can help me with this.
As noted in the comments, this method will return the ClientArea of a Process Window, excluding the Caption (Title Bar) only when the Window actually has a Caption. Not all applications do (in WinForms sense).
Also, no guarantee whatsoever that it can work with a maximized Window which is rendered using DirectX APIs.
In this case, it would be necessary to implement a completely different method.
Anyway, it should work correctly with the sample image you're showing.
The overloaded main method accepts both a Process Name and Process ID.
Parameters:
string processName or int processId:
Specify the process friendly Name (the one shown in Task Manager, it's not case sensitive) or the Process Id (also available in Task Manager).
It can be easily modified to also accept the Window Title.
bool clientAreaOnly:
Switch. Lets you specify whether you want a screenshot of the ClientArea section only or the whole Windows bounds.
float dpi:
You can define the Dpi resolution of the final Bitmap.
Sample usage:
Takes a screenshot of the Main Window of a process with Name [Some Process Name]
Instructs to only consider the ClientArea (the Window Caption and Borders will be excluded)
Specifies that the Bitmap resolution should be 120Dpi
Saves a Bitmap in PNG format, named ScreenShot.png, in [Some Path]
using (var bitmap = ProcessWindowScreenshot("[Some Process Name]", true, 120)) {
bitmap.Save(#"[Some Path]\ScreenShot.png", ImageFormat.Png);
}
Sample procedure:
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Runtime.InteropServices;
public Bitmap ProcessWindowScreenshot(string processName, bool clientAreaOnly, float dpi) {
using (var process = Process.GetProcessesByName(processName)
.FirstOrDefault(p => p.MainWindowHandle != IntPtr.Zero)) {
if (process == null) return null;
return GetProcessWindowScreenshot(GetProcessWindowRect(process.Id, clientAreaOnly), dpi);
}
}
public Bitmap ProcessWindowScreenshot(int processId, bool clientAreaOnly, float dpi) {
return GetProcessWindowScreenshot(GetProcessWindowRect(processId, clientAreaOnly), dpi);
}
private Bitmap GetProcessWindowScreenshot(Rectangle window, float dpi)
{
var bitmap = new Bitmap(window.Width, window.Height, PixelFormat.Format32bppArgb);
bitmap.SetResolution(dpi, dpi);
using (var g = Graphics.FromImage(bitmap))
{
g.InterpolationMode = InterpolationMode.High;
g.CopyFromScreen(window.Location, Point.Empty, window.Size, CopyPixelOperation.SourceCopy);
return bitmap;
};
}
private Rectangle GetProcessWindowRect(int processId, bool isClientArea)
{
IntPtr hWnd = Process.GetProcessById(processId).MainWindowHandle;
if (isClientArea) {
return GetWindowClientRectangle(hWnd);
}
else {
return GetWindowRectangle(hWnd);
}
}
Windows API declarations and helpers methods:
[UIPermission(SecurityAction.Demand, Action = SecurityAction.Demand, Window = UIPermissionWindow.AllWindows)]
public static Rectangle GetWindowRectangle(IntPtr handle) {
NativeMethods.GetWindowRect(handle, out RECT rect);
return rect.ToRectangle();
}
[UIPermission(SecurityAction.Demand, Action = SecurityAction.Demand, Window = UIPermissionWindow.AllWindows)]
public static Rectangle GetWindowClientRectangle(IntPtr handle) {
NativeMethods.GetClientRect(handle, out RECT rect);
NativeMethods.ClientToScreen(handle, out POINT point);
return rect.ToRectangleOffset(point);
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public Rectangle ToRectangle() => Rectangle.FromLTRB(Left, Top, Right, Bottom);
public Rectangle ToRectangleOffset(POINT p) => Rectangle.FromLTRB(p.x, p.y, Right + p.x, Bottom + p.y);
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
}
internal static class NativeMethods
{
[DllImport("User32.dll", SetLastError = true)]
internal static extern bool ClientToScreen(IntPtr hWnd, out POINT point);
[DllImport("User32.dll", SetLastError = true)]
internal static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
[DllImport("User32.dll", SetLastError = true)]
internal static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
}
I have a WPF application that takes a screen shot of the running Handbrake executable using a class called ScreenCapture that I copied from stack overflow.
public class ScreenCapture
{
[DllImport("user32.dll")]
static extern int GetWindowRgn(IntPtr hWnd, IntPtr hRgn);
//Region Flags - The return value specifies the type of the region that the function obtains. It can be one of the following values.
const int ERROR = 0;
const int NULLREGION = 1;
const int SIMPLEREGION = 2;
const int COMPLEXREGION = 3;
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(HandleRef hWnd, out RECT lpRect);
[DllImport("gdi32.dll")]
static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left, Top, Right, Bottom;
public RECT(int left, int top, int right, int bottom)
{
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
public RECT(System.Drawing.Rectangle r) : this(r.Left, r.Top, r.Right, r.Bottom) { }
public int X
{
get { return Left; }
set { Right -= (Left - value); Left = value; }
}
public int Y
{
get { return Top; }
set { Bottom -= (Top - value); Top = value; }
}
public int Height
{
get { return Bottom - Top; }
set { Bottom = value + Top; }
}
public int Width
{
get { return Right - Left; }
set { Right = value + Left; }
}
public System.Drawing.Point Location
{
get { return new System.Drawing.Point(Left, Top); }
set { X = value.X; Y = value.Y; }
}
public System.Drawing.Size Size
{
get { return new System.Drawing.Size(Width, Height); }
set { Width = value.Width; Height = value.Height; }
}
public static implicit operator System.Drawing.Rectangle(RECT r)
{
return new System.Drawing.Rectangle(r.Left, r.Top, r.Width, r.Height);
}
public static implicit operator RECT(System.Drawing.Rectangle r)
{
return new RECT(r);
}
public static bool operator ==(RECT r1, RECT r2)
{
return r1.Equals(r2);
}
public static bool operator !=(RECT r1, RECT r2)
{
return !r1.Equals(r2);
}
public bool Equals(RECT r)
{
return r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom;
}
public override bool Equals(object obj)
{
if (obj is RECT)
return Equals((RECT)obj);
else if (obj is System.Drawing.Rectangle)
return Equals(new RECT((System.Drawing.Rectangle)obj));
return false;
}
public override int GetHashCode()
{
return ((System.Drawing.Rectangle)this).GetHashCode();
}
public override string ToString()
{
return string.Format(System.Globalization.CultureInfo.CurrentCulture, "{{Left={0},Top={1},Right={2},Bottom={3}}}", Left, Top, Right, Bottom);
}
}
public Bitmap GetScreenshot(IntPtr ihandle)
{
IntPtr hwnd = ihandle;//handle here
RECT rc;
GetWindowRect(new HandleRef(null, hwnd), out rc);
Bitmap bmp = new Bitmap(rc.Right - rc.Left, rc.Bottom - rc.Top, PixelFormat.Format32bppArgb);
Graphics gfxBmp = Graphics.FromImage(bmp);
IntPtr hdcBitmap;
try
{
hdcBitmap = gfxBmp.GetHdc();
}
catch
{
return null;
}
bool succeeded = PrintWindow(hwnd, hdcBitmap, 0);
gfxBmp.ReleaseHdc(hdcBitmap);
if (!succeeded)
{
gfxBmp.FillRectangle(new SolidBrush(Color.Gray), new Rectangle(Point.Empty, bmp.Size));
}
IntPtr hRgn = CreateRectRgn(0, 0, 0, 0);
GetWindowRgn(hwnd, hRgn);
Region region = Region.FromHrgn(hRgn);//err here once
if (!region.IsEmpty(gfxBmp))
{
gfxBmp.ExcludeClip(region);
gfxBmp.Clear(Color.Transparent);
}
gfxBmp.Dispose();
return bmp;
}
public void WriteBitmapToFile(string filename, Bitmap bitmap)
{
bitmap.Save(filename, ImageFormat.Bmp);
}
So when the button click handler below is called a screenshot of the handbrake window is taken.
I write it to the harddrive to make sure its ok:
handbrake screen shot.
I create an instance of a CLR class library ClassLibrary1::Class1 and call the method "DoSomething" passing it the System.Drawing.Bitmap object.
private void button4_Click(object sender, RoutedEventArgs e)
{
string wName = "HandBrake";
IntPtr hWnd = IntPtr.Zero;
foreach (Process pList in Process.GetProcesses())
{
if (pList.MainWindowTitle.Contains(wName))
{
hWnd = pList.MainWindowHandle;
var sc = new ScreenCapture();
SetForegroundWindow(hWnd);
var bitmap = sc.GetScreenshot(hWnd);
sc.WriteBitmapToFile("handbrake.bmp", bitmap);
Bitmap image1 = (Bitmap)System.Drawing.Image.FromFile("handbrake.bmp", true);
ClassLibrary1.Class1 opencv = new ClassLibrary1.Class1();
opencv.DoSomething(image1);
}
}
}
Inside DoSomething I attempt to convert the System.Drawing.Bitmap to a OpenCV class cv::Mat. I call cv::imwrite to make sure the bitmap is still ok, unfortunately somethings gone wrong: mangled handbrake screenshot
void Class1::DoSomething(Bitmap ^mybitmap)
{
cv::Mat *imgOriginal;
// Lock the bitmap's bits.
Rectangle rect = Rectangle(0, 0, mybitmap->Width, mybitmap->Height);
Imaging::BitmapData^ bmpData = mybitmap->LockBits(rect, Imaging::ImageLockMode::ReadWrite, mybitmap->PixelFormat);
try
{
// Get the address of the first line.
IntPtr ptr = bmpData->Scan0;
// Declare an array to hold the bytes of the bitmap.
// This code is specific to a bitmap with 24 bits per pixels.
int bytes = Math::Abs(bmpData->Stride) * mybitmap->Height;
array<Byte>^rgbValues = gcnew array<Byte>(bytes);
// Copy the RGB values into the array.
System::Runtime::InteropServices::Marshal::Copy(ptr, rgbValues, 0, bytes);
imgOriginal = new cv::Mat(mybitmap->Height, mybitmap->Width, CV_8UC3, (void *)ptr, std::abs(bmpData->Stride));
}
finally { mybitmap->UnlockBits(bmpData); }//Remember to unlock!!!
cv::imwrite("from_mat.bmp", *imgOriginal);
}
Can anybody spot my error?
Since your image is stretched horizontally, I'm betting that you have the wrong pixel format selected. (It's not stretched vertically, nor skewed diagonally, so the stride is correct.) CV_8UC3 specifies 24 bits per pixel, but I think that your BMP file is using 32 bits per pixel.
Switch your pixel format to CV_8UC4, or better yet, read the number of bits per pixel from the image and select the correct CV format based on that.
Side note: Since you're doing sc.WriteBitmapToFile() followed by opencv.DoSomething(Image.FromFile(), the entire bit about how you're capturing the screenshot is irrelevant. You're reading the bitmap from a file; that's all that matters.
I'm trying to read a pixel from an external program and then get its RGB colors.
This works flawlessly whenever I find the location with the mouse and extract the pixel colors. However when I try to do it from a console program, the RBG colors comes back.. differently than what I expected.
I believe it could be an offset missing, so whenever I find the location using my mouse it's using my screens pixel, and whenever I activate the external program using the function below it will take the pixel locations from that window handle.
It could also be something about it being a game, and it's getting drawn differently, any tips? If I try to get the pixel colors from notepad it works.
[DllImport("user32.dll")] static extern bool SetForegroundWindow(IntPtr hWnd);
public static void Activate(string processName = "CookieGame")
{
var processes = Process.GetProcessesByName(processName);
var process = processes.FirstOrDefault();
if (process != null)
{
SetForegroundWindow(process.MainWindowHandle);
}
}
I use the following function for extracting pixel colors from a location, this is run after I've set the program as active window (function above):
public class MailReader
{
[DllImport("user32.dll")] public static extern bool GetCursorPos(ref Point lpPoint);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)] public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
static Bitmap screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppArgb);
public static Color GetColorAt(Point location)
{
using (Graphics gdest = Graphics.FromImage(screenPixel))
{
using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero)){
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
}
}
return screenPixel.GetPixel(0, 0);
}
}
Conole program being run, this returns the correct X and Y pixels that I told it to, but the colors come back off:
namespace TestProgram.TestConsole
{
class Program
{
static void Main(string[] args)
{
var model = new PixelInformation
{
X = 505,
Y = 27,
R = 117,
G = 208,
B = 50
};
var point = new Point();
point.X = model.X;
point.Y = model.Y;
ActivateWindow.Activate("cookieGame");
var location = PixelReader.GetColorAt(point);
Console.WriteLine("Position X: " + point.X + " Y: " + point.Y);
Console.WriteLine("R:" + location.R + " " + "G:" + location.G + " B:" + location.B);
Console.ReadKey();
}
}
}
These symptoms are consistent with your system using font scaling other than 100% and the process not being DPI aware. Thus the process is subject to DPI virtualization.
Mouse stimulation using SendInput works perfectly on MainDisplay. However when I use SendInput for extended screen (e.g. Second screen placed to the left of the main display in my case. Issues is replicable irrespective of the extended display any place around main display but with different resolution then main display):
If I use SendInput on extended screen, the mouse position has offset in both X and Y position, ever so slightly ranging from 40 to 80 points in x and 10 to 20 points in Y based on if X (width) and Y(height) of extended screen is different to main display width/height)
Thanks in advance for any support as to why difference on extended screen
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetCursorPos(ref Win32Point pt);
[DllImport("user32.dll")]
internal static extern bool SetCursorPos(int X, int Y);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
internal enum SendInputEventType : int
{
InputMouse,
InputKeyboard
}
[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint nInputs, ref Input pInputs, int cbSize);
public struct Input
{
public uint InputType;
public MouseInput MI;
}
public struct MouseInput
{
public int Dx;
public int Dy;
public uint MouseData;
public uint DwFlags;
public uint Time;
public IntPtr DwExtraInfo;
}
public enum MouseEventInfo
{
mouseEventfMove = 0x0001,
mouseEventfLeftdown = 0x0002,
mouseEventfLeftup = 0x0004,
mouseEventfRightdown = 0x0008,
mouseEventfRightup = 0x0010,
mouseEventfWheel = 0x0800,
mouseEventfAbsolute = 0x8000,
wheelDelta = 0x0078
}
static int CalculateAbsoluteCoordinateX(int x, System.Drawing.Rectangle currentBounds)
{
return ((currentBounds.X + x) * 65536) / (currentBounds.Width);
}
static int CalculateAbsoluteCoordinateY(int y, System.Drawing.Rectangle currentBounds)
{
return (((currentBounds.Y + y) * 65536) / currentBounds.Height);
}
// for me screen at index 0 (screen no 1) is main display. Screen id 2
//placed to the left of the main display as per resolution screen i.e.at
//index 1 (Screen.AllScreens[1]) is extended display and Bound.X is a -ve value
public static int ScreenId = 2;
public static System.Drawing.Rectangle CurrentBounds
{
get
{
return SysForms.Screen.AllScreens[ScreenId - 1].Bounds;
}
}
public static void ClickLeftMouseButton(int x, int y)
{
Input mouseInput = new Input();
mouseInput.InputType = SendInputEventType.InputMouse;
mouseInput.MI.Dx = CalculateAbsoluteCoordinateX(x, CurrentBounds);
mouseInput.MI.Dy = CalculateAbsoluteCoordinateY(y, CurrentBounds);
mouseInput.MI.MouseData = 0;
mouseInput.MI.DwFlags = MouseEventInfo.mouseEventfMove | MouseEventInfo.mouseEventfAbsolute;
SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));
mouseInput.MI.DwFlags = MouseEventInfo.mouseEventfLeftdown;
SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));
mouseInput.MI.DwFlags = MouseEventFlags.mouseEventfLeftup;
SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));
}
//Below is code of the WPF MainWindow for testing. Two buttons with click event.
// For main display with screenid as 1 both setcursor position and sendinput
//work perfectly, as I get the MousePosition, but when I apply this to
//extended screen (currently with two screen, main display is screen 1 in my
//case and screen 2 is extended screen, they put the mouse at two different positions.
//I have my doubts the way I am using the extended screen Bounds.X, but
//haven't will able to fix the issue
int x = 600;
int y = 300;
private void btnSend_Click(object sender, RoutedEventArgs e)
{
SetCursorPos(SysForms.Screen.AllScreens[ScreenId - 1].Bounds.X + x, SysForms.Screen.AllScreens[screenId - 1].Bounds.Y + y);
}
private void btnSend1_Click(object sender, RoutedEventArgs e)
{
ClickLeftMouseButton(x, y);
}
Found the issue. While using SendInput, the conversion of x,y in absolute value must be done in relation to Main/Primary screen.
Thus the changes:
static int CalculateAbsoluteCoordinateX(int x, System.Drawing.Rectangle currentBounds)
{
return ((currentBounds.X + x) * 65536) / (SystemParameters.PrimaryScreenWidth);
}
static int CalculateAbsoluteCoordinateY(int y, System.Drawing.Rectangle currentBounds)
{
return (((currentBounds.Y + y) * 65536) / SystemParameters.PrimaryScreenHeight);
}
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