Set specific size to application's window programmatically - c#

My program (C#) is a taskbar and in its menu I can launch applications, compatible XP to 8.1. I would like to start any applications with a specific window's size, because few applications (OpenOffice, LibreOffice...), start flattened when i launch them for the first time.
I've tested to maximize this window with ShellExecute with ShowCommands.SW_MAXIMIZE parameter:
ShellExecute(IntPtr.Zero, "open", executablePath, executableParam, "", ShowCommands.SW_MAXIMIZE);
But when I clicked on the "restore" button of the window, there is the same problem, the window is flattened.
While the "restored" size isn't configured by manual resizing, the used value is specific for each applications.
Instead of ShellExecute, I use CreateProcess to specify a size :
const uint NORMAL_PRIORITY_CLASS = 0x0020;
STARTUPINFO si = new STARTUPINFO();
si.dwY = 50;
si.dwX = 50;
si.dwXSize = 200;
si.dwYSize = 800;
si.dwFlags = 0x00000006; //STARTF_USESIZE + STARTF_USEPOSITION
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
CreateProcess(programPath, programParams, IntPtr.Zero, IntPtr.Zero, true, NORMAL_PRIORITY_CLASS, IntPtr.Zero, null, ref si, out pi);
But there is one problem, we can launch an other OpenOffice/LibreOffice process with File->New->... In this case the application isn't launch by my program so my default size isn't applied.
I've checked the Windows Registry before and after changing this value, two keys are changed :
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\OpenSavePidlMRU\reg]
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\Count]
These keys contains hexa values (first key) :
before :
"MRUListEx"=hex:00,00,00,00,04,00,00,00,01,00,00,00,05,00,00,00,03,00,00,00,02,\00,00,00,ff,ff,ff,ff
after :
"MRUListEx"=hex:01,00,00,00,00,00,00,00,04,00,00,00,05,00,00,00,03,00,00,00,02,\00,00,00,ff,ff,ff,ff
It isn't understandable to me, so I'm still searching if exist a programmatic way to specific window size of an application/all applications or an other specification to set into the Windows Registry. I'm not searching a solution to resize or move a window already launched like SetWindowPos.
But many websites explain there's just one solution to define the window's size: "drag the window to the size, then close the window and restart it"
http://help.wfu.edu/public/computers/standard-load-software/windows-7---set-default-window-size
http://www.tomshardware.co.uk/forum/28659-45-change-default-window-size
This answer is the final solution or there is a way to do it programmatically?
Thanks in advance.
EDIT :
Actually, when a window is restored, I resize only the first time until the user kill it.
My program keeps every window's handle in an object with a boolean initialized to false to specify if my window has been resized. I use GetForegroundWindow then with GetWindowPlacement I check if WINDOWPLACEMENT.flags == WindowPlacementFlag.WPF_NONE and if my boolean is always egals to false in that case I use GetWindowRect and SetWindowPos to set a specific size with the same position and assign my boolean to true.
I think is a good solution, but I loose the saved position by user (I can do it when window's size is under a specific value but the problem still there) and we seen the resizing...

This problem usually occurs because the coder used GetWindowPos to get the position to save, but the window is closed while minimised, so the position is a tiny window hidden by the taskbar. This is then the position which is saved. The correct method is to use GetWindowPlacement to save the position.
You could enumerate all top-level windows, check GetWindowPlacement to see which are visible and restored but with an inappropriate size or position (e.g. off-screen, too small). Then you could call SetWindowPlacement to move those windows back to a reasonable minimum size on-screen.
In all likelihood when they save their position again they will save the new position.
Don't poll though. Instead make it a utility that the user can run when they need to. (You will probably need an exceptions list too - some utilities might create an off-screen window on purpose.)

Related

Programmatically Maximize Borderless Window

I currently have a way to do exactly what im asking, but the results are very in-efficient for some users.
Some users reported it making it as if it minimized (no window showing ever but still in taskbar), some reported for example in an ultrawide 21:9 it would only maximize from the left of a 1080p 16:9 width, yet I have a 32:9 super-ultrawise and have had no issues.
My current flow:
Get the Screen Dimensions excluding the taskbar on-load:
MaximizedBounds = Screen.FromHandle(mForm.Handle).WorkingArea; (mForm = Application.OpenForms[0], for support from any thread and such)
From another thread/function/class run:
Form1.mForm.Invoke(new MethodInvoker(() => Form1.mForm.WindowState = FormWindowState.Maximized));
This should result in a maximized window with the proper resolution, but it doesn't :/
What I would prefer:
NOT require to get the screen dimensions on-load, so use something from official Microsoft Code, either DllImport's, .NET Framework Code, or msapi to get the PROPER MaximizedBounds for Borderless Forms. (formborderstyle.none)
I set MaximizedBounds because if I don't, the application will fullscreen the entire screen, not "maximize" like traditional apps but would end up doing more of a Video Player style fullscreen.
Using my 32:9 screen and my 4k 16:9 laptop's screen on Extend Display mode, I managed to re-create the issue
Re-production Steps:
Open the application, leave it on the screen it first started in
Maximize the application (will work fine)
Unmaximize and move it to the other screen
Click maximize, your result should be like above.
This means, the on-load Maximize Bounds only gets the active bounds once which is expected, but due to me executing the Form Style change on a different class and different thread, I cant actually edit the MaximizedBounds property on it everytime I want to maximize, due to property not being public.
Thanks to HansPassant!
Found out exactly whats needed.
Problem:
When moving application to other monitor, it would use the same bounds. Causing it to not properly Maximize
When updating bounds via WndProc(), it would still try and maximize on Monitor 1 but outside its actual bounds, making it look like the application was hidden >:(
Updating the Bounds (put this in Form1):
protected override void WndProc(ref Message m) {
if(m.Msg == 0x02E0) {
Rectangle b = Screen.FromControl(mForm).WorkingArea; //where mForm is Application.OpenForms[0], this is specific to my applications use case.
b.X = 0; //The bounds will try to base it on Monitor 1 for example, 1920x1080, to another 1920x1080 monitor on right, will set X to 1080, making it show up on the monitor on right, outside its bounds.
b.Y = 0; //same as b.X
MaximizedBounds = b; //Apply it to MaximizedBounds
m.Result = (IntPtr)0; //Tell WndProc it did stuff
}
base.WndProc(ref m); //If any other messages were called, do the defaults this method does. Required even if you edited a msg.
}
Updating DPI Awareness for the WndProc message to fire (put this in app.manifest, make it if it doesnt exist):
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness> //Windows 10
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware> //Old-Compatibility
</windowsSettings>
</application>
Now it no longer shows up out of bounds, and is finally updating the MaximizeBounds when changing monitors!
Thanks #HansPassant for the tips!

How to detect if a window is visible on modern multi monitor systems

How do I detect if window is not visible to the user and then move it to a position that is visible to allow the user to size it to their liking.
The problem:-
I have a program that save the position and size of the form this works perfectly.
Unfortunately, many users now have multiple screen graphics cards that can be switched between duplicate and extended.
Therefore, if the user closes the form (the program saves where it was) and the user switches his system to say extended from duplicate then opens the program.
The program form is now not visible because it has moved off the limits of the current screen system :(
The user can see it running on the task bar. However clicking the icon only gives a brief flash of minimising or maximising the program and right clicking only has the option to close.
I need to trap for not visible to the user and then auto resize to any active window. The user can then resize/move as they see fit.
I would welcome any ideas/pointers
This answer was provided for the same question on the MSDN forums - essentially, it's a quick check of the bounds of the screen, and checks to see if the program is within those bounds. If not, it moves it to 0, 0:
using System.Windows.Forms;
class Form1 : Form {
protected void EnsureVisible() {
foreach (Screen scrn in Screen.AllScreens) {
// You may prefer Intersects(), rather than Contains()
if (scrn.Bounds.Contains(this.Bounds)) {
return;
}
}
this.Location = new Point( 0, 0 );
}
}
You could make use of the AllScreens property to get a list of all the screens connected to the computer. Using that, you can check the bounding boxes of each screen and test if your window lies outside all of them. If so, then reset the position.
And Jared Harley beat me to it, while I was typing this.

set Cursor to desired point c#

Im trying to move my cursor according to my hand point in kinect, I can get the real coordinates I mean I can move an image on screen but I want real cursor to be settled according to my hand coordinates. I tried Console.SetCursor(x,y) but it gives exception I also tried to download windows forms dll but I cant find the version 4.00 . Is there any simple way to set cursor in a desired position? (which is working by the way and as I said Console.SetcursorPosition is not wodking?)
You didn't provide very much information about you app but I suspect that you just need to assign to Cursor.Position from System.Windows.Forms. You may need to add a reference to System.Windows.Forms in order to gain access to this, depending on exactly what type of project you have.
If you want to keep it lightweight and avoid taking a reference to WinForms then you could just pinvoke to SetCursorPos.
[DllImport("user32.dll")]
static extern bool SetCursorPos(int X, int Y);
Just use
Cursor.Position = new Point();
You can find more information's here
Thank you for question and answer.
I found strange and unobvious behavior.
Let you use multi-monitor (two or three monitor) configuration, and use two windows in your application.
One window is active, and you set cursor position for second windows.
And this two windows located on different monitors.
Then, you need call SetCursorPos TWICE!
TWICE, who would have thought?
Firs call move cursor to virtual borderline between monitors.
And only second call move cursor to required position second window!
Example of code (after 6 hours of experiments):
SetForegroundWindow(this.Handle); // set second window active
SendMessage(this.Handle, 0x20, this.Handle, (IntPtr)1); // Send WM_SETCURSOR
SetCursorPos(400, 600);
Thread.Sleep(50);
SetCursorPos(400, 600);

PrintScreen only active window

I'm writing a simple Windows Forms utility to screenshot either the entire screen or an active window and save the file. The winform has a button called 'Capture' that, when clicked, takes the screenshot.
I have the code entirely working for the entire screen but can't figure it out for the active window.
By my current code, if I select 'Active Window', it ends up Print Screening the application itself which isn't what I want.
Here's my code (under the Capture_Click method):
try
{
this.Hide();
bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
//bmpScreenshot = new Bitmap(this.Bounds.Width, this.Bounds.Height, PixelFormat.Format32bppArgb);
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
if (AltPrint.Checked)
{
gfxScreenshot.CopyFromScreen(this.Bounds.X, this.Bounds.Y, 0, 0, this.Bounds.Size, CopyPixelOperation.SourceCopy);
}
else
{
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
}
bmpScreenshot.Save(SaveLocation, ImageFormat.Png);
number++;
}
In your button_Click method, to capture a window other than the one which is doing the capturing, the only possible way I can see is to make use of the user32 Windows API.
You will first need to platform invoke some methods like this:
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, GetWindowLongNIndex nIndex);
I recommend www.pinvoke.net/ for more info about this.
Here is my own Window class (work in progress, but useable) which you are welcome to use as an example: http://pastebin.com/fj2tEaPY
Some useful properties in there for you are:
get AllWindows
get TaskbarWindows
set ForegroundWindow
You'll need to get the handle of your current window (use get ForegroundWindow, then the Handle property), then find the window which you want to capture the image of, perhaps using code similar to that in TaskbarWindows. Once you have the window, you will need to send the keypresses Alt + PrtScn (again maybe using user32 API, but I won't go into it.) Then use the static ForegroundWindow property to set the orignal window as the forground using the handle which you saved at the start.
Good luck!
As far as I can see your code uses the bounds of its own form to capture the "active" window. That is probably not what you intend.
If you want to use the mouse to "print" a window (and not hotkey which probably is easier to implement) you need to be able to from your own form let the user point to another window to capture. Here is an idea describing how to do that:
When the user presses the "Capture" button capture the mouse and initiate a drag operation.
Let the user drag the mouse (perhaps now having a "bullseye" or a "camera" cursor) to the window to capture.
When the user releases the mouse determine the window it was on top and use the location and size of this window to capture the correct pixels.
You will probably need to use P/Invoke to get information about all top-level windows on the desktop.
Note that "mouse capture" is very different from "screen capture". When an application "captures the mouse" it takes ownership of the mouse cursor and doesn't the ability to track the mouse even when it is moved outside the bounds of the window of the application.
Your comments seem to understand that once your app gets the focus (when you click on it) the other app has lost it and is no longer 'active'.
A possible alternative: your application gets a WM_SETFOCUS message when it gets focus. The wParam of that message is a handle to whichever window has just given up the focus -- and that window belongs to the app you want to capture.
Of course, that window will only be a subwindow or control, but you can figure out which app owns that button.
I ended up taking a different route.
I allow the user to resize my form window to cover exactly the area they need to screenshot, and then when they click the button, the form hides itself, takes a screenshot of the area it was covering and appears again. Contrary to my initial plan, I believe this will be more beneficial, as my targeted users (friends at an overclocking forum I'm part of) seldom take screenshots of just one window. They mostly need a couple of windows covered (temp monitoring, clockspeed monitoring, stability test etc.).
Thanks for all the answers guys. They definitely got me thinking, and made me realize what my options were.
Should anyone else need it, this is the code I use for the button that captures only part of the screen:
try
{
this.Hide();
Thread.Sleep(250);
bmpScreenshot = new Bitmap(this.Bounds.Width, this.Bounds.Height, PixelFormat.Format32bppArgb);
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
gfxScreenshot.CopyFromScreen(this.Bounds.X, this.Bounds.Y, 0, 0, this.Bounds.Size, CopyPixelOperation.SourceCopy);
bmpScreenshot.Save(SaveLocation, ImageFormat.Png);
tbxStatus.AppendText(Environment.NewLine);
tbxStatus.AppendText(Environment.NewLine);
tbxStatus.AppendText("Screenshot saved at " + SaveLocation);
numSuffix++;
}
catch (Exception ex)
{
tbxStatus.AppendText(Environment.NewLine);
tbxStatus.AppendText(Environment.NewLine);
tbxStatus.AppendText("Unable to take screenshot. Exception: " + ex.ToString());
}
finally
{
this.Show();
}
You need to borrow the design of commercially available screen capturing apps, such as Psp. I think you need to use a 'hot' key to trigger the capturing of the active window. Instead of doing it on Capture_Click event. This way, you can hide your application's window so that it does not become the active window. Then when the user pushes the 'hot' key, you can capture it.
What about SendKeys?
Windows.Forms.SendKeys.Send("%{PRTSC}");
It will copy currently selected window in to Clipboard object.
Than calling
BitmapSource image = Clipboard.GetImage();
you can get your image back

Is there a way to change the maximum width of a window without using the WM_GETMINMAXINFO message?

I want to change the imposed Windows maximum width that a window can be resized to, for an external application's window (not my C#/WinForms program's window).
The documentation of GetSystemMetrics for SM_CXMAXTRACK says:
"The default maximum width of a window that has a caption and sizing borders, in pixels. This metric refers to the entire desktop. The user cannot drag the window frame to a size larger than these dimensions. A window can override this value by processing the WM_GETMINMAXINFO message."
Is there a way to modify this SM_CXMAXTRACK value (either system wide or for one particular window), without processing the WM_GETMINMAXINFO message? Maybe an undocumented function, a registry setting, etc.?
(Or: The documentation for MINMAXINFO.ptMaxTrackSize says: "This value is based on the size of the virtual screen and can be obtained programmatically from the system metrics SM_CXMAXTRACK and SM_CYMAXTRACK." Maybe there is a way to change the size of the virtual screen?)
Thank you
The only two ways I know of of limiting the size of a Window is by handling WM_GETMINMAXINFO, or by modifying the values passed in the WINDOWPOS structure in WM_WINDOWPOSCHANGING. Both of these methods involve being able to intercept and handle the messages for the Window. There's no external setting to limit the size of a Window as far as I know.
If you don't have the source code to the other app about the only thing I could suggest is to write a program that injects code into the other app (via SetWindowsHookEx or CreateRemoteThread), and then subclass the window and handle those messages.

Categories

Resources