positioning the windows system date time clock window [duplicate] - c#

This question already has answers here:
Opening process and changing window position
(3 answers)
Closed 5 years ago.
The event method below brings up the windows system date time clock window. My label is on the lower right side of my form and the system date time clock window appears on the upper left side of my form. Is there a way to position this date time clock window to be on the lower right side of my form when this event handler is clicked?
private void LabelDateTime_Click(object sender, System.EventArgs e)
{
// bring up the date & time dialog
System.Diagnostics.Process.Start("timedate.cpl");
}

Starting a process using System.Diagnostics.Process.Start() in this manner, is not effective, since the generated Process will exit immediately after the window is created. A .cpl applet is not a standard executable and needs then operating system shell and a launcher to start.
However, a stable process can be created using Rundll32.exe, which will generate some threads to host the applet controls and the GDI+ support.
Reaching the applet Window requires some P/Invoke(ing), though, since rundll is window-less and it doesn't reference the one it helps create, so the Process.MainWindowHandle = 0.
Doc Ref. MSDN EnumThreadWndProc() Callback, EnumThreadWindows(), GetWindowRect(), GetWindowText(), SetWindowPos()
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
ProcessStartInfo psInfo = new ProcessStartInfo() {
UseShellExecute = true,
FileName = "rundll32.exe",
Arguments = "shell32.dll, Control_RunDLL timedate.cpl,,0", //<- 0 = First Tab
WindowStyle = ProcessWindowStyle.Normal
};
Process sysClockProcess = new Process() {
SynchronizingObject = this,
EnableRaisingEvents = true,
StartInfo = psInfo
};
sysClockProcess.Start();
sysClockProcess.WaitForInputIdle();
//Insert the Window title. It's case SENSITIVE
//Window Title in HKEY_CURRENT_USER\Software\Classes\Local Settings\MuiCache\[COD]\[LANG]\
string windowTitle = "Date and Time";
int maxLenght = 256;
SetWindowPosFlags flags = SetWindowPosFlags.NoSize |
SetWindowPosFlags.AsyncWindowPos |
SetWindowPosFlags.ShowWindow;
//The first thread is the Main thread. All Dialog windows' handles are attached here.
//The second thread is for GDI+ Hook Window. Ignore it.
EnumThreadWindows((uint)sysClockProcess.Threads[0].Id, (hWnd, lParam) =>
{
StringBuilder lpString = new StringBuilder(maxLenght);
if (GetWindowText(hWnd, lpString, maxLenght) > 0)
if (lpString.ToString() == windowTitle)
{
GetWindowRect(hWnd, out RECT lpRect);
Size size = new Size(lpRect.Right - lpRect.Left, lpRect.Bottom - lpRect.Top);
//Caculate the position of the Clock Windows relative to the ref. Form Size
SetWindowPos(hWnd, (IntPtr)0, ((this.Width + this.Left) - size.Width),
((this.Height + this.Top) - size.Height), 0, 0, flags);
return false;
}
//Window not found: return true to continue the enumeration
return true;
}, ref windowTitle);
sysClockProcess.Exited += (s, ev) => {
Console.WriteLine($"The process has exited. Code: " +
$"{sysClockProcess.ExitCode} Time: {sysClockProcess.ExitTime}");
sysClockProcess.Dispose();
};
Win32 declarations:
// SetWindowPos() flags
[Flags]
public enum SetWindowPosFlags : uint
{
NoSize = 0x0001,
NoActivate = 0x0010,
ShowWindow = 0x0040,
DeferErase = 0x2000,
AsyncWindowPos = 0x4000
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
//Callback for `EnumThreadWindows()`.
public delegate bool EnumThreadWndProc([In] IntPtr hWnd, [In] IntPtr lParam);
[DllImport("user32.dll")]
static extern bool EnumThreadWindows([In] uint dwThreadId, [In] EnumThreadWndProc lpfn, [In] ref string lParam);
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, [Out] StringBuilder lpString, [In] int nMaxCount);
[DllImport("user32.dll", SetLastError = true)]
static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
[DllImport("user32.dll", SetLastError=true)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);

Related

Changing window size using PInvoke does not cause the client area to update

