With this code:
internal static List<DetectedWindow> EnumerateWindows()
{
var shellWindow = GetShellWindow();
var windows = new List<DetectedWindow>();
EnumWindows(delegate (IntPtr handle, int lParam)
{
if (handle == shellWindow)
return true;
if (!IsWindowVisible(handle))
return true;
if (IsIconic(handle))
return true;
var length = GetWindowTextLength(handle);
if (length == 0)
return true;
var builder = new StringBuilder(length);
GetWindowText(handle, builder, length + 1);
GetWindowRect(handle, out Rect rect);
windows.Add(new DetectedWindow(handle, rect.ToRect(), builder.ToString()));
return true;
}, IntPtr.Zero);
return windows;
}
Auxiliar class:
public class DetectedWindow
{
public IntPtr Handle { get; private set; }
public Rect Bounds { get; private set; }
public string Name { get; private set; }
public DetectedWindow(IntPtr handle, Rect bounds, string name)
{
Handle = handle;
Bounds = bounds;
Name = name;
}
}
I'm getting this list of applications (Window text - Rect bounds):
Microsoft Visual Studio - -8;-8;1936;1056
Microsoft Edge - 0;77;1920;963
EnumWindows - Stack Overflow and 7 more pages - Microsoft Edge - -8;-8;1936;1056
Microsoft Edge - 0;77;1920;963
Microsoft Edge - 0;77;1920;963
Microsoft Edge - 0;0;1920;1080
Microsoft Edge - 0;0;1920;1080
Microsoft Edge - 0;8;1920;1040
Microsoft Edge - 0;85;1920;963
Microsoft Edge - 150;79;1532;42
Microsoft Edge - 0;85;1920;963
Microsoft Edge - 0;77;1920;963
Microsoft Edge - 0;85;1920;963
Microsoft Edge - 0;213;1920;964
Microsoft Edge - 0;0;1920;1080
Microsoft Edge - 484;208;952;174
Microsoft Edge - 0;84;1920;964
Microsoft Edge - 0;84;1920;964
Microsoft Edge - 0;84;1920;964
Microsoft Edge - 0;0;1920;1080
Mail - 0;32;1356;693
Mail - 278;252;1372;733
OneNote - 0;8;1920;1040
My notes - OneNote - -8;-8;1936;1056
Photos - 0;32;1920;1008
Photos - -8;-8;1936;1056
Skype - 0;40;1920;1008
Skype - -8;-8;1936;1056
Store - 0;40;1920;1008
Store - -8;-8;1936;1056
Movies & TV - 0;0;1920;1080
Movies & TV - -8;-8;1936;1056
Groove Music - 0;32;1466;712
Groove Music - -7;3;1372;733
Settings - 0;40;1920;1008
Settings - -8;-8;1936;1056
Windows Shell Experience Host - 0;0;1920;1080
My current not minimized windows are Visual Studio and two Edge windows (with several tabs each). I can understand the fact that only one Edge item was listing the title of the current page. Because I recently recovered from a crash and only that page was loaded.
My questions are:
Why are my closed Windows Store apps being listed? (and even twice)
Why are my Edge tabs being listed?
How can I filter the Edge tabs and the closed Windows Store apps?
EDIT:
By "Filter": Only retrieve the apps with visible window. With my use case, only 3 windows are visible.
I tried to get the WsStyle and WsEXStyle of each window to compare, but I couldn't find any difference.
The method IsWindowVisible() fails filter out the Windows Store apps that are not visible.
Why are my closed Windows Store apps being listed?
Because they are not actually closed. Easy to see with Task Manager, Processes tab. You'll see that the process that owns these windows is suspended. Part of the WinRT (aka UWP, aka Store, aka Modern UI, aka Metro) programming framework approach, modern machines have enough RAM to make it feasible to keep processes running even if the user doesn't interact with them. Brings them back quickly again and saves battery life. If RAM is needed elsewhere then the OS will salvage it by killing such a process.
Why are my Edge tabs being listed?
Because Edge is a WinRT app as well.
How can I filter the Edge tabs and the closed Windows Store apps?
It is not entirely clear by which property you want to filter, given that the window is in fact not closed. GetWindowThreadProcessId() and IsImmersiveProcess() can tell you that you are dealing with such a process. Consider IsWindowVisible(). Maybe this post can help, also tells you why you see multiple windows.
Edit (Nicke Manarin):
By checking the Cloacked attribute, it is possible to ignore the hidden/background Store apps:
DwmGetWindowAttribute(handle, (int)DwmWindowAttribute.Cloaked, out bool isCloacked, Marshal.SizeOf(typeof(bool)));
if (isCloacked)
return true;
Edit 2 (Nicke Manarin):
Each Edge tab behaves like a window (I believe it has something to do with the fact that you can drag the tab to create a new window).
I can't reproduce the behavior in my W10 Desktop. After using your code, also filtering out the WsEXStyle WS_EX_TOOLWINDOW, shows the same apps than alt+tab does. I opened and closed Edge and Photos and they didn't appear when closed anymore. Maybe it was tinkering with P/Invoke that triggered that behavior. Does it continue with the code below after a restart?.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
public class EnumerateWindowsTest
{
[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
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(IntPtr hWnd, out Rect lpRect);
[DllImport("user32.dll")]
static extern IntPtr GetShellWindow();
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
public delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll")]
static extern int EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsIconic(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern System.UInt32 GetWindowLong(IntPtr hWnd, int nIndex);
internal static List<DetectedWindow> EnumerateWindows()
{
var shellWindow = GetShellWindow();
var windows = new List<DetectedWindow>();
EnumWindows(delegate (IntPtr handle, IntPtr lParam)
{
if (handle == shellWindow)
return true;
if (!IsWindowVisible(handle))
return true;
if (IsIconic(handle))
return true;
if (HasSomeExtendedWindowsStyles(handle))
return true;
var length = GetWindowTextLength(handle);
if (length == 0)
return true;
var builder = new StringBuilder(length);
GetWindowText(handle, builder, length + 1);
GetWindowRect(handle, out Rect rect);
windows.Add(new DetectedWindow(handle, rect, builder.ToString()));
return true;
}, IntPtr.Zero);
return windows;
}
static bool HasSomeExtendedWindowsStyles(IntPtr hwnd)
{
const int GWL_EXSTYLE = -20;
const uint WS_EX_TOOLWINDOW = 0x00000080U;
uint i = GetWindowLong(hwnd, GWL_EXSTYLE);
if ((i & (WS_EX_TOOLWINDOW)) != 0)
{
return true;
}
return false;
}
}
public class DetectedWindow
{
public IntPtr Handle { get; private set; }
public EnumerateWindowsTest.Rect Bounds { get; private set; }
public string Name { get; private set; }
public DetectedWindow(IntPtr handle, EnumerateWindowsTest.Rect bounds, string name)
{
Handle = handle;
Bounds = bounds;
Name = name;
}
}
class Program
{
static void DetectWindows()
{
foreach (DetectedWindow w in EnumerateWindowsTest.EnumerateWindows())
{
Console.WriteLine("{0} - {1};{2};{3};{4}",w.Name,w.Bounds.Left,w.Bounds.Top,w.Bounds.Right,w.Bounds.Bottom);
}
}
static void Main(string[] args)
{
DetectWindows();
Console.ReadLine();
}
}
}
I have made simple window filter method that include Microsoft store apps that are cloaked. They are also identified by the class names ApplicationFrameWindow and Windows.UI.Core.CoreWindow
public static class WindowFilter
{
public static bool NormalWindow(IWindow window)
{
if (IsHiddenWindowStoreApp(window, window.ClassName)) return false;
return !window.Styles.IsToolWindow && window.IsVisible;
}
private static bool IsHiddenWindowStoreApp(IWindow window, string className)
=> (className == "ApplicationFrameWindow" || className == "Windows.UI.Core.CoreWindow") && window.IsCloaked;
}
The above example is part of a project of github, were you can see the rest of the code.
https://github.com/mortenbrudvik/WindowExplorer
Related
I followed Huw Collingbourne program in creating a screen capture program with C#. However, I noticed a couple of weird items, and whether I use his created program or my modified program does the same. Specifically I created a program that opens a window that allows you to capture the area.
I think it has to do with sittings on my computer, but need to know how to anticipate and fix this if others are going to use my screen capture program!
If my display for windows 10 is set to 100% I get a little more than the selected window and if I set display to 125% text then I get a lot of the selected area. Leaving at default size I should be 555, 484 in size. but I capture much larger.
public partial class Form1 : Form
{
//https://learn.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getwindowrect
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, ref Rectangle lpRect);
//ICON info
//https://learn.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getcursorinfo
[DllImport("user32.dll")]
private static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);
[DllImport("user32.dll")]
private static extern bool GetCursorInfo(out CURSORINFO pci);
public struct POINT
{
public Int32 x;
public Int32 y;
}
public struct ICONINFO
{
public bool fIcon;
public Int32 xHotspot;
public Int32 yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
public struct CURSORINFO
{
public Int32 cbSize;
public Int32 flags;
public IntPtr hCursor;
public Point ptScreenPos;
}
GrabRegionForm grabForm;
public void GrabRect(Rectangle rect)
{
int rectWidth = rect.Width - rect.Left;
int rectHeight = rect.Height - rect.Top;
Bitmap bm = new Bitmap(rectWidth, rectHeight);
Graphics g = Graphics.FromImage(bm);
g.CopyFromScreen(rect.Left, rect.Top, 0, 0, new Size(rectWidth, rectHeight));
DrawMousePointer(g, Cursor.Position.X - rect.Left, Cursor.Position.Y - rect.Top);
this.pb_screengrab.Image = bm;
Clipboard.SetImage(bm);
}
}
public partial class GrabRegionForm : Form
{
public Rectangle formRect;
private Form1 mainForm;
[DllImport("user32.dll")]
public static extern bool ReleaseCapture();
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
private void buttonOK_Click(object sender, EventArgs e)
{
formRect = new Rectangle(this.Left, this.Top, this.Left + this.Width, this.Top + this.Height);
this.Hide();
mainForm.GrabRect(formRect);
Close();
}
}
ScreenGrab with Display at 100%
ScreenGrab with Display at 125%
Area showing capture window
Area Actually Captured
IF using earlier than 4.7 and not windows 10 follow Jimi's examples and ensure you sign out and back into windows.
From Jimi https://stackoverflow.com/users/7444103/jimi
How to configure an app to run correctly on a machine with a high DPI setting How to configure an app to run correctly on a machine with a high DPI setting (e.g. 150%)?
From Jimi https://stackoverflow.com/users/7444103/jimi
Using SetWindowPos with multiple monitors Using SetWindowPos with multiple monitors
If I target my app for windows 10 Only it is incredibly simple now.
Microsoft made it even easier to change DPI settings with using 4.7 if using Windows 10.
https://learn.microsoft.com/en-us/dotnet/framework/winforms/high-dpi-support-in-windows-forms
Declare compatibility with Windows 10.
Then add the following to the app.manifest file in the XML under the commend for Windows 10 compatibility.
supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"
Enable per-monitor DPI awareness in the app.config file.
Windows Forms introduces a new element to support new features and customizations added starting with the .NET Framework 4.7. To take advantage of the new features that support high DPI, add the following to your application configuration file.
Go to the XML line for System.Windows.Forms.ApplicationConfigurationSection
add key="DpiAwareness" value="PerMonitorV2"
I am trying to find out if the windows 10 virtual touch keyboard is visible or not to know whether to open it or not from my application. THe following code has worked fine up until the latest Windows 10 update 15063 or possible the one right before it. Seems like Microsoft changed something with the window styles possibly but I can't figure it out.
public static bool IsKeyboardVisible()
{
IntPtr keyboardHandle = GetKeyboardWindowHandle();
// Specifies we wish to retrieve window styles.
int GWL_STYLE = -16;
//The window is disabled. See http://msdn.microsoft.com/en-gb/library/windows/desktop/ms632600(v=vs.85).aspx.
UInt32 WS_VISIBLE = 0x10000000;
UInt32 WS_DISABLED = 0x08000000;
UInt32 WS_POPUP = 0x80000000;
bool visible = false;
bool disabled = false;
if (keyboardHandle != IntPtr.Zero)
{
UInt32 style = GetWindowLong(keyboardHandle, GWL_STYLE);
visible = ((style & WS_VISIBLE) == WS_VISIBLE);
disabled = ((style & WS_DISABLED) == WS_DISABLED); // ref https://stackoverflow.com/questions/11065026/get-window-state-of-another-process
log.InfoFormat("style:{0:X4} visible:{1} disabled:{2}", style, visible, disabled);
}
return visible && !disabled ;
}
This is related to: Show touch keyboard (TabTip.exe) in Windows 10 Anniversary edition
I've done some research with Spy++ . Looks like the new keyboard in Fall Creators Update (ver. 1709) is hosted by another window. This window has Windows.UI.Core.CoreWindow class and Microsoft Text Input Application as its title.
The following code works for all Windows 10 versions including the new 1803 and older Windows versions as well (starting with Windows 8, I believe).
static class TouchKeyboard
{
public static bool GetIsOpen()
{
return GetIsOpen1709() ?? GetIsOpenLegacy();
}
private static bool? GetIsOpen1709()
{
var parent = IntPtr.Zero;
for (;;)
{
parent = FindWindowEx(IntPtr.Zero, parent, WindowParentClass1709);
if (parent == IntPtr.Zero)
return null; // no more windows, keyboard state is unknown
// if it's a child of a WindowParentClass1709 window - the keyboard is open
var wnd = FindWindowEx(parent, IntPtr.Zero, WindowClass1709, WindowCaption1709);
if (wnd != IntPtr.Zero)
return true;
}
}
private static bool GetIsOpenLegacy()
{
var wnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, WindowClass);
if (wnd == IntPtr.Zero)
return false;
var style = GetWindowStyle(wnd);
return style.HasFlag(WindowStyle.Visible)
&& !style.HasFlag(WindowStyle.Disabled);
}
private const string WindowClass = "IPTip_Main_Window";
private const string WindowParentClass1709 = "ApplicationFrameWindow";
private const string WindowClass1709 = "Windows.UI.Core.CoreWindow";
private const string WindowCaption1709 = "Microsoft Text Input Application";
private enum WindowStyle : uint
{
Disabled = 0x08000000,
Visible = 0x10000000,
}
private static WindowStyle GetWindowStyle(IntPtr wnd)
{
return (WindowStyle)GetWindowLong(wnd, -16);
}
[DllImport("user32.dll", SetLastError = false)]
private static extern IntPtr FindWindowEx(IntPtr parent, IntPtr after, string className, string title = null);
[DllImport("user32.dll", SetLastError = false)]
private static extern uint GetWindowLong(IntPtr wnd, int index);
}
Update: I updated the answer and the code to be compatible with Redstone 4 (v1803) as well.
I'm using this solution, and it is working on Windows 1607, 1709 and 1803 (check the Main method below on the code):
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
class Program
{
[ComImport, Guid("D5120AA3-46BA-44C5-822D-CA8092C1FC72")]
public class FrameworkInputPane
{
}
[ComImport, System.Security.SuppressUnmanagedCodeSecurity,
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("5752238B-24F0-495A-82F1-2FD593056796")]
public interface IFrameworkInputPane
{
[PreserveSig]
int Advise(
[MarshalAs(UnmanagedType.IUnknown)] object pWindow,
[MarshalAs(UnmanagedType.IUnknown)] object pHandler,
out int pdwCookie
);
[PreserveSig]
int AdviseWithHWND(
IntPtr hwnd,
[MarshalAs(UnmanagedType.IUnknown)] object pHandler,
out int pdwCookie
);
[PreserveSig]
int Unadvise(
int pdwCookie
);
[PreserveSig]
int Location(
out Rectangle prcInputPaneScreenLocation
);
}
static void Main(string[] args)
{
var inputPane = (IFrameworkInputPane)new FrameworkInputPane();
inputPane.Location(out var rect);
Console.WriteLine((rect.Width == 0 && rect.Height == 0) ? "Keyboard not visible" : "Keyboard visible");
}
}
}
It uses the IFrameworkInputPane interface (https://learn.microsoft.com/en-us/windows/desktop/api/shobjidl_core/nn-shobjidl_core-iframeworkinputpane)
I discovered yet another undocumented COM API that returns the position of the touch keyboard. It returns the bounds of the keyboard window or zeroes if the keyboard is hidden. I tested it in Windows 8.1, Windows 10 and Windows 10 Fall Creators Update and it works fine.
Now some bad news: in all versions prior to Fall Creators Update it only reports accurate results if the active window and the touch keyboard are located on the same monitor. If this is not the case - the API just returns the previous cached value. I'm guessing it has something to do with the fact that this API was meant to be used to calculate occlusion of the touch keyboard and your app's window. (It's called inside Windows::UI::ViewManagement::InputPane.get_OccludedRect() UWP API).
So if you don't care about supporting older versions or multi-monitor scenarios - use it. Otherwise I would suggest checking the Windows version and falling back to the previous method (GetIsOpenLegacy() from my other answer).
The API:
[ComImport, Guid("228826af-02e1-4226-a9e0-99a855e455a6")]
class ImmersiveShellBroker
{
}
[ComImport, Guid("9767060c-9476-42e2-8f7b-2f10fd13765c")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IImmersiveShellBroker
{
void Dummy();
IInputHostManagerBroker GetInputHostManagerBroker();
}
[ComImport, Guid("2166ee67-71df-4476-8394-0ced2ed05274")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IInputHostManagerBroker
{
void GetIhmLocation(out Rect rect, out DisplayMode mode);
}
[StructLayout(LayoutKind.Sequential)]
struct Rect
{
public int Left, Top, Right, Bottom;
}
enum DisplayMode
{
NotSupported = 0,
Floating = 2,
Docked = 3,
}
Usage example:
// do this once:
var brokerClass = new ImmersiveShellBroker();
var broker = (IImmersiveShellBroker)brokerClass;
var ihm = broker.GetInputHostManagerBroker();
Marshal.ReleaseComObject(broker);
// now ihm reference can be cached and used later:
Rect rect;
DisplayMode mode;
ihm.GetIhmLocation(out rect, out mode);
Note: looks like GetIhmLocation() always returns DisplayMode.NotSupported instead of the actual mode prior to Windows 10.
How can I show/hide the desktop icons programmatically, using C#?
I'm trying to create an alternative desktop, which uses widgets, and I need to hide the old icons.
You can do this using the Windows API. Here is sample code in C# that will toggle desktop icons.
[DllImport("user32.dll", SetLastError = true)] static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)] static extern IntPtr GetWindow(IntPtr hWnd, GetWindow_Cmd uCmd);
enum GetWindow_Cmd : uint
{
GW_HWNDFIRST = 0,
GW_HWNDLAST = 1,
GW_HWNDNEXT = 2,
GW_HWNDPREV = 3,
GW_OWNER = 4,
GW_CHILD = 5,
GW_ENABLEDPOPUP = 6
}
[DllImport("user32.dll", CharSet = CharSet.Auto)] static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
private const int WM_COMMAND = 0x111;
static void ToggleDesktopIcons()
{
var toggleDesktopCommand = new IntPtr(0x7402);
IntPtr hWnd = GetWindow(FindWindow("Progman", "Program Manager"), GetWindow_Cmd.GW_CHILD);
SendMessage(hWnd, WM_COMMAND, toggleDesktopCommand, IntPtr.Zero);
}
This sends a message to the SHELLDLL_DefView child window of Progman, which tells it to toggle visibility (by adding or removing the WS_VISIBLE style) of it's only child, "FolderView". "FolderView" is the actual window that contains the icons.
To test to see if icons are visible or not, you can query for the WS_VISIBLE style by using the GetWindowInfo function, shown below:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
private static extern bool GetWindowInfo(IntPtr hwnd, ref WINDOWINFO pwi);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
private int _Left;
private int _Top;
private int _Right;
private int _Bottom;
}
[StructLayout(LayoutKind.Sequential)]
struct WINDOWINFO
{
public uint cbSize;
public RECT rcWindow;
public RECT rcClient;
public uint dwStyle;
public uint dwExStyle;
public uint dwWindowStatus;
public uint cxWindowBorders;
public uint cyWindowBorders;
public ushort atomWindowType;
public ushort wCreatorVersion;
public WINDOWINFO(Boolean? filler)
: this() // Allows automatic initialization of "cbSize" with "new WINDOWINFO(null/true/false)".
{
cbSize = (UInt32)(Marshal.SizeOf(typeof(WINDOWINFO)));
}
}
Here is a function that calls the above code and returns true if the window is visible, false if not.
static bool IsVisible()
{
IntPtr hWnd = GetWindow(GetWindow(FindWindow("Progman", "Program Manager"), GetWindow_Cmd.GW_CHILD), GetWindow_Cmd.GW_CHILD);
WINDOWINFO info = new WINDOWINFO();
info.cbSize = (uint)Marshal.SizeOf(info);
GetWindowInfo(hWnd, ref info);
return (info.dwStyle & 0x10000000) == 0x10000000;
}
The windows API code along with more information about the window styles can be found here: http://www.pinvoke.net/default.aspx/user32/GetWindowInfo.html
Even though this is quite old when I tried Ondrej Balas's answer, one problem I found with this solution is that it does not work if the ToggleDesktop command is used to show the desktop ( also if wallpaper rotation is enabled ).
In both of these cases the SHELLDLL_DefView window, which is the recipient of the toggleDesktopCommand in the ToggleDesktopIcons function, is not a child of the "Program manager" window but of a 'WorkerW" window. (see WinApi - How to obtain SHELLDLL_DefView and Windows Desktop ListView Handle.
Based on those and building upon Ondrej Balas's earlier answer change the ToggleDesktopIcons function to be :
static void ToggleDesktopIcons()
{
var toggleDesktopCommand = new IntPtr(0x7402);
SendMessage(GetDesktopSHELLDLL_DefView(), WM_COMMAND, toggleDesktopCommand, IntPtr.Zero);
}
And add a GetDesktopSHELLDLL_DefView function:
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
[DllImport("user32.dll", SetLastError = false)]
static extern IntPtr GetDesktopWindow();
static IntPtr GetDesktopSHELLDLL_DefView()
{
var hShellViewWin = IntPtr.Zero;
var hWorkerW = IntPtr.Zero;
var hProgman = FindWindow("Progman", "Program Manager");
var hDesktopWnd = GetDesktopWindow();
// If the main Program Manager window is found
if (hProgman != IntPtr.Zero)
{
// Get and load the main List view window containing the icons.
hShellViewWin = FindWindowEx(hProgman, IntPtr.Zero, "SHELLDLL_DefView", null);
if (hShellViewWin == IntPtr.Zero)
{
// When this fails (picture rotation is turned ON, toggledesktop shell cmd used ), then look for the WorkerW windows list to get the
// correct desktop list handle.
// As there can be multiple WorkerW windows, iterate through all to get the correct one
do
{
hWorkerW = FindWindowEx(hDesktopWnd, hWorkerW, "WorkerW", null);
hShellViewWin = FindWindowEx(hWorkerW, IntPtr.Zero, "SHELLDLL_DefView", null);
} while (hShellViewWin == IntPtr.Zero && hWorkerW != IntPtr.Zero);
}
}
return hShellViewWin;
}
Now regardless of the desktop toggle or wallpaper rotation the ToggleDesktopIcons should always work.
For reference this is my toggle desktop function which caused the issue with the original ToggleDesktopIcons function
static public void ToggleDesktop(object sender, EventArgs e)
{
var shellObject = new Shell32.Shell();
shellObject.ToggleDesktop();
}
In response to James M, this function returns the current state:
bool IconsVisible()
{
var hWnd = GetDesktopListView();
var info = new User32.WINDOWINFO(null);
User32.GetWindowInfo(hWnd, ref info);
return (info.dwStyle & User32.WindowStyle.WS_VISIBLE) == User32.WindowStyle.WS_VISIBLE;
}
A different approach is to create a separate desktop and show it instead. It will not have icons.
Application running itself on a separate desktop
You can do this in RegEdit
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced
change HideIcons to 1
static void HideIcons()
{
RegistryKey myKey = Registry.CurrentUser.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced", true);
if (myKey != null)
{
myKey.SetValue("HideIcons", 1);
myKey.Close();
}
}
Use the Registry class as described here.
http://msdn.microsoft.com/en-us/library/microsoft.win32.registry.aspx
You can create a full screen view application and make it the top most window.
Then make your application to be start up with windows.
You are going about this the wrong way. What you are really trying to do is to replace the shell. Windows provides for this so you should just take advantage of it. Write your own shell to replace explorer.
Nice topic. Without actually creating a different desktop it would be visually pleasant to have the running applications minimized in the same swoop.
I have a ListView in a Winform User Control. The VirtualMode is true and the VirtualListSize is 200. When there are less items in the ListView than visible rows, I get weird characters below the (real) items. These "artifacts" appear when the software is run on Windows 8, 10 or Windows Server 2012, but not on Windows 7.
Does anyone know what could be causing these "artifacts"? I added a character "A" "B", etc. to the Title of all the places where ListViewItems are created. So I know that none of the code in this user control is creating them. I added a sample solution that shows the problem below.
Sometimes they appear as chinese characters, sometimes just a random letter and character combination. Usually they are not longer than 4 characters.
[Update] It does not occur on the latest Version of Windows 10.
[Update2] I was able to reproduce the problem on a small sample solution. Find the zip file here.
I ended up assigning the number of visible lines of the ListView to the VirtualListSize property, whenever the number of items in the ListView changed.
I used the following class to determine the number of visible lines in the ListView:
public static class ListViewSizer
{
const UInt32 LVM_FIRST = 0x1000;
const UInt32 LVM_GETHEADER = (LVM_FIRST + 31);
private static int GetHeaderHeight(ListView listView)
{
Rect rect = new Rect();
IntPtr hwnd = SendMessage((IntPtr)listView.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
if (hwnd != null)
{
if (GetWindowRect(new System.Runtime.InteropServices.HandleRef(null, hwnd), out rect))
{
return rect.Bottom - rect.Top;
}
}
return -1;
}
public static int GetLastVisibleLine(ListView listView)
{
int firstVisibleIndex = listView.TopItem.Index;
int heightOfFirstItem = listView.GetItemRect(firstVisibleIndex, ItemBoundsPortion.Entire).Height;
// assuming each item has the same height (I think this is true for list view and details view)
const int DefaultVisibleLines = 11;
int visibleLines = heightOfFirstItem != 0 ? (int)Math.Ceiling((decimal)((listView.Height - GetHeaderHeight(listView)) / heightOfFirstItem)) : DefaultVisibleLines;
int lastVisibleIndexInDetailsMode = firstVisibleIndex + visibleLines;
return lastVisibleIndexInDetailsMode;
}
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool GetWindowRect(System.Runtime.InteropServices.HandleRef hwnd, out Rect lpRect);
[Serializable, System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
}
I know this is a bit of a hack. The artifacts would still be there, but you just can't see them anymore, because the ListView always has as many lines as can be displayed. This is the best I could come up with without changing the control.
As the title says, I want to position it to the bottom left corner of the screen. Here's the code I have so far:
Console.WindowWidth = 50
Console.WindowHeight = 3
Console.BufferWidth = 50
Console.BufferHeight = 3
Console.BackgroundColor = ConsoleColor.Black
Console.ForegroundColor = ConsoleColor.DarkMagenta
Console.Title = "My Title"
Console.WriteLine("")
Console.Write(" Press any key to close this window ...")
Console.ReadKey()
Note: Despite their names, setting Console.WindowLeft and Console.WindowTop of the System.Console class does not change the window's position on screen.
Instead, they position the visible part of the window relative to the (potentially larger) window buffer - you cannot use type System.Console to change the position of console windows on the screen - you need to use the Windows API for that.
The following is code for a complete console application that positions its own window in the lower left corner of the screen, respecting the location of the taskbar.
Note:
It should work with multi-monitor setups - positioning the window on the specific monitor (display, screen) it is (mostly) being displayed on - but I haven't personally verified it.
Only Windows API functions are used via P/Invoke declarations, avoiding the need for referencing the WinForms assembly (System.Windows.Forms), which is not normally needed in console applications.
You'll see that a good portion of the code is devoted to P/Invoke signatures (declaration) for interfacing with the native Windows APIs; these were gratefully adapted from pinvoke.net
The actual code in the Main() method is short by comparison.
If you compile the code below from a console-application project in Visual Studio and run the resulting executable from a cmd.exe console window (Command Prompt), that console window should shift to the lower left corner of the (containing screen).
To verify the functionality while running from Visual Studio, place a breakpoint at the closing } and, when execution pauses, Alt-Tab to the console window to verify its position.
using System;
using System.Runtime.InteropServices; // To enable P/Invoke signatures.
public static class PositionConsoleWindowDemo
{
// P/Invoke declarations.
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
const int MONITOR_DEFAULTTOPRIMARY = 1;
[DllImport("user32.dll")]
static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
[StructLayout(LayoutKind.Sequential)]
struct MONITORINFO
{
public uint cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
public static MONITORINFO Default
{
get { var inst= new MONITORINFO(); inst.cbSize = (uint)Marshal.SizeOf(inst); return inst; }
}
}
[StructLayout(LayoutKind.Sequential)]
struct RECT
{
public int Left, Top, Right, Bottom;
}
[StructLayout(LayoutKind.Sequential)]
struct POINT
{
public int x, y;
}
[DllImport("user32.dll", SetLastError = true)]
static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
[DllImport("user32.dll", SetLastError = true)]
static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
const uint SW_RESTORE= 9;
[StructLayout(LayoutKind.Sequential)]
struct WINDOWPLACEMENT
{
public uint Length;
public uint Flags;
public uint ShowCmd;
public POINT MinPosition;
public POINT MaxPosition;
public RECT NormalPosition;
public static WINDOWPLACEMENT Default
{
get
{
var instance = new WINDOWPLACEMENT();
instance.Length = (uint) Marshal.SizeOf(instance);
return instance;
}
}
}
public static void Main()
{
// Get this console window's hWnd (window handle).
IntPtr hWnd = GetConsoleWindow();
// Get information about the monitor (display) that the window is (mostly) displayed on.
// The .rcWork field contains the monitor's work area, i.e., the usable space excluding
// the taskbar (and "application desktop toolbars" - see https://msdn.microsoft.com/en-us/library/windows/desktop/ms724947(v=vs.85).aspx)
var mi = MONITORINFO.Default;
GetMonitorInfo(MonitorFromWindow(hWnd, MONITOR_DEFAULTTOPRIMARY), ref mi);
// Get information about this window's current placement.
var wp = WINDOWPLACEMENT.Default;
GetWindowPlacement(hWnd, ref wp);
// Calculate the window's new position: lower left corner.
// !! Inexplicably, on W10, work-area coordinates (0,0) appear to be (7,7) pixels
// !! away from the true edge of the screen / taskbar.
int fudgeOffset = 7;
wp.NormalPosition = new RECT() {
Left = -fudgeOffset,
Top = mi.rcWork.Bottom - (wp.NormalPosition.Bottom - wp.NormalPosition.Top),
Right = (wp.NormalPosition.Right - wp.NormalPosition.Left),
Bottom = fudgeOffset + mi.rcWork.Bottom
};
// Place the window at the new position.
SetWindowPlacement(hWnd, ref wp);
}
}
You can use Console.WindowTop and Console.WindowWidth of the System.Console class to set the location of the console window.
Here is an example on MSDN
The BufferHeight and BufferWidth property gets/sets the number of rows and columns to be displayed.
WindowHeight and WindowWidth properties must always be less than BufferHeight and BufferWidth respectively.
WindowLeft must be less than BufferWidth - WindowWidth and WindowTop must be less than BufferHeight - WindowHeight.
WindowLeft and WindowTop are relative to the buffer.
To move the actual console window, this article has a good example.
I have used some of your code and code from the CodeProject sample. You can set window location and size both in a single function. No need to set Console.WindowHeight and Console.WindowWidth again. This is how my class looks:
class Program
{
const int SWP_NOZORDER = 0x4;
const int SWP_NOACTIVATE = 0x10;
[DllImport("kernel32")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter,
int x, int y, int cx, int cy, int flags);
static void Main(string[] args)
{
Console.WindowWidth = 50;
Console.WindowHeight = 3;
Console.BufferWidth = 50;
Console.BufferHeight = 3;
Console.BackgroundColor = ConsoleColor.Black;
Console.ForegroundColor = ConsoleColor.DarkMagenta;
var screen = System.Windows.Forms.Screen.PrimaryScreen.Bounds;
var width = screen.Width;
var height = screen.Height;
SetWindowPosition(100, height - 300, 500, 100);
Console.Title = "My Title";
Console.WriteLine("");
Console.Write(" Press any key to close this window ...");
Console.ReadKey();
}
/// <summary>
/// Sets the console window location and size in pixels
/// </summary>
public static void SetWindowPosition(int x, int y, int width, int height)
{
SetWindowPos(Handle, IntPtr.Zero, x, y, width, height, SWP_NOZORDER | SWP_NOACTIVATE);
}
public static IntPtr Handle
{
get
{
//Initialize();
return GetConsoleWindow();
}
}
}
Run any console application
Click RMB on headline.
Choose properties option.
choose Position tab.
Uncheck box "Automatic choice"
Set console position as you want.