There are two monitors and an application (e.g. IE) is currently active/displayed in the 2nd monitor. How do I detect if an application is in 1st or 2nd monitor.
I need to know this - to show a userform just on top of the application irrespective of the monitor in which it is. I know what the WindowText (Title) would be (if that helps).
Right now I just show my form near the system tray but would like to show it on top of the application.
// FORM POSITION
this.StartPosition = FormStartPosition.Manual;
Rectangle workArea = Screen.PrimaryScreen.WorkingArea;
int left = workArea.Width - this.Width;
int top = workArea.Height - this.Height;
this.Location = new Point(left, top);
This was converted from VB but should work.
The Test() method shows how you would use it.
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle);
public static RECT GetWindowLocationByName(string name)
{
IntPtr handle = FindWindow(default(string), name);
RECT result = default(RECT);
GetWindowRect(handle, ref result);
return result;
}
public static void Test()
{
dynamic location = GetWindowLocationByName("Untitled - Notepad");
Screen result = null;
foreach (Screen s in Screen.AllScreens) {
if (s.WorkingArea.IntersectsWith(new Rectangle(location.Left, location.Top, location.Right - location.Left, location.Bottom - location.Top))) {
result = s;
}
}
}
Edit: More Info
Step 1: Get the window handle
Step 2: Get the window rect (location/size)
Step 3: Determin which monitor the window resides on
If your looking at overlyaying one window on top of another you don't actually need to know which monitor it is on just the position and size of the window relative to the desktop. In both windows forms and WPF when you set the window location X / Left is the distance in pixels from the left side of the left most monitor. E.g if you have two montior 1024 pixels wide setting X / Left to 2000 will put the window 86 pixels into right hand monitor.
To get the windows position of another process you could use the library mentioned here
How to get and set the window position of another application in C#
You should then be able to check on which screen the position of the rectangle is
private Screen IsVisibleOnScreen(Rectangle rect)
{
foreach (Screen screen in Screen.AllScreens)
{
if (screen.WorkingArea.IntersectsWith(rect))
{
return Screen;
}
}
return null;
}
Related
My initial approach to this was using GetSystemMetrics with SystemMetric.SM_CXSIZE and some simple math based on which buttons are available (times 3, or times 1), via WindowStyle.
[DllImport("user32.dll")]
private static extern int GetSystemMetrics(SystemMetric smIndex);
This has an issue on Windows 10, where the calculated width is approximately 70% of actual. So the width covers just two buttons - maximize and close. Windows 7 and 8.1 are fine, same DPI setting, where it covers all buttons.
I checked a few existing questions on Stack Overflow, and had most success with this one from 2011:
How do I compute the non-client window size in WPF?
Unfortunately, while the suggested approach does work in windows 8.1, it calculates 0 on Windows 10 (latest version, all recommended updates). Is there a way that works on all OS from 7 to 10?
Code was taken from the above answer and modified to calculate total width of window's control buttons, by window handle (hwnd), and changed marshalling to RECT from Rectangle (then I get correct values of left/right).
public static int GetControlButtonsWidth(IntPtr hwnd)
{
// Create and initialize the structure
TITLEBARINFOEX tbi = new TITLEBARINFOEX();
tbi.cbSize = Marshal.SizeOf(typeof(TITLEBARINFOEX));
// Send the WM_GETTITLEBARINFOEX message
SendMessage(hwnd, WM_GETTITLEBARINFOEX, IntPtr.Zero, ref tbi);
int sum = tbi.rgrect.Sum(r => r.right - r.left);
// Return the filled-in structure
return sum;
}
internal const int WM_GETTITLEBARINFOEX = 0x033F;
internal const int CCHILDREN_TITLEBAR = 5;
[StructLayout(LayoutKind.Sequential)]
internal struct TITLEBARINFOEX
{
public int cbSize;
public RECT rcTitleBar;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHILDREN_TITLEBAR + 1)]
public int[] rgstate;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHILDREN_TITLEBAR + 1)]
public RECT[] rgrect;
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessage(
IntPtr hWnd,
int uMsg,
IntPtr wParam,
ref TITLEBARINFOEX lParam);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left, top, right, bottom;
}
You can use DwmGetWindowAttribute, the combined width for those 3 buttons should be 185 pixels on Windows 10, at 125% DPI. Note that if your application is not DPI aware, then the result will still be the same, 185 for example.
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("dwmapi.dll")]
public static extern int DwmGetWindowAttribute(
IntPtr hwnd, int attr, out RECT ptr, int size);
public void foo()
{
int DWMWA_CAPTION_BUTTON_BOUNDS = 5;
RECT rc;
if (0 != DwmGetWindowAttribute(this.Handle, DWMWA_CAPTION_BUTTON_BOUNDS,
out rc, Marshal.SizeOf(typeof(RECT))))
{
//error
}
int width = rc.right - rc.left;
}
I'm working in Visual Studio 2015, in C # and WPF technology. I need to embed to a window of my program, another window system of a third party system, such as Notepad.
I found the answer here (sorry the site is in spanish) but it only works with Windows Form.
This is my code.
//get the third party window
public static IntPtr getWindow(string titleName)
{
Process[] pros = Process.GetProcesses(".");
foreach (Process p in pros)
if (p.MainWindowTitle.ToUpper().Contains(titleName.ToUpper()))
return p.MainWindowHandle;
return new IntPtr();
}
//Get my own window
IntPtr ptr = new WindowInteropHelper(this).Handle;
//a window embedded within the other
[DllImport("user32.dll")]
public extern static IntPtr SetParent(IntPtr hWnChild, IntPtr hWndNewParent);
As I said, that works for Windows Forms, but in WPF doesn't work.
Here is the code, the problem was that you need to set position, width, height and repaint the child window in the new parent window.
public void CapturarApp()
{
hWndApp = ScreenFuntion.getWindow("Notepad");
if (hWndApp.ToInt32() > 0)
{
ProgramsEncrustForm.MoveWindow(hWndApp,
0, 0,
Int32.Parse(Width.ToString()),
Int32.Parse(Height.ToString()), 1);
ProgramsEncrustForm.SetParent(hWndApp, new WindowInteropHelper(this).Handle);
}
else
{
hWndApp = IntPtr.Zero;
}
this.Show();
}
And here is the method to move and repaint the window
[System.Runtime.InteropServices.DllImport("user32.dll")]
public extern static int MoveWindow(IntPtr hWnd, int x, int y,
int nWidth, int nHeight, int bRepaint);
Let me start by saying that I am no stranger to using winapi calls to manipulate other windows, but this is the first time I have seen a window that has two identical control IDs. It seems that the color dialog hasn't changed much between windows versions and I can confirm that this behavior exists on all color dialogs from Windows Vista through to Windows 10 (possibly exists in win xp and lower as well but I can't be bothered to check).
What I am attempting to do is use winapi calls to localize the text in a color dialog control in C#. The best way I have found to do this is to use GetDlgItem() to get a handle to the control I wish to change and then use SetWindowText() to actually change the text. This works great for all controls on the color dialog except for the 'Basic colors:' and 'Custom colors:' labels, which both have a control ID of 0xFFFF (decimal value: 65535).
I use an app called WinID to do this type of work (I find it much easier than using Spy++) and you can see from the screenshots below that the ID of the two text labels do in-fact register as the same ID.
NOTE: I have
tested this using Spy++ and of course I get the same values as shown below:
I would like to know 2 things:
How is it possible for 2 controls to have the same control id?
Is there a 'better way' to get a handle to a control from an external dialog/app using winapi calls? Please keep in mind that using something like FindWindowEx(nColorDialogHandle, IntPtr.Zero, "Static", "&custom colors:"); works, but is not useful to me because I must be able to find the handle without relying on the text in English since this must also work on color dialogs from a non-English version of Windows.
Below is some sample code to demonstrate how I am currently able to change the text on a color dialog. I am happy with the code except that I am unable to get a direct handle to the 'Custom colors:' label since using GetDlgItem() with the control id of 0xFFFF seems to return a handle to the first instance of the control with that ID (in this case it always returns a handle to the 'Basic colors:' label). The only way I am able to get the 'Custom colors:' handle is by using an indirect method of looping through all controls on the color dialog until I find one with text that has not already been changed. This works fine but I would like to know if there is a more direct way to get this handle without looping through controls:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Open the color dialog before the form actually loads
ColorDialogEx oColorDialog = new ColorDialogEx(this.CreateGraphics());
oColorDialog.FullOpen = true;
oColorDialog.ShowDialog();
}
}
public class ColorDialogEx : ColorDialog
{
private const Int32 WM_INITDIALOG = 0x0110; // Windows Message Constant
private Graphics oGraphics;
private const uint GW_HWNDLAST = 1;
private const uint GW_HWNDPREV = 3;
private string sColorPickerText = "1-Color Picker";
private string sBasicColorsText = "2-Basic colors:";
private string sDefineCustomColorsButtonText = "3-Define Custom Colors >>";
private string sOKButtonText = "4-OK";
private string sCancelButtonText = "5-Cancel";
private string sAddToCustomColorsButtonText = "6-Add to Custom Colors";
private string sColorText = "7-Color";
private string sSolidText = "|8-Solid";
private string sHueText = "9-Hue:";
private string sSatText = "10-Sat:";
private string sLumText = "11-Lum:";
private string sRedText = "12-Red:";
private string sGreenText = "13-Green:";
private string sBlueText = "14-Blue:";
private string sCustomColorsText = "15-Custom colors:";
// WinAPI definitions
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool SetWindowText(IntPtr hWnd, string text);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32.dll")]
public static extern long GetWindowRect(int hWnd, ref Rectangle lpRect);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetTitleBarInfo(IntPtr hwnd, ref TITLEBARINFO pti);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
private static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int GetClassName(IntPtr hWnd, System.Text.StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[StructLayout(LayoutKind.Sequential)]
struct TITLEBARINFO
{
public const int CCHILDREN_TITLEBAR = 5;
public uint cbSize;
public RECT rcTitleBar;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHILDREN_TITLEBAR + 1)]
public uint[] rgstate;
}
[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 ColorDialogEx(Graphics g)
{
oGraphics = g;
}
protected override IntPtr HookProc(IntPtr nColorDialogHandle, int msg, IntPtr wparam, IntPtr lparam)
{
IntPtr returnValue = base.HookProc(nColorDialogHandle, msg, wparam, lparam);
if (msg == WM_INITDIALOG)
{
IntPtr[] oStaticHandleArray = new IntPtr[9];
// Change the window title
SetWindowText(nColorDialogHandle, sColorPickerText);
// Get titlebar info for calculations later
TITLEBARINFO oTITLEBARINFO = new TITLEBARINFO();
oTITLEBARINFO.cbSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf(oTITLEBARINFO);
GetTitleBarInfo(nColorDialogHandle, ref oTITLEBARINFO);
// Change the text of the "Basic colors:" label
oStaticHandleArray[0] = GetDlgItem(nColorDialogHandle, 0xFFFF);
SetWindowText(oStaticHandleArray[0], sBasicColorsText);
// Change the text of the "Define Custom Colors >>" button
SetWindowText(GetDlgItem(nColorDialogHandle, 0x2CF), sDefineCustomColorsButtonText);
// Save the "OK" button size and new width
Rectangle oOKButtonRect = new Rectangle();
int nOKButtonWidth = (int)oGraphics.MeasureString(sOKButtonText, new Font("Microsoft Sans Serif", 8, FontStyle.Regular)).Width + 20; // +20 accounts for extra +10 padding on either side
// Find the "OK" Button
IntPtr nChildHandle = GetDlgItem(nColorDialogHandle, 0x1);
if (nChildHandle.ToInt32() > 0)
{
// The "OK" button was found
// Now save the current size and position
GetWindowRect(nChildHandle.ToInt32(), ref oOKButtonRect);
// We have to subtract oOKButtonRect.X value from oOKButtonRect.Width to obtain the "real" button width (same thing with subtracting Y value from Height)
oOKButtonRect.Width = oOKButtonRect.Width - oOKButtonRect.X;
oOKButtonRect.Height = oOKButtonRect.Height - oOKButtonRect.Y;
// Resize the "OK" button so that the new text fits properly
// NOTE: I cannot be sure 100% if it is correct to use the titlebar to find the position of the button or not but the math works out in all of my tests
MoveWindow(nChildHandle, oOKButtonRect.X - oTITLEBARINFO.rcTitleBar.X, oOKButtonRect.Y - oTITLEBARINFO.rcTitleBar.Y - oTITLEBARINFO.rcTitleBar.Height, nOKButtonWidth, oOKButtonRect.Height, true);
// Finally, change the button text
SetWindowText(nChildHandle, sOKButtonText);
}
// Find the "Cancel" Button
nChildHandle = GetDlgItem(nColorDialogHandle, 0x2);
if (nChildHandle.ToInt32() > 0)
{
// The "Cancel" button was found
// Now get the current size and position
Rectangle oCancelButtonRect = new Rectangle();
GetWindowRect(nChildHandle.ToInt32(), ref oCancelButtonRect);
// We have to subtract oCancelButtonRect.X value from oCancelButtonRect.Width to obtain the "real" button width (same thing with subtracting Y value from Height)
oCancelButtonRect.Width = oCancelButtonRect.Width - oCancelButtonRect.X;
oCancelButtonRect.Height = oCancelButtonRect.Height - oCancelButtonRect.Y;
// Resize the "Cancel" button so that the new text fits properly
// NOTE: I cannot be sure 100% if it correct to use the titlebar to find the position of the button or not but the math works out in all of my tests
MoveWindow(nChildHandle, oOKButtonRect.X + nOKButtonWidth - oTITLEBARINFO.rcTitleBar.X + 6, oCancelButtonRect.Y - oTITLEBARINFO.rcTitleBar.Y - oTITLEBARINFO.rcTitleBar.Height, (int)oGraphics.MeasureString(sCancelButtonText, new Font("Microsoft Sans Serif", 8, FontStyle.Regular)).Width + 20, oCancelButtonRect.Height, true);
// Finally, change the button text
SetWindowText(nChildHandle, sCancelButtonText);
}
// Change the text of the "Add to Custom Colors" button
SetWindowText(GetDlgItem(nColorDialogHandle, 0x2C8), sAddToCustomColorsButtonText);
// Change the text of the "Color" label text
oStaticHandleArray[1] = GetDlgItem(nColorDialogHandle, 0x2DA);
SetWindowText(oStaticHandleArray[1], sColorText);
// Change the text of the "Solid" label text
oStaticHandleArray[2] = GetDlgItem(nColorDialogHandle, 0x2DB);
SetWindowText(oStaticHandleArray[2], sSolidText);
// Change the text of the "Hue:" label
oStaticHandleArray[3] = GetDlgItem(nColorDialogHandle, 0x2D3);
SetWindowText(oStaticHandleArray[3], sHueText);
// Change the text of the "Sat:" label
oStaticHandleArray[4] = GetDlgItem(nColorDialogHandle, 0x2D4);
SetWindowText(oStaticHandleArray[4], sSatText);
// Change the text of the "Lum:" label
oStaticHandleArray[5] = GetDlgItem(nColorDialogHandle, 0x2D5);
SetWindowText(oStaticHandleArray[5], sLumText);
// Change the text of the "Red:" label
oStaticHandleArray[6] = GetDlgItem(nColorDialogHandle, 0x2D6);
SetWindowText(oStaticHandleArray[6], sRedText);
// Change the text of the "Green:" label
oStaticHandleArray[7] = GetDlgItem(nColorDialogHandle, 0x2D7);
SetWindowText(oStaticHandleArray[7], sGreenText);
// Change the text of the "Blue:" label
oStaticHandleArray[8] = GetDlgItem(nColorDialogHandle, 0x2D8);
SetWindowText(oStaticHandleArray[8], sBlueText);
// Change the text of the "Custom Colors:" label
SetCustomColorsText(nColorDialogHandle, oStaticHandleArray);
}
return returnValue;
}
private static string GetClassName(IntPtr nHandle)
{
// Create the stringbuilder object that is used to get the window class name from the GetClassName win api function
System.Text.StringBuilder sClassName = new System.Text.StringBuilder(100);
GetClassName(nHandle, sClassName, sClassName.Capacity);
return sClassName.ToString();
}
private static string GetWindowText(IntPtr nHandle)
{
// Create the stringbuilder object that is used to get the window text from the GetWindowText win api function
System.Text.StringBuilder sWindowText = new System.Text.StringBuilder(100);
GetWindowText(nHandle, sWindowText, sWindowText.Capacity);
return sWindowText.ToString();
}
private void SetCustomColorsText(IntPtr nHandle, IntPtr[] oStaticHandleArray)
{
// Find the last control based on the handle to the main window
IntPtr nWorkingHandle = GetWindow(FindWindowEx(nHandle, IntPtr.Zero, null, null), GW_HWNDLAST);
bool bFound = false;
do
{
// Look only for "Static" controls that we have not already changed
if (GetClassName(nWorkingHandle) == "Static" && oStaticHandleArray.Contains(nWorkingHandle) == false)
{
// Found a "Static" control
// Check to see if it is the one we are looking for
string sControlText = GetWindowText(nWorkingHandle);
if (sControlText != "")
{
// Found the "Custom Colors:" label
// Change the text of the "Custom Colors:" label
SetWindowText(nWorkingHandle, sCustomColorsText);
bFound = true;
}
}
// Working backwards we look for the previous control
nWorkingHandle = GetWindow(nWorkingHandle, GW_HWNDPREV);
// Jump out of the loop when the working handle doesn't find anymore controls
if (nWorkingHandle == IntPtr.Zero)
break;
} while (bFound == false);
}
}
}
This dialog is already localized. You can look at it with Visual Studio. Copy c:\windows\system32\en-US\comdlg32.dll.mui to, say, c:\temp\test.dll. Replace "en-US" with your local language tag. In VS use File > Open > File and pick test.dll. You'll see the resources in the MUI file, open the Dialog node and double-click the one named "CHOOSECOLOR". The resource editor opens, you can pick an item in the dialog template and look at its properties in the Property window.
Hopefully it is obvious why the STATIC control has the default IDSTATIC id (65535), there is no need for Windows to do anything to change its properties so no need to find it back. And not for you either, your user will have his own copy of the MUI file that contains the dialog with strings in his native language.
Do note that a machine usually only has MUI files for a single language. If you need to do this to, say, create screenshots for documentation then start here.
It seems that all notes under StikyNot.exe are single exes instead of multiple. Also that means the coordinates of its location are always 0, 0, 0, 0. Is there a way to move it around? I tried using Win32's MoveWindow function without success.
Here's an example of how to iterate through all the Sticky Note windows and move each of them. (Error checking has been removed for brevity. Also, be sure to read the note at the end for some comments on this implementation.)
First, we have to define the RECT struct.
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public RECT(int l, int t, int r, int b)
{
Left = l;
Top = t;
Right = r;
Bottom = b;
}
public int Left;
public int Top;
public int Right;
public int Bottom;
}
Then some key p/Invokes. We'll need FindWindowExW to locate the window with the correct window class for a sticky note. We also need GetWindowRect, so we can figure out the size of the window, so we only move it, rather than a move and resize. Finally, we need SetWindowPos which is pretty self-explanatory.
[DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr FindWindowExW(IntPtr hWndParent, IntPtr hWndAfter,
string lpszClass, string lpszWindow);
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X,
int Y, int cx, int cy, uint uFlags);
Finally our algorithm.
IntPtr hWnd = FindWindowExW(IntPtr.Zero, IntPtr.Zero, "Sticky_Notes_Note_Window", null);
if (hWnd == IntPtr.Zero)
{
int error = Marshal.GetLastWin32Error();
if (error > 0) throw new Win32Exception(error);
else return;
}
IntPtr first = hWnd;
int currentX = 0;
while (hWnd != IntPtr.Zero)
{
RECT r;
bool result = GetWindowRect(hWnd, out r);
if (!result)
{
int error = Marshal.GetLastWin32Error();
if (error > 0) throw new Win32Exception(error);
else return;
}
result = SetWindowPos(hWnd,
IntPtr.Zero,
currentX,
0,
r.Right - r.Left,
r.Bottom - r.Top,
0);
if (!result)
{
int error = Marshal.GetLastWin32Error();
if (error > 0) throw new Win32Exception(error);
else return;
}
currentX += r.Right - r.Left;
hWnd = FindWindowExW(IntPtr.Zero, hWnd, "Sticky_Notes_Note_Window", null);
if (hWnd == IntPtr.Zero)
{
int error = Marshal.GetLastWin32Error();
if (error > 0) throw new Win32Exception(error);
else return;
}
if (hWnd == first) hWnd = IntPtr.Zero;
}
How does it work? First, using a tool like Spy++, I found the window class. From the window's property sheet, we can see that the window's class name is Sticky_Notes_Note_Window.
With the information from Spy++, the first window handle is obtained using FindWindowExW. This value is cached so that it can be determined when we've finished iterating all the windows. Inside the loop, we move the window, then use FindWindowEx to again locate the next window with the same class, if none are found, hWnd will be IntPtr.Zero aka NULL. We also have to check whether we are back to the start of our iteration. (If the notes are wider than the screen, they will spill off to the right. Wrapping the notes to another row is left as an exercise)
The issue with this implementation is that, if the first sticky note is closed, before we have iterated through all of them, then the program will never terminate. It would be better to keep track of all the windows that have been seen, and if any is seen again, then all have been enumerated.
An alternative method would be to use EnumWindows and inside the callback call GetClassName to see if it's a Sticky_Notes_Note_Window, and then act appropriately. The method above required less work, so it's the method I chose.
References:
RECT Structure
FindWindowEx(W)
GetWindowRect
SetWindowPos
Edit: Added error checking based on #DavidHeffernan's comment. Also added clarification about how I found the Window class name.
I've seen a few solutions for the question in the title, but they all don't do what I want.
I have ch = Console.ReadKey().KeyChar; , if a user inputs 'y' I want the app to go into background. if user inputs 'n' the program continues like nothing happened. Is this possible? I did a lot of research but still couldn't find one that fits me.
Here is what i have till now:
char ch = '0';
Console.WriteLine("Enter Log File Destenation:");
string url = Console.ReadLine();
Console.WriteLine("Run In BackGround ? (Defaul Set to False)";
ch = Console.ReadKey().KeyChar;
if (ch == 'y')
{
//Move To Background
}
// continue with program
It's a bitcoin rate logger. it takes the bitrate from a websites title and logs it into a txt file which destination is set by user at the start of the program.
after the destination is set, it asks if it should run in the background or not.
either way the program enters a while(true) loop for log.
Use SetWindowPos method with the param HWND_BOTTOM
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
[DllImport("user32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, out W32RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
public struct W32RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
public const uint HWND_BOTTOM = 1;
static void Main(string[] args)
{
IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;
W32RECT rect;
GetWindowRect(handle , out rect); //to get position and size of your console
SetWindowPos(handle, IntPtr.Zero, rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top, HWND_BOTTOM);//to set background position of your console with the same size and screen's position
}
}
}
Not tested but it will be working
Source : http://www.developpez.net/forums/d199635/environnements-developpement/delphi/setforegroundwindow-setbackgroundwindow/ (I can translate if you need)