I'm trying to write a simple program that resizes an old game created in the 800x600 era of desktop monitors that doesn't have a resizable form border. So far I have been able to successfully change the window size using either MoveWindow or SetWindowPos, but the problem is the client area (i.e. the actual game) doesn't update; if the window size is increased the extra region is padded with black, and if it's decreased then the image is cropped. However, I have discovered that the show desktop button from Explorer will force the client area to update properly. Therefore, my question is how do I replicate what Explorer is doing when it shows the desktop, sans the minimizing everything part?
I have tried RedrawWindow, UpdateWindow, and even sending a WM_PAINT message directly but nothing seems to work. The game window also does not seem to have child windows, according to spy++.
UPDATE
I have just experimented with minimizing and then re-showing the window, and it does also fix the client area proper. This probably explains why show desktop worked before. Although this is a potential solution, ideally I'd like to be able to resize in-place rather than having to quickly minimize and then re-show.
UPDATE2
I've included the code used for testing below as requested, as well as a screenshot of the symptoms described above.
Test Code:
foreach (var process in Process.GetProcessesByName("magic string"))
{
var h = process.MainWindowHandle;
if (h == IntPtr.Zero)
continue;
GetWindowRect(h, out RECT rect);
//define new window size
int width = 646;
int height = 509;
MoveWindow(h, rect.Left, rect.Top, width, height, true);
//working solution, but not visually ideal
ShowWindow(h, ShowWindowCommands.Minimize);
ShowWindow(h, ShowWindowCommands.Restore);
//note: none of the below methods that attempt to force update work
//(i.e. does not visibly scale the client area)
//RedrawWindow attempts
RedrawWindow(h, IntPtr.Zero, IntPtr.Zero, RedrawWindowFlags.EraseNow);
RedrawWindow(h, IntPtr.Zero, IntPtr.Zero, RedrawWindowFlags.UpdateNow);
RedrawWindow(h, IntPtr.Zero, IntPtr.Zero, RedrawWindowFlags.Invalidate);
//SetWindowPos attempts
SetWindowPos(h, IntPtr.Zero, rect.Left, rect.Top, width, height, SetWindowPosFlags.SWP_SHOWWINDOW);
SetWindowPos(h, IntPtr.Zero, rect.Left, rect.Top, width, height, SetWindowPosFlags.SWP_DRAWFRAME);
//send WM_PAINT
SendMessage(h, WM_PAINT, IntPtr.Zero, IntPtr.Zero);
//#dxiv's suggestion
RedrawWindow(h, IntPtr.Zero, IntPtr.Zero, RedrawWindowFlags.Invalidate);
RedrawWindow(h, IntPtr.Zero, IntPtr.Zero, RedrawWindowFlags.Erase);
//#NetMage's suggestion
UInt32 lParam = 0;
lParam = (UInt32)((UInt16)height << 16 | (UInt16)width);
SendMessage(h, WM_SIZE, IntPtr.Zero, new IntPtr(lParam));
//#Drake Wu - MSFT's suggestion
SetWindowPos(h, IntPtr.Zero, rect.Left, rect.Top, width, height, SetWindowPosFlags.SWP_FRAMECHANGED);
}
PInvoke definitions:
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[DllImport("user32.dll", SetLastError = true)]
static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
[DllImport("user32.dll", SetLastError = true)]
static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int Width, int Height, bool Repaint);
private enum ShowWindowCommands : int
{
Hide = 0,
Normal = 1,
ShowMinimized = 2,
Maximize = 3,
ShowMaximized = 3,
ShowNoActivate = 4,
Show = 5,
Minimize = 6,
ShowMinNoActive = 7,
ShowNA = 8,
Restore = 9,
ShowDefault = 10,
ForceMinimize = 11
}
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);
[Flags]
private enum RedrawWindowFlags : uint
{
Invalidate = 0x1,
InternalPaint = 0x2,
Erase = 0x4,
Validate = 0x8,
NoInternalPaint = 0x10,
NoErase = 0x20,
NoChildren = 0x40,
AllChildren = 0x80,
UpdateNow = 0x100,
EraseNow = 0x200,
Frame = 0x400,
NoFrame = 0x800
}
[DllImport("user32.dll")]
static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, RedrawWindowFlags flags);
[Flags]
private enum SetWindowPosFlags : uint
{
SWP_ASYNCWINDOWPOS = 0x4000,
SWP_DEFERERASE = 0x2000,
SWP_DRAWFRAME = 0x0020,
SWP_FRAMECHANGED = 0x0020,
SWP_HIDEWINDOW = 0x0080,
SWP_NOACTIVATE = 0x0010,
SWP_NOCOPYBITS = 0x0100,
SWP_NOMOVE = 0x0002,
SWP_NOOWNERZORDER = 0x0200,
SWP_NOREDRAW = 0x0008,
SWP_NOREPOSITION = 0x0200,
SWP_NOSENDCHANGING = 0x0400,
SWP_NOSIZE = 0x0001,
SWP_NOZORDER = 0x0004,
SWP_SHOWWINDOW = 0x0040,
}
[DllImport("user32.dll", SetLastError = true)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
private const int WM_PAINT = 0x000F;
private const int WM_SIZE = 0x0005;
[DllImport("user32.dll")]
public static extern Int64 SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
I use Spy++ to monitor the message of this game window, and found that it seems to redraw window content by processing WM_MOVE. Simply calling PostMessage(hwnd,WM_MOVE,0,0) could make the window updated(NOTE THAT: This solution is only useful for this game window.)
The general method is that you can use Spy++ to monitor window messages and compare the differences between the received messages when the behavior is different.
Another method is to add sizing border style to the window:
LONG_PTR stlyle = GetWindowLongPtr(hwnd, GWL_STYLE);
LONG_PTR ret = SetWindowLongPtr(hwnd, GWL_STYLE, stlyle | WS_SIZEBOX);
Then you can manually drag the border to change the size, of course you still need to manually move the window to update content.
Maybe there's a client window within your game's main window that does not change its size when the game's main window does. After all, it is not designed for resizing at all. You can evaluate the window hierachrchy of your application using Spy++, which ships with Visual studio, or a similar tool.
if there is a child window within the main window, you can try to send similar messages to it to make it resize.

Creating a 'Ghost Mouse' to simulate mouse clicks

I know there are already a million threads about this, but I've spent countless hours trying to find a solution and i'm hoping I can find it here instead.
My goal is to create a 'Ghost mouse', meaning that I want to be able to simulate mouse clicks on a certain position in a minimized window, without moving my own cursor our mouse. I want to be able to browse the internet and click on other stuff, while the program does its own thing. This is a feature that many game bots have, but my intention is not to create a bot, but only to experience with.
So far i've managed to simulate mouse clicks, but with my actual mouse.
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
private const int MOUSEEVENTF_LEFTDOWN = 0x02;
private const int MOUSEEVENTF_LEFTUP = 0x04;
public static void LeftClick()
{
int X = Cursor.Position.X;
int Y = Cursor.Position.Y;
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, X, Y, 0, 0);
}
static void Main(string[] args)
{
System.Threading.Thread.Sleep(5000);
Point pt = new Point(1259, 560);
Cursor.Position = pt;
LeftClick();
}
As far as I know, calling mouse_event will not click in minimized windows.
You have to use SendMessage WinApi for this.
First, acquire a handle for the process, either using OpenProcess or Process.GetProcessesByName(processName).First().MainWindowHandle, then you may use the following code:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
public const uint WM_LBUTTONDOWN = 0x0201;
public const uint WM_LBUTTONUP = 0x0202;
public static IntPtr makeLParam(int x, int y)
{
return (IntPtr)((y << 16) | x);
}
public static void sendMouseClick(IntPtr handle, int x, int y)
{
SendMessage(handle, WM_LBUTTONDOWN, (IntPtr)1, makeLParam(x, y));
SendMessage(handle, WM_LBUTTONUP, (IntPtr)1, makeLParam(x, y));
}
Please note that this code is a bit old and might not still work. Also, keep in mind the game protections prevents acquiring handles for the game process. In addition, some games might query the mouse position from the windows rather than using the ones provided with SendMessage

