Enumerate windows like alt-tab does - c#

I'm creating an alt-tab replacement for Vista but I have some problems listing all active programs.
I'm using EnumWindows to get a list of Windows, but this list is huge. It contains about 400 items when I only have 10 windows open. It seems to be a hwnd for every single control and a lot of other stuff.
So I have to filter this list somehow, but I can't manage to do it exactly as alt-tab does.
This is the code I use to filter the list right now. It works pretty well, but I get some unwanted windows like detached tool-windows in Visual Studio and I also miss windows like iTunes and Warcraft3.
private bool ShouldWindowBeDisplayed(IntPtr window)
{
uint windowStyles = Win32.GetWindowLong(window, GWL.GWL_STYLE);
if (((uint)WindowStyles.WS_VISIBLE & windowStyles) != (uint)WindowStyles.WS_VISIBLE ||
((uint)WindowExStyles.WS_EX_APPWINDOW & windowStyles) != (uint)WindowExStyles.WS_EX_APPWINDOW)
{
return true;
}
return false;
}

Raymond Chen answered this a while back
(https://devblogs.microsoft.com/oldnewthing/20071008-00/?p=24863):
It's actually pretty simple although
hardly anything you'd be able to guess
on your own. Note: The details of this
algorithm are an implementation
detail. It can change at any time, so
don't rely on it. In fact, it already
changed with Flip and Flip3D; I'm just
talking about the Classic Alt+Tab
window here.
For each visible window, walk up its
owner chain until you find the root
owner. Then walk back down the visible
last active popup chain until you find
a visible window. If you're back to
where you're started, then put the
window in the Alt+Tab list. In
pseudo-code:
BOOL IsAltTabWindow(HWND hwnd)
{
// Start at the root owner
HWND hwndWalk = GetAncestor(hwnd, GA_ROOTOWNER);
// See if we are the last active visible popup
HWND hwndTry;
while ((hwndTry = GetLastActivePopup(hwndWalk)) != hwndTry) {
if (IsWindowVisible(hwndTry)) break;
hwndWalk = hwndTry;
}
return hwndWalk == hwnd;
}
Follow the link to Chen's blog entry for more details and some corner conditions.

Thanks Mike B.
The example from Raymonds blog pointed me in the correct direction.
There are however some exceptions that has to be made, Windows Live messenger got alot of hacks for creating shadows under windows etc :#
Here is my complete code, have been using it for one day now and havn't noticed any differences from the real alt tab. There's some underlying code not posted but it's no problem figuring out what it does. :)
private static bool KeepWindowHandleInAltTabList(IntPtr window)
{
if (window == Win32.GetShellWindow()) //Desktop
return false;
//http://stackoverflow.com/questions/210504/enumerate-windows-like-alt-tab-does
//http://blogs.msdn.com/oldnewthing/archive/2007/10/08/5351207.aspx
//1. For each visible window, walk up its owner chain until you find the root owner.
//2. Then walk back down the visible last active popup chain until you find a visible window.
//3. If you're back to where you're started, (look for exceptions) then put the window in the Alt+Tab list.
IntPtr root = Win32.GetAncestor(window, Win32.GaFlags.GA_ROOTOWNER);
if (GetLastVisibleActivePopUpOfWindow(root) == window)
{
WindowInformation wi = new WindowInformation(window);
if (wi.className == "Shell_TrayWnd" || //Windows taskbar
wi.className == "DV2ControlHost" || //Windows startmenu, if open
(wi.className == "Button" && wi.windowText == "Start") || //Windows startmenu-button.
wi.className == "MsgrIMEWindowClass" || //Live messenger's notifybox i think
wi.className == "SysShadow" || //Live messenger's shadow-hack
wi.className.StartsWith("WMP9MediaBarFlyout")) //WMP's "now playing" taskbar-toolbar
return false;
return true;
}
return false;
}
private static IntPtr GetLastVisibleActivePopUpOfWindow(IntPtr window)
{
IntPtr lastPopUp = Win32.GetLastActivePopup(window);
if (Win32.IsWindowVisible(lastPopUp))
return lastPopUp;
else if (lastPopUp == window)
return IntPtr.Zero;
else
return GetLastVisibleActivePopUpOfWindow(lastPopUp);
}

Good work vhanla. My Pascal is a bit rusty, but your solution was a great help. I'm new to this, so please excuse my code and/or way of expressing things. The answer is relatively simple.
To make the list of Alt-Tab Windows, it appears you need three criteria
1) The Window must be visible - using GetWindowVisible
2) The Window must not be a Tool bar Window - using GetWindowInfo
3) The Window must not be cloaked - using DwmGetWindowAttribute
I don't believe that you need to look at class names. I think the WS_EX_APPWINDOW flag often fails the test (e.g. Chrome) - even when used in conjunction with the WS_EX_TOOLWINDOW. Also ... I don't think you need to look at the parent Window if you are enumerating Windows at the top level.
public static bool IsAltTabWindow(IntPtr hWnd)
{
const uint WS_EX_TOOLWINDOW = 0x00000080;
const uint DWMWA_CLOAKED = 14;
// It must be a visible Window
if (!IsWindowVisible(hWnd)) return false;
// It must not be a Tool bar window
WINDOWINFO winInfo = new WINDOWINFO(true);
GetWindowInfo(hWnd, ref winInfo);
if ((winInfo.dwExStyle & WS_EX_TOOLWINDOW) != 0) return false;
// It must not be a cloaked window
uint CloakedVal;
DwmGetWindowAttribute(hWnd, DWMWA_CLOAKED, out CloakedVal, sizeof(uint));
return CloakedVal == 0;
}

