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
Related
I'm using visual Studio 2015. I want to Create a rounded corner windows button in C#. Like this:
RoundedButton
I'm musing this code
[System.Runtime.InteropServices.DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")]
private static extern System.IntPtr CreateRoundRectRgn
(
int nLeftRect, // x-coordinate of upper-left corner
int nTopRect, // y-coordinate of upper-left corner
int nRightRect, // x-coordinate of lower-right corner
int nBottomRect, // y-coordinate of lower-right corner
int nWidthEllipse, // height of ellipse
int nHeightEllipse // width of ellipse
);
[System.Runtime.InteropServices.DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
private static extern bool DeleteObject(System.IntPtr hObject);
private void button1_Paint(object sender, PaintEventArgs e)
{
System.IntPtr ptr = CreateRoundRectRgn(0, 0, this.Width, this.Height, 15, 15); // _BoarderRaduis can be adjusted to your needs, try 15 to start.
this.Region = System.Drawing.Region.FromHrgn(ptr);
DeleteObject(ptr);
}
When I use this on `Form_paint`, it is working fine, but not working on `Button`.
When I use this on Form_paint, it is working fine, but not working on Button.
The problem is that you are still getting the size for the rounded region from the whole form, rather than the button, and then you are applying the region to the form as well, rather than to the button. So, in essence, by putting the region-manipulating code in the button's Paint event, you have changed when it's happening, but you haven't changed what it's doing. Try this:
[DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")]
private static extern System.IntPtr CreateRoundRectRgn
(
int nLeftRect, // x-coordinate of upper-left corner
int nTopRect, // y-coordinate of upper-left corner
int nRightRect, // x-coordinate of lower-right corner
int nBottomRect, // y-coordinate of lower-right corner
int nWidthEllipse, // height of ellipse
int nHeightEllipse // width of ellipse
);
[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
private static extern bool DeleteObject(System.IntPtr hObject);
private void button1_Paint(object sender, PaintEventArgs e)
{
IntPtr ptr = CreateRoundRectRgn(0, 0, button1.Width, button1.Height, 15, 15);
button1.Region = Region.FromHrgn(ptr);
DeleteObject(ptr);
}
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.
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);
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);
How to open the Interaction.InputBox to center of the form? I know there is a code for the position of the InputBox
Interaction.InputBox("Question?", "Title", "Default Text", x,y);
I will be using this InputBox in different form of different sizes. Is there a way to open the InputBox in the center of the form? Or I have to position them individually on each form?
Is it possible also to reposition the OKbutton and Cancelbutton of InputBox?
Here is something simple to calculate the center of a form, the extra offset is for the size of the input box.
{
int x = this.Left + (this.Width / 2) - 200;
int y = this.Top + (this.Height / 2) - 100;
}
Pass these into the input box for x and y
If you want full customisation then creating your own form is the best way to go as indicated in Fabio's comment.
However, if you just want to approximately centre the box and you will be doing it many times then you could write your own extension method to show and position the input box for you:
public static class FormExtensions
{
public static string CentredInputBox(this Form form, string prompt, string title = "", string defaultResponse = "")
{
const int approxInputBoxWidth = 370;
const int approxInputBoxHeight = 158;
int left = form.Left + (form.Width / 2) - (approxInputBoxWidth / 2);
left = left < 0 ? 0 : left;
int top = form.Top + (form.Height / 2) - (approxInputBoxHeight / 2);
top = top < 0 ? 0 : top;
return Microsoft.VisualBasic.Interaction.InputBox(prompt, title, defaultResponse, left, top);
}
}
Usage from within a form:
this.CentredInputBox("MyPrompt", "MyTitle", "MyDefaultResponse");
It's not perfect because if the box is bigger than normal for some reason then it won't quite be in the centre, and I think its size is variable depending on how much text is in it. However, it shouldn't be far off in normal usage.
To center your InputBox, you can try using Win32 functions to deal with it. This code works for you:
[DllImport("user32")]
private static extern int SetWindowPos(IntPtr hwnd, IntPtr afterHwnd, int x, int y, int cx, int cy, int flag);
[DllImport("user32")]
private static extern IntPtr FindWindow(string className, string caption);
[DllImport("user32")]
private static extern int GetWindowRect(IntPtr hwnd, out RECT rect);
//RECT structure
public struct RECT {
public int left, top, right, bottom;
}
public void ShowCenteredInputBox(string prompt, string title, string defaultReponse){
BeginInvoke((Action)(() => {
while (true) {
IntPtr hwnd = FindWindow(null, title + "\n\n\n");//this is just a trick to identify your InputBox from other window with the same caption
if (hwnd != IntPtr.Zero) {
RECT rect;
GetWindowRect(hwnd, out rect);
int w = rect.right - rect.left;
int h = rect.bottom - rect.top;
int x = Left + (Width - w) / 2;
int y = Top + (Height - h) / 2;
SetWindowPos(hwnd, IntPtr.Zero, x, y, w, h, 0x40);//SWP_SHOWWINDOW = 0x40
break;
}
};
}));
Microsoft.VisualBasic.Interaction.InputBox(prompt, title + "\n\n\n", defaultResponse,0,0);
}
Of course you can also change the position of the buttons, label and TextBox on your InputBox but it's very nasty and tricky, we can say that it's not simple. The recommended solution for you is to create new standard form in System.Windows.Forms.Form, add controls to it and use the method ShowDialog() to show your form.. Of course it requires more code to do but it allows you to fully customize the look and feel and its behaviors.
You can set the InputBox's starting position. There's a property for that
InputBox ib = new InputBox();
ib.StartPosition = FormStartPosition.CenterParent;
Where as FormStartPosition is an enum, from which you can select your desired position!
You can sipmly use -1 for x and y:
Interaction.InputBox("Question?", "Title", "Default Text", -1,-1);