How to fix memory leak in Word Add-In

I have a MS Word Application Add-in written with VSTO. It contains a button used to create new Letter documents. When pressed a document is instantiated, a WPF dialog is displayed to capture information and then the information is inserted into the document.
On one of my test machines I get the following exception when approximately 40 letters are created in a single Word session:
The disk is full. Free some space on this drive, or save the document
on another disk.
Try one or more of the following:
Close any unneeded documents, programs or windows.
Save the document to another disk.
So I monitored the Winword.exe process using Task Manager:
Memory starts at 97,000k
Memory steadily increases with each letter document until the error is seen at approximately 1,000,000k
If I then close all the documents the memory only drops down to 500,000k
Any tips on how I can troubleshoot the memory leak?
I've gone through my code and ensured that event handlers are unregistered and that i'm disposing objects that need disposing.
Any reference articles that I should be reading?
-- Edit --
Malick, I use unmanaged code to make the WPF window look like an Office dialog. Is there a better way of doing this? I'll try removing it. (edit, there wasn't a change. I'll try the memory monitoring tools)
public class OfficeDialog : Window
{
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int width, int height, uint flags);
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam);
const int GWL_EXSTYLE = -20;
const int WS_EX_DLGMODALFRAME = 0x0001;
const int SWP_NOSIZE = 0x0001;
const int SWP_NOMOVE = 0x0002;
const int SWP_NOZORDER = 0x0004;
const int SWP_FRAMECHANGED = 0x0020;
const uint WM_SETICON = 0x0080;
const int ICON_SMALL = 0;
const int ICON_BIG = 1;
public OfficeDialog()
{
this.ShowInTaskbar = false;
//this.Topmost = true;
}
public new void ShowDialog()
{
try
{
var helper = new WindowInteropHelper(this);
using (Process currentProcess = Process.GetCurrentProcess())
helper.Owner = currentProcess.MainWindowHandle;
base.ShowDialog();
}
catch (System.ComponentModel.Win32Exception ex)
{
Message.LogWarning(ex);
//this.Topmost = true;
var helper = new WindowInteropHelper(this);
using (Process currentProcess = Process.GetCurrentProcess())
helper.Owner = currentProcess.MainWindowHandle;
base.ShowDialog();
}
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
RemoveIcon(this);
HideMinimizeAndMaximizeButtons(this);
//using (Process currentProcess = Process.GetCurrentProcess())
// SetCentering(this, currentProcess.MainWindowHandle);
}
public static void HideMinimizeAndMaximizeButtons(Window window)
{
const int GWL_STYLE = -16;
IntPtr hwnd = new WindowInteropHelper(window).Handle;
long value = GetWindowLong(hwnd, GWL_STYLE);
SetWindowLong(hwnd, GWL_STYLE, (int)(value & -131073 & -65537));
}
public static void RemoveIcon(Window w)
{
// Get this window's handle
IntPtr hwnd = new WindowInteropHelper(w).Handle;
// Change the extended window style to not show a window icon
int extendedStyle = OfficeDialog.GetWindowLong(hwnd, GWL_EXSTYLE);
OfficeDialog.SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME);
// reset the icon, both calls important
OfficeDialog.SendMessage(hwnd, WM_SETICON, (IntPtr)ICON_SMALL, IntPtr.Zero);
OfficeDialog.SendMessage(hwnd, WM_SETICON, (IntPtr)ICON_BIG, IntPtr.Zero);
// Update the window's non-client area to reflect the changes
OfficeDialog.SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
}
static void SetCentering(Window win, IntPtr ownerHandle)
{
bool isWindow = IsWindow(ownerHandle);
if (!isWindow) //Don't try and centre the window if the ownerHandle is invalid. To resolve issue with invalid window handle error
{
//Message.LogInfo(string.Format("ownerHandle IsWindow: {0}", isWindow));
return;
}
//Show in center of owner if win form.
if (ownerHandle.ToInt32() != 0)
{
var helper = new WindowInteropHelper(win);
helper.Owner = ownerHandle;
win.WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
else
win.WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWindow(IntPtr hWnd);
}

The handle to window is not found just after showing MessageBox but after 1 second it is found

I show MessageBox and close it after 1 second by finding it by FindWindow function and then sending const UInt32 WM_CLOSE = 0x0010; to this MessageBox. The closing is success, but before I close it I try to move it to the upperright corner of the screen, but just after creating MessageBox by MessageBox.Show() it cannot be found.
[DllImport("user32.dll", SetLastError = true, PreserveSig = true)]
static extern IntPtr FindWindow(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.Dll")]
static extern int PostMessage(IntPtr hWnd, UInt32 msg, int wParam, int lParam);
[DllImport("user32.Dll")]
static extern int MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
const UInt32 WM_CLOSE = 0x0010;
static Thread thread;
static void CloseMessageBox() {
IntPtr hWnd = FindWindow(IntPtr.Zero, "Success!");
if (hWnd != IntPtr.Zero) {//ALWAYS FALSE DOES NOT FIND THE MESSAGEBOX
MoveWindow(hWnd, 0, 0, 300, 300, true);
// PostMessage(hWnd, WM_CLOSE, 0, 0);
Debug.WriteLine("HERE 1");
}
}
static void ShowMessageBox() {
MessageBox.Show("Data has been loaded!", "Success!");
IntPtr hWnd = FindWindow(IntPtr.Zero, "Success!");
if (hWnd != IntPtr.Zero) {//ALWAYS FALSE DOES NOT FIND THE MESSAGEBOX
Debug.WriteLine("HERE 2");
MoveWindow(hWnd, 0, 0, 300, 300, true);
}
}
public static void Seed() {
thread = new Thread(ShowMessageBox);
new Thread(() => {
thread.Start();
Thread.Sleep(1000);
CloseMessageBox();
}).Start();
}
I doubt .NET world MessageBox.Show message box can be found or controlled by native API calls. Better try with MessageBox API itself. Also ensure that API import and string are both Unicode. I have had problems with FindWindow also when searching by caption!
I would suggest to give Spy++ tool a try on how windows are placed, and if they can be found. Z-order, parent/child relationship also matters.

Get form handle from point using pinvoke

I'm trying to get a window handle from point using p/invoke, where window is a form, and not any child control. I have a simple interface where X and Y are entered by user, and then Find button is used to call win32 and get necessary information. My problem is that window is not necessarily a form, it can also be a control. See below screenshot - at (100,100) happened to be Notepad's text area with "StackOverflow" written in it. As a result, Found window shows "StackOverflow".
Is there any way I can restrict window type to be a Form? Expected result is "Untitled - Notepad" for below test case. Alternatively, is there a way to ask another application's control to provide its form's handle? In short, I need to get form's title from (x,y) point. Button click handler code:
private void btn_Find_Click(object sender, EventArgs e)
{
int xPoint = Convert.ToInt32(txt_WindowX.Text);
int yPoint = Convert.ToInt32(txt_WindowY.Text);
IntPtr hWnd = Win32.GetWindowHandleFromPoint(xPoint, yPoint);
txt_FormTitle.Text = Win32.GetWindowTitle(hWnd);
}
Major portion of Win32 class comes from this answer:
Tergiver's answer to "C# - unable to read another application's caption"
Full Win32 class code is provided below:
public class Win32
{
/// <summary>
///
/// </summary>
/// <param name="hwnd"></param>
/// <remarks>https://stackoverflow.com/questions/4604023/unable-to-read-another-applications-caption</remarks>
public static string GetWindowTitle(IntPtr hwnd)
{
if (hwnd == IntPtr.Zero)
throw new ArgumentNullException("hwnd");
int length = Win32.SendMessageGetTextLength(hwnd, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
if (length > 0 && length < int.MaxValue)
{
length++; // room for EOS terminator
StringBuilder sb = new StringBuilder(length);
Win32.SendMessageGetText(hwnd, WM_GETTEXT, (IntPtr)sb.Capacity, sb);
return sb.ToString();
}
return String.Empty;
}
public static IntPtr GetWindowHandleFromPoint(int x, int y)
{
var point = new Point(x, y);
return Win32.WindowFromPoint(point);
}
const int WM_GETTEXT = 0x000D;
const int WM_GETTEXTLENGTH = 0x000E;
[DllImport("user32.dll")]
private static extern IntPtr WindowFromPoint(Point p);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessageGetTextLength(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessageGetText(IntPtr hWnd, int msg, IntPtr wParam, [Out] StringBuilder lParam);
}
You need to locate the top level window. Start from the window that GetWindowHandleFromPoint yielded. Then call GetParent repeatedly until you find a window with no parent. That window with no parent is the top level window that you are looking for.

Categories

Resources