This is a function in pascal/delphi, you can easily translate it to C#.
It includes support for Windows 10 applications.
EnumWindows(#ListApps, 0);
function ListApps(LHWindow: HWND; lParam: Pointer): Boolean; stdcall;
var
LHDesktop: HWND;
LHParent: HWND;
LExStyle: DWORD;
AppClassName: array[0..255] of char;
Cloaked: Cardinal;
titlelen: Integer;
title: String;
begin
LHDesktop:=GetDesktopWindow;
GetClassName(LHWindow, AppClassName, 255);
LHParent:=GetWindowLong(LHWindow,GWL_HWNDPARENT);
LExStyle:=GetWindowLong(LHWindow,GWL_EXSTYLE);
if AppClassName = 'ApplicationFrameWindow' then
DwmGetWindowAttribute(LHWindow, DWMWA_CLOAKED, #cloaked, sizeof(Cardinal))
else
cloaked := DWM_NORMAL_APP_NOT_CLOAKED;
if IsWindowVisible(LHWindow)
and (AppClassName <> 'Windows.UI.Core.CoreWindow')
and ( (cloaked = DWM_NOT_CLOAKED) or (cloaked = DWM_NORMAL_APP_NOT_CLOAKED) )
and ( (LHParent=0) or (LHParent=LHDesktop) )
and (Application.Handle<>LHWindow)
and ((LExStyle and WS_EX_TOOLWINDOW = 0) or (LExStyle and WS_EX_APPWINDOW <> 0))
then
begin
titlelen := GetWindowTextLength(LHWindow);
SetLength(title, titlelen);
GetWindowText(LHWindow, PChar(title), titlelen + 1);
{ add each to a list }
But.ListBox1.Items.Add(title);
{ also add each HWND to the list too, later switch using SwitchToThisWindow }
{ ... }
end;
Result := True;
end;

Related

Get file path for drag-n-drop file on Windows Explorer

drag-n-drop is a pretty discussed topic in a lot sites (this too) and i found nice questions too, but no answer to this case.
I have a listView with some elements and i need them to be droppable on the Windows Explorer. When dropped i need only the file path where those are dropped, i don't need to copy anything, just need the path.
Similar question (and why they don't work for me):
Drag and drop to Desktop / Explorer (this works only if you already have the file to copy and doesn't give path)
http://blogs.msdn.com/b/delay/archive/2009/11/16/creating-something-from-nothing-and-knowing-it-developer-friendly-virtual-file-implementation-for-net-refined.aspx (this looks good but it still create a virtual file and copy it to the location, so i have no path and still have to copy something in my destination)
The only solution i found:
http://www.codeproject.com/Articles/23207/Drag-and-Drop-to-Windows-Folder-C
This works but in a very "unpratical" way, it creates a file watcher, create a dummy file, let the DragDrop function copy it, watch where it was created and finally delete it. Testing it in my Windows8.1 results in a incorrect Explorer refresh and i can still see the file until i refresh my screen (F5).
Is this the only way? I still can't believe i can't achieve this in a simpler way
Think about it for a minute... if you know about drag and drop, then you'll know that the drag source worries about packaging up the data into the correct format and the drag target worries about retrieving the data in the correct format. Your problem is that your drag target is not in your WPF application and so there is very little that you can do as the data is dropped.
A much better solution would be to implement your own basic file browser and then as part of your application, it would be far simpler to access the file path with the drag and drop operation. Either way, you've got a lot of work to do.
Create an empty FileDrop item as soon as you start dragging any item from your ListView.
As one of your mouse buttons is always down while dragging, start a timer which fires an event as soon as you release the pressed mouse button.
When the button is released, get the window handle of the window where the mouse is located. Match that handle against any opened Windows Explorer window.
If a matched window was found, get the location URL of that Windows Explorer Window and manipulate the available URL of that Windows Explorer window to get the (UNC) Windows path.
Create a Windows Form in design mode and add a ListView to it with name lvFiles. Set its AllowDrop property to True.
Then add a timer to the form and name it dropTimer. Set the interval to 50. Set Enabled to False.
In the events of dropTimer, double click, so the event will be dropTimer_Tick.
Go to code behind and paste code below.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace test
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern int GetForegroundWindow();
[DllImport("user32.dll")]
static extern short GetKeyState(VirtualKeyStates nVirtKey);
enum VirtualKeyStates : int
{
VK_LBUTTON = 0x01,
VK_RBUTTON = 0x02,
}
bool IsKeyPressed(VirtualKeyStates testKey)
{
bool keyPressed = false;
short result = GetKeyState(testKey);
switch (result)
{
case 0:
keyPressed = false;
break;
case 1:
keyPressed = false;
break;
default:
keyPressed = true;
break;
}
return keyPressed;
}
int GetActiveWindowHandle()
{
const int nChars = 256;
int handle = 0;
StringBuilder Buff = new StringBuilder(nChars);
handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
return handle;
else
return 0;
}
private string GetWindowsExplorerPathFromWindowHandle(int handle)
{
// Add a project COM reference to Microsoft Internet Controls 1.1
SHDocVw.ShellWindows shellWindows = new SHDocVw.ShellWindowsClass();
string fileName;
string path = "";
foreach ( SHDocVw.InternetExplorer ie in shellWindows )
{
fileName = Path.GetFileNameWithoutExtension(ie.FullName).ToLower();
if (fileName.Equals("explorer") && ie.HWND == handle)
{
path = ie.LocationURL;
path = path.ToLower();
path = path.Replace("file://", "");
if (path.StartsWith("/"))
path = path.Substring(1);
path = path.Replace("/", "\\");
if (!path.Contains(":")) // unc paths
path = "\\\\" + path;
break;
}
}
return path;
}
// Replace the created event from the designer with this event:
//
private void lvFiles_ItemDrag(object sender, ItemDragEventArgs e)
{
// fake drag and drop effect (start)
string dataFormat = DataFormats.FileDrop;
string[] data = new string[1];
data[0] = "";
DataObject dataObject = new DataObject(dataFormat, data);
// catch mouse events
if (IsKeyPressed(VirtualKeyStates.VK_LBUTTON))
MouseButtonPressed = MouseButtons.Left;
else if (IsKeyPressed(VirtualKeyStates.VK_RBUTTON))
MouseButtonPressed = MouseButtons.Right;
else
MouseButtonPressed = MouseButtons.None;
if (MouseButtonPressed == MouseButtons.Left || MouseButtonPressed == MouseButtons.Right)
this.dropTimer.Enabled = true;
// fake drag and drop effect (launch)
DoDragDrop(dataObject, DragDropEffects.Copy);
}
private void dropTimer_Tick(object sender, EventArgs e)
{
bool mouseButtonsReleased = false;
if (MouseButtonPressed == MouseButtons.Left && !IsKeyPressed(VirtualKeyStates.VK_LBUTTON))
mouseButtonsReleased = true;
else if (MouseButtonPressed == MouseButtons.Right && !IsKeyPressed(VirtualKeyStates.VK_RBUTTON))
mouseButtonsReleased = true;
if (mouseButtonsReleased)
{
dropTimer.Enabled = false;
int handle = GetActiveWindowHandle();
string dropPath = GetWindowsExplorerPathFromWindowHandle(handle);
MessageBox.Show(dropPath); // Here is where the Windows Explorer path is shown
}
}
}
}
Fill your ListView some way and drag any of the ListView item(s) to a Windows Explorer window; The drop path will show up.

SendKeys not stopping screensaver coming on [duplicate]

I want to disable the screensaver and monitor power off. At this stage there's no windows form, which I could youse. Thus I wan't to use NativeWindow.
Here's my code
sealed class ObserverWindow : NativeWindow, IDisposable
{
internal ObserverWindow()
{
this.CreateHandle(new CreateParams()
{
Parent= IntPtr.Zero
});
}
public void Dispose()
{
DestroyHandle();
}
protected override void WndProc(ref Message msg)
{
if (msg.Msg == WM_SYSCOMMAND &&
((((long)msg.WParam & 0xFFF0) == SC_SCREENSAVE) ||
((long)msg.WParam & 0xFFF0) == SC_MONITORPOWER))
{
msg.Msg = 0;
msg.HWnd = IntPtr.Zero;
}
base.WndProc(ref msg);
}
}
The Problem is, that the WndProc is not called with WM_SYSCOMMAND. Actualy the WndProc is called 4 times. At the last call there's msg.Msg == WM_CREATE.
I think I'm missing some create parameter. Does anyone have advise?
Regards Michael
UPDATE
I was running the code in a non STA thread. Thus the window did not reveive any messages exept the initial ones. Now I'm receiving WM_SYSCOMMAND messages. But when the screensaver is activated, there's no message.
I also tried to overwrite a Form's WndProc with the same result. But this used to work in Windows XP. Is there a change in Windows 7?
OS: Windows 7 64bit.
SOLUTION
As a comment in this Question states, only the foreground window can cancel the screensaver. Thus the above code can't work. The NativeWindow is great for receiving messages, but not for canceling a screensaver. For latter I recommend the answer to this question.
The proper way to do this is by telling Windows that your thread needs to have the display active. Commonly used by video players. P/Invoke the SetThreadExecutionState() API function, pass ES_DISPLAY_REQUIRED. And ES_SYSTEM_REQUIRED to keep the machine from shutting down automatically. Visit pinvoke.net for the required declarations.
Disabling the screen saver is much easier, according to this KB article:
This can be done easily using:
SystemParametersInfo( SPI_SETSCREENSAVEACTIVE,
FALSE,
0,
SPIF_SENDWININICHANGE
);
[...]
If you need the screen saver to start up again, you'll need to reinitialize the time-out period. Do this by [c]alling SystemParametersInfo (SPI_SETSCREENSAVEACTIVE, TRUE, 0, SPIF_SENDWININICHANGE).
You could try overriding DefWndProc instead.
public override void DefWndProc(ref Message msg)
{
if (msg.Msg == WM_SYSCOMMAND &&
((((long)msg.WParam & 0xFFF0) == SC_SCREENSAVE) ||
((long)msg.WParam & 0xFFF0) == SC_MONITORPOWER))
{
msg.Msg = 0;
msg.HWnd = IntPtr.Zero;
}
base.DefWndProc(ref msg);
}
I'm not on a Windows box right now, so I cannot test this. Let me know if it works.

How to use Regular Expression with FindWindow?

I am ultimately trying to write something that will check if a specific window exists, and set it as active in the event it is.
I was able to use FindWindow to find a literal windows name.
int hWnd = FindWindow(null, "121226-000377 - company - Oracle RightNow CX Cloud Service");
if (hWnd > 0) //If found
{
SetForegroundWindow(hWnd); //Activate it
}
else
{
MessageBox.Show("Window Not Found!");
}
The number at the front of the title changes and will never be the same twice, therefor I was trying to use Regular Expressions to find if any active window has the name structure as shown above but the numbers can change. I have a regular expresion that works for this, but I don't know how to implement it. I tried:
int hWnd = FindWindow(null, #"^\d+-\d+\s.*?RightNow CX");
if (hWnd > 0) //If found
{
SetForegroundWindow(hWnd); //Activate it
}
else
{
MessageBox.Show("Window Not Found!");
}
But it continually fails. So how do I use the FindWindow/SetForegroundWindow commands while making them use the regular expression to check with?
UPDATE~~~~
I selected a best answer, but here is the actual code for how I got this to work in case anyone is interested.
protected static bool EnumTheWindows(IntPtr hWnd, IntPtr lParam)
{
int size = GetWindowTextLength(hWnd);
if (size++ > 0 && IsWindowVisible(hWnd))
{
StringBuilder sb = new StringBuilder(size);
GetWindowText(hWnd, sb, size);
Match match = Regex.Match(sb.ToString(), #"^\d+-\d+\s.*?RightNow CX",
RegexOptions.IgnoreCase);
// Here we check the Match instance.
if (match.Success)
{
ActivateRNT(sb.ToString());
}
else
{
//this gets triggered for every single failure
}
//do nothing
}
return true;
}
private static void ActivateRNT(string rnt)
{
//Find the window, using the CORRECT Window Title, for example, Notepad
int hWnd = FindWindow(null, rnt);
if (hWnd > 0) //If found
{
SetForegroundWindow(hWnd); //Activate it
}
else
{
MessageBox.Show("Window Not Found!");
}
}
I still need to figure out how to test in the EnumWindows method to throw an alert if the window needed doesn't exist, but I'll worry about that at a later time.
I guess EnumWindows() is what you're looking for, although I'm not 100% sure how you'd use it in C# as you'll need a callback.
Edit: pinvoke.net got some code including an example callback.
Edit 2: The linked [MSDN sample][3] has more details on why/how to do it that way.
If you know the process name of the window searched you could try with something like this:
Process[] processes = Process.GetProcessesByName("notepad");
foreach (Process p in processes)
{
IntPtr pFoundWindow = p.MainWindowHandle;
SetForegroundWindow(pFoundWindow);
}
MSDN on GetProcessesByName
I don't think there is a built-in function/method/API for searching for windows with a regular expression pattern. One way of accomplishing it would be to enumerate the windows such as with this example and then compare the window text in the callback function using the regular expression.

C# PInvoke Finding the window handle of a child window from a known window

I am currently attempting to get some text from a child window using SendMessage via C# pinvoke. However, my previous attempts to hardcore the window handle failed as the value changes upon the startup of the application. Is there a way to reliably get the window handle of this child window? Winspector spy shows that the class name of this window is RichEdit20W. My current code is as follows :
IntPtr hWnd= (IntPtr) 0xA0E88; // Hardcode window handle
int txtlen = SendMessage(hWnd, WM_GETTEXTLENGTH, 20, null);
StringBuilder text = new StringBuilder(txtlen);
int RetVal = SendMessage(hWnd, WM_GETTEXT, text.Capacity, text);
If you can get the top-level window (the one with the title bar), you can use FindWindowEx to recurse through the children. This lets you specify the text of the window (use null since you don't know it), and/or the class (which you know).
http://www.pinvoke.net/default.aspx/user32.findwindowex
I ended up using the Managed Windows API to enumerate all descendant windows of the window.
var descendantwindows = childWindows[0].AllDescendantWindows; // Get all descendant windows of CMainWindow
for (int i = 0; i<descendantwindows.Length; i++)
{
if (descendantwindows[i].ClassName == "RichEdit20W")
childHandle = descendantwindows[i].HWnd;
}
You can PInvoke the API [FindWindow][1] in order to have the top level window, or maybe better, if you know the process name, by using:
Process[] processes = Process.GetProcessesByName("yourprocessname");
foreach (Process p in processes)
{
IntPtr pFoundWindow = p.MainWindowHandle;
// Do something with the handle...
//
}
Notice there is more entries because there can be potentially more process instance running at the same time. Then you need some strategy to lookup the top level children to point exactly the window you are looking for.

Use NativeWindow to disable screensaver

I want to disable the screensaver and monitor power off. At this stage there's no windows form, which I could youse. Thus I wan't to use NativeWindow.
Here's my code
sealed class ObserverWindow : NativeWindow, IDisposable
{
internal ObserverWindow()
{
this.CreateHandle(new CreateParams()
{
Parent= IntPtr.Zero
});
}
public void Dispose()
{
DestroyHandle();
}
protected override void WndProc(ref Message msg)
{
if (msg.Msg == WM_SYSCOMMAND &&
((((long)msg.WParam & 0xFFF0) == SC_SCREENSAVE) ||
((long)msg.WParam & 0xFFF0) == SC_MONITORPOWER))
{
msg.Msg = 0;
msg.HWnd = IntPtr.Zero;
}
base.WndProc(ref msg);
}
}
The Problem is, that the WndProc is not called with WM_SYSCOMMAND. Actualy the WndProc is called 4 times. At the last call there's msg.Msg == WM_CREATE.
I think I'm missing some create parameter. Does anyone have advise?
Regards Michael
UPDATE
I was running the code in a non STA thread. Thus the window did not reveive any messages exept the initial ones. Now I'm receiving WM_SYSCOMMAND messages. But when the screensaver is activated, there's no message.
I also tried to overwrite a Form's WndProc with the same result. But this used to work in Windows XP. Is there a change in Windows 7?
OS: Windows 7 64bit.
SOLUTION
As a comment in this Question states, only the foreground window can cancel the screensaver. Thus the above code can't work. The NativeWindow is great for receiving messages, but not for canceling a screensaver. For latter I recommend the answer to this question.
The proper way to do this is by telling Windows that your thread needs to have the display active. Commonly used by video players. P/Invoke the SetThreadExecutionState() API function, pass ES_DISPLAY_REQUIRED. And ES_SYSTEM_REQUIRED to keep the machine from shutting down automatically. Visit pinvoke.net for the required declarations.
Disabling the screen saver is much easier, according to this KB article:
This can be done easily using:
SystemParametersInfo( SPI_SETSCREENSAVEACTIVE,
FALSE,
0,
SPIF_SENDWININICHANGE
);
[...]
If you need the screen saver to start up again, you'll need to reinitialize the time-out period. Do this by [c]alling SystemParametersInfo (SPI_SETSCREENSAVEACTIVE, TRUE, 0, SPIF_SENDWININICHANGE).
You could try overriding DefWndProc instead.
public override void DefWndProc(ref Message msg)
{
if (msg.Msg == WM_SYSCOMMAND &&
((((long)msg.WParam & 0xFFF0) == SC_SCREENSAVE) ||
((long)msg.WParam & 0xFFF0) == SC_MONITORPOWER))
{
msg.Msg = 0;
msg.HWnd = IntPtr.Zero;
}
base.DefWndProc(ref msg);
}
I'm not on a Windows box right now, so I cannot test this. Let me know if it works.

Categories

Resources