I use Remote Desktop to connect from a laptop with Windows XP Professional SP3 and one screen to a remote PC running Windows 7 Professional with two monitors.
The laptop resolution is around 1024x768 and each monitor on the remote PC is around 1600x900.
Before I start the Remote Desktop session, I move all windows on the second monitor of the Windows 7 PC to the first monitor. (Both laptop and PC are in the same office area.)
The Remote Desktop session works, but after closing the session on the laptop and returning to work on the remote Windows 7 PC, I usually have to relocate and resize many of the windows to get back to the original arrangement.
With my current configuration, how can I avoid the "relocate and resize" step above?
If the laptop had Windows 7 Professional, would that help solve this problem?
You should probably move this to superuser, but since you asked on StackOverflow, you could implement a program that does what you describe.
In pseudocode:
class WindowPosition {
IntPtr hWnd;
RECT Location;
}
List<WindowPosition> positions = null;
void OnCaptureWindowPositionHotkey() {
positions = new List<WindowPosition>();
EnumWindows(enumStoreWindows, null);
}
void OnResetWindowPositionHotkey() {
EnumWindows(enumStoreWindows, null);
}
void enumSetWindows(IntPtr hwnd, IntPtr obj) {
positions.Add(new WindowPosition() {
hWnd = hwnd,
Location = GetWndLocation(hwnd)
};
}
RECT GetWndLocation(IntPtr hwnd) {
RECT outrect = null;
GetWindowRect(hwnd, out outrect);
return outrect;
}
void enumSetWindows(IntPtr hwnd, IntPtr obj) {
var loc = (from wl in positions
where wl.hWnd == hwnd
select wl).FirstOrDefault();
if (loc == null) return;
SetWindowPos(hwnd, null,
loc.Location.X,
loc.Location.Y,
loc.Location.Width,
loc.Location.Height,
0);
}
where EnumWindows, SetWindowPos and GetWindowRect are all Win32 functions. See:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633497(v=vs.85).aspx, http://msdn.microsoft.com/en-us/library/windows/desktop/ms633545(v=vs.85).aspx, and http://msdn.microsoft.com/en-us/library/windows/desktop/ms633519(v=vs.85).aspx.
Related
I use without problems a program that uses [SetForegroundWindow] in C# to bring the window to the foreground and activate in Windows 7 and Windows 10 operating systems, but now with Windows 11 it stopped working.
I need help, does anyone know how to be able to use SetForegroundWindow on Windows 11?
Site: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setforegroundwindow
//windows forms project
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
//use
IntPtr janela = FindWindow(null, "Seleção de Cliente");
SetForegroundWindow(janela);
I'm working with code in a WPF application that needs to figure out the DPI scaling size for each monitor in Windows. I'm able to figure out the DPI of the primary screen but for some reason I cannot figure out how to get the scale for other monitors - the others all return the same DPI as the main monitor.
There's a bit of code to do this so bear with me. The first set of code deals with getting the DPI based on an HWND. The code gets the active monitor and then retrieves the DPI settings and compares figures out a ratio to the 96 DPI (typically 100%).
public static decimal GetDpiRatio(Window window)
{
var dpi = WindowUtilities.GetDpi(window, DpiType.Effective);
decimal ratio = 1;
if (dpi > 96)
ratio = (decimal)dpi / 96M;
return ratio;
}
public static decimal GetDpiRatio(IntPtr hwnd)
{
var dpi = GetDpi(hwnd, DpiType.Effective);
decimal ratio = 1;
if (dpi > 96)
ratio = (decimal)dpi / 96M;
//Debug.WriteLine($"Scale: {factor} {ratio}");
return ratio;
}
public static uint GetDpi(IntPtr hwnd, DpiType dpiType)
{
var screen = Screen.FromHandle(hwnd);
var pnt = new Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var mon = MonitorFromPoint(pnt, 2 /*MONITOR_DEFAULTTONEAREST*/);
Debug.WriteLine("monitor handle: " + mon);
try
{
uint dpiX, dpiY;
GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
return dpiX;
}
catch
{
// fallback for Windows 7 and older - not 100% reliable
Graphics graphics = Graphics.FromHwnd(hwnd);
float dpiXX = graphics.DpiX;
return Convert.ToUInt32(dpiXX);
}
}
public static uint GetDpi(Window window, DpiType dpiType)
{
var hwnd = new WindowInteropHelper(window).Handle;
return GetDpi(hwnd, dpiType);
}
[DllImport("User32.dll")]
private static extern IntPtr MonitorFromPoint([In]System.Drawing.Point pt, [In]uint dwFlags);
[DllImport("Shcore.dll")]
private static extern IntPtr GetDpiForMonitor([In]IntPtr hmonitor, [In]DpiType dpiType, [Out]out uint dpiX, [Out]out uint dpiY);
public enum DpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
This code is used as part of a screen capture solution where there's supposed to be an overlay over the window the user's mouse is over. I capture the mouse position and based on that I get a pixel location and I then create the WPF window there. Here I have to apply the DPI ratio in order to get the Window to render in the right place and size.
This all works fine on the primary monitor or on multiple monitors as long as the DPI is the same.
The problem is that the call to GetDpiForMonitor() always returns the primary monitor DPI even though the HMONITOR value passed to it is different.
DPI Awareness
This is a WPF application so the app is DPI aware, but WPF runs in System DPI Awareness, rather than Per Monitor DPI Aware. To that effect I hooked up static App() code on startup to explicitly set to per monitor DPI:
try
{
// for this to work make sure [assembly:dpiawareness
PROCESS_DPI_AWARENESS awareness;
GetProcessDpiAwareness(Process.GetCurrentProcess().Handle, out awareness);
var result = SetProcessDpiAwareness(PROCESS_DPI_AWARENESS.Process_Per_Monitor_DPI_Aware);
GetProcessDpiAwareness(Process.GetCurrentProcess().Handle, out awareness);
}
[DllImport("SHCore.dll", SetLastError = true)]
public static extern bool SetProcessDpiAwareness(PROCESS_DPI_AWARENESS awareness);
[DllImport("SHCore.dll", SetLastError = true)]
public static extern void GetProcessDpiAwareness(IntPtr hprocess, out PROCESS_DPI_AWARENESS awareness);
public enum PROCESS_DPI_AWARENESS
{
Process_DPI_Unaware = 0,
Process_System_DPI_Aware = 1,
Process_Per_Monitor_DPI_Aware = 2
}
// and in assemblyinfo
[assembly: DisableDpiAwareness]
I see that the DPI setting changes to Process_Per_Monitor_DPI_Aware but that also seems to have no effect on the behavior. I still see the DPI results returned as the same as the main monitor.
There's a test in a largish solution that allows playing with this here:
https://github.com/RickStrahl/MarkdownMonster/blob/master/Tests/ScreenCaptureAddin.Test/DpiDetectionTests.cs in case anyone is interested in checking this out.
Any ideas how I can reliably get the DPI Scaling level for all monitors on the system (and why the heck is there no system API or even a WMI setting for this)?
WPF has per-monitor DPI support since .NET Framework 4.6.2. There is more information and an example available at GitHub: http://github.com/Microsoft/WPF-Samples/tree/master/PerMonitorDPI.
You may also want to check out the VisualTreeHelper.GetDpi method.
I have been wrestling with similar problems (secondary monitor Screen bounds seemed to be scaled by the same scale factor as set on the primary display), and I found some documentation that seems to at least explain that this is expected behavior:
DPI Awareness Mode - System
Windows Version Introduced - Vista
Application's view of DPI - All displays have the same DPI
(the DPI of the primary display at the time the Windows session was
started)
Behavior on DPI change - Bitmap-stretching (blurry)
This is extracted from the first table in High DPI desktop application development on Windows
That's the first documentation I found that at least explicitly spells out that the code will report that all windows share the same scaling when the application is under System DPI Awareness.
I am working on a project in c which i am trying to simulate chrome browsing.
my only difficult is to find a way to get links position on screen after i receive a handle to chrome window. anyone knows how to get the links position??
i am familiar with selenium and managed to do so in python but i require to do so in c too.
any ideas?
int wmain()
{
HWND hwnd = NULL;
for (;;)
{
hwnd = FindWindowEx(0, hwnd, L"Chrome_WidgetWin_1", 0);
if (!hwnd)
break;
////Get Links position
}
I'm developing a project that needs to show HTML that is returned by a third party service. I am currently using a WPF WebBrowser to show this output. However this creates a potential security problem in the eyes of my customers. When focus is set to this control you can open any webpage by using CTRL+O or open Internet Explorer by using Ctrl+N. My application is targeted for a kiosk-like environment (Terminal Services).
In the past I've used the WinForms WebBrowser control and was able to sink into the events through COM, however those tactics don't seem to work with the WPF version. My development partners are adamant that we develop a pure WPF application instead of mixing in the WinForms option.
Has anyone had success getting to the IWebBrowserEvets2COM interface of the WPF WebBrowser? I have been able cast the WebBrowser.Document to an IWebBrowser, but haven't got to where I need.
Please help me tap into the events so that I can stop the user from creating new windows and other events that might cause "security" problems for my clients. Or is there a better control out there to do the rendering of HTML and basic navigation?
Thanks in advance,
Jerod
You can try to use Windows hooks here. Find browser window and install proper keyboard hooks for it. This externals should be helpful for you:
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, IntPtr windowTitle);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr SetWindowsHookEx(Int32 idHook, HookProc lpfn, IntPtr hInstance, Int32 threadId);
And here is a sample how to use those, but you have to implement KeyboardHookProcedure according to your needs.
private IntPtr FindExplorerWindow()
{
IntPtr wnd = Browser.Handle;
if (wnd != IntPtr.Zero)
{
wnd = FindWindowEx(wnd, IntPtr.Zero, "Shell DocObject View", IntPtr.Zero);
if (wnd != IntPtr.Zero)
{
wnd = FindWindowEx(wnd, IntPtr.Zero, "Internet Explorer_Server", IntPtr.Zero);
return wnd;
}
}
return IntPtr.Zero;
}
private void InstallHook()
{
if (_hHook.ToInt32() > 0) return;
IntPtr wnd = FindExplorerWindow();
if (wnd != IntPtr.Zero)
{
if (_hookProc == null)
{
_hookProc = new HookProc(KeyboardHookProcedure);
}
_hHook = SetWindowsHookEx(WH_KEYBOARD, _hookProc, (IntPtr)0, GetCurrentThreadId());
}
}
Good luck!
I have a custom windows implementation in a WPF app that hooks WM_GETMINMAXINFO as follows:
private void MaximiseWithTaskbar(System.IntPtr hwnd, System.IntPtr lParam)
{
MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
System.IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (monitor != System.IntPtr.Zero)
{
MONITORINFO monitorInfo = new MONITORINFO();
GetMonitorInfo(monitor, monitorInfo);
RECT rcWorkArea = monitorInfo.rcWork;
RECT rcMonitorArea = monitorInfo.rcMonitor;
mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);
mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);
mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);
mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
mmi.ptMinTrackSize.x = Convert.ToInt16(this.MinWidth * (desktopDpiX / 96));
mmi.ptMinTrackSize.y = Convert.ToInt16(this.MinHeight * (desktopDpiY / 96));
}
Marshal.StructureToPtr(mmi, lParam, true);
}
It all works a treat and it allows me to have a borderless window maximized without having it sit on to of the task bar, which is great, but it really doesn't like being moved between monitors with the new Win7 keyboard shortcuts.
Whenever the app is moved with Win+Shift+Left/Right the WM_GETMINMAXINFO message is received, as I'd expect, but MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) returns the monitor the application has just been moved FROM, rather than the monitor it is moving TO, so if the monitors are of differing resolutions the window end up the wrong size.
I'm not sure if there's something else I can call, other then MonitorFromWindow, or whether there's a "moving monitors" message I can hook prior to WM_GETMINMAXINFO. I'm assuming there is a way to do it because "normal" windows work just fine.
According to the MSDN page the WM_GETMINMAXINFO is sent:
"when the size or position of the window is about to change"
which explains why your call to MonitorFromWindow returns the previous monitor.
How about using the WM_WINDOWPOSCHANGED message? It's sent after every time the window is moved. You can get the HWND from the WINDOWPOS structure and use that in a MonitorFromWindow call to get the monitor. When a subsequent WM_GETMINMAXINFO is sent you can use that as you do now.