i "stole" the code from
http://improve.dk/archive/2007/04/07/finding-specific-windows.aspx
but instead of writing the class name , title and handle into the console i want to check if a certain button is visible. and if the button is visible i want to maximize the window.
i changed this part =>
private static bool foundWindow(int handle)
{
bool buttonCheck = false;
IntPtr hButton = FindWindowEx((IntPtr)handle, IntPtr.Zero, "AfxWnd90u21", null);
if (hButton != IntPtr.Zero)
{
buttonCheck = true;
}
if (buttonCheck)
{
ShowWindowAsync(handle, (int)3); // maximize the window
}
return true;
}
the button class is `AfxWnd90u` and the instance is `21`. I wrote this in autoit before and AfxWnd90u21 is 100 % correct.
the problem is that i cant find the button with AfxWnd90u21. if i only use
IntPtr hButton = FindWindowEx((IntPtr)handle, IntPtr.Zero, "AfxWnd90u", null);
all windows get maximized.
It has to be something with the instance.
i hope you can help me,
thanks
Newest Edit
i just tried to find the class name with "GetClassName". I find 190~ classes per handle, but the class that i need is not in there.
iam really desperate
I hope someone can help me,
thanks
private static bool foundWindow(int handle)
{
int i = 0;
IntPtr hWnd = (IntPtr)handle;
// System.Windows.Forms.Control control = System.Windows.Forms.Control.FromHandle(hWnd);
StringBuilder sbClass = new StringBuilder(256);
while (hWnd != IntPtr.Zero)
{
++i;
///////////////////////////////////////////////////
////////////// Compare if the classname exists/////
GetClassName((int)hWnd, sbClass, sbClass.Capacity);
if (sbClass.ToString().Equals("AfxWnd90u21"))
{
MessageBox.Show(sbClass.ToString());
}
///////////////////////////////////////////////////
////// trying to find the correct class with findwindowEX//////////
IntPtr hButton = FindWindowEx(hWnd, IntPtr.Zero, "AfxWnd90u21", null);
if (hButton != IntPtr.Zero)
{
MessageBox.Show("true");
ShowWindowAsync(handle, (int)2); // maximize the window
}
hWnd = FindWindowEx(IntPtr.Zero, hWnd, null, null);
}
MessageBox.Show(""+i);
return true;
}
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633500%28v=vs.85%29.aspx
lpszWindow [in, optional]
Type: LPCTSTR
The window name (the window's title). If this parameter is NULL,
all window names match.
Looks like with this API, in order to match an instance, you need to give your instances unique window names. Or, you could search through all children manually cast to a Control, then check the instances yourself.
But if you go that far, it's easier to cast the parent to a Control, and iterate through it's .Controls member. You can use reflection to check the control's type and so on.
To convert a handle to a control:
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.fromhandle.aspx
Iterate over Control.Controls using whichever loop style you prefer.
Related
I need to retrieve the handle of a window selected by the user and then retrieve its handle. This window must be one of those shown when ALT+TAB is pressed.
I tried enumerating the windows using EnumWindows, but it does not enumerate the full screen UWP windows. For example, if you open a picture with the Photos app and put it in full screen, EnumWindows will not enumerate it.
Then I tried EnumChildWindows because I thought it could enumerate everything, even fullscreen UWP windows, but probably not.
The GraphicsCapturePicker.PickSingleItemAsync method shows a list of windows and the user can pick one, but it returns a GraphicsCaptureItem and I guess you can't get the window handle from it.
Is it possible to reuse the ALT+TAB window to do this (or any other way that shows a list of windows) and retrieve the handle of the window selected by the user?
Note: I need all the windows shown when ALT+TAB is pressed, even the full screen UWP windows, and no others.
I have investigated with Spy++ and neither EnumWindows nor EnumChildWindows retrieve the handles of the root owners of full screen UWP windows. However EnumChildWindows retrieves their child windows, and each UWP window has a child window whose class name is ApplicationFrameInputSinkWindow (and other child windows). Then, you can retrive the root owner window with GetAncestor.
So, to retrieve "standard" windows, you could call EnumWindows.
But to retrieve full screen UWP windows:
Call EnumChildWindows with the desktop window as parent.
In the callback function fetch only the windows whose class name is ApplicationFrameInputSinkWindow.
Call GetAncestor to get the root owner window.
If the root owner window has the extended window style WS_EX_TOPMOST and not WS_EX_NOACTIVATE or WS_EX_TOOLWINDOW, it is a full screen UWP window.
This sample shows how to use both EnumWindows and EnumChildWindows to enumerate all "ALT+TAB windows", even full-screen UWP windows. These are listed in a Form with a two-column DataGridView and the window handle corresponding to the row the user clicks on is retrieved.
const int GWL_EXSTYLE = -20;
const uint DWMWA_CLOAKED = 14;
const uint DWM_CLOAKED_SHELL = 0x00000002;
const uint GA_ROOTOWNER = 3;
const uint WS_EX_TOOLWINDOW = 0x00000080;
const uint WS_EX_TOPMOST = 0x00000008;
const uint WS_EX_NOACTIVATE = 0x08000000;
private void Form1_Load(object sender, EventArgs e)
{
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridView1.MultiSelect = false;
dataGridView1.ReadOnly = true;
dataGridView1.Click += dataGridView1_Click;
EnumWindows(GetAltTabWindows, IntPtr.Zero);
EnumChildWindows(GetDesktopWindow(), GetFullScreenUWPWindows, IntPtr.Zero);
}
private bool GetAltTabWindows(IntPtr hWnd, IntPtr lparam)
{
if (IsAltTabWindow(hWnd))
AddWindowToGrid(hWnd);
return true;
}
private bool GetFullScreenUWPWindows(IntPtr hWnd, IntPtr lparam)
{
// Check only the windows whose class name is ApplicationFrameInputSinkWindow
StringBuilder className = new StringBuilder(1024);
GetClassName(hWnd, className, className.Capacity);
if (className.ToString() != "ApplicationFrameInputSinkWindow")
return true;
// Get the root owner of the window
IntPtr rootOwner = GetAncestor(hWnd, GA_ROOTOWNER);
if (IsFullScreenUWPWindows(rootOwner))
AddWindowToGrid(rootOwner);
return true;
}
private bool IsAltTabWindow(IntPtr hWnd)
{
// The window must be visible
if (!IsWindowVisible(hWnd))
return false;
// The window must be a root owner
if (GetAncestor(hWnd, GA_ROOTOWNER) != hWnd)
return false;
// The window must not be cloaked by the shell
DwmGetWindowAttribute(hWnd, DWMWA_CLOAKED, out uint cloaked, sizeof(uint));
if (cloaked == DWM_CLOAKED_SHELL)
return false;
// The window must not have the extended style WS_EX_TOOLWINDOW
uint style = GetWindowLong(hWnd, GWL_EXSTYLE);
if ((style & WS_EX_TOOLWINDOW) != 0)
return false;
return true;
}
private bool IsFullScreenUWPWindows(IntPtr hWnd)
{
// Get the extended style of the window
uint style = GetWindowLong(hWnd, GWL_EXSTYLE);
// The window must have the extended style WS_EX_TOPMOST
if ((style & WS_EX_TOPMOST) == 0)
return false;
// The window must not have the extended style WS_EX_NOACTIVATE
if ((style & WS_EX_NOACTIVATE) != 0)
return false;
// The window must not have the extended style WS_EX_TOOLWINDOW
if ((style & WS_EX_TOOLWINDOW) != 0)
return false;
return true;
}
private void AddWindowToGrid(IntPtr hWnd)
{
StringBuilder windowText = new StringBuilder(1024);
GetWindowText(hWnd, windowText, windowText.Capacity);
var strTitle = windowText.ToString();
var strHandle = hWnd.ToString("X8");
dataGridView1.Rows.Add(new string[] { strHandle, strTitle });
}
private void dataGridView1_Click(object sender, EventArgs e)
{
var dgv = (DataGridView)sender;
if (dgv.SelectedRows.Count == 0)
return;
// Get the value of the first cell of the selected row
var value = dgv.SelectedRows[0].Cells[0].Value;
if (value == null)
return;
// Convert the value to IntPtr
var strValue = value.ToString();
var intValue = int.Parse(strValue, System.Globalization.NumberStyles.HexNumber);
var windowHandle = new IntPtr(intValue);
// Do what you want with the window handle
}
Of course, you can also just use EnumChildWindows to get all "ALT+TAB windows", as long as the callback function has all the necessary filters to filter the different windows.
I want to set the foreground of a window in Visual Studio.
What i've tried:
When i have 2 windows "docked" side by side i can't set the foreground of a window under the mouse in VS.
I'm using GetCursorPos and WindowFromPoint which usually works for standard windows. I also tried to use EnumChildWindows from pinvoke.net (sample code 2 -https://www.pinvoke.net/default.aspx/user32.enumchildwindows) but it returns 0 when i pass the WindowFromPoint or MainWindowHandle of the process.
I assume it's not a typical child window technically or something else that i don't understand?
public static IntPtr GetProcessMainWindowHandle()
{
Process process = null;
if (GetCursorPos(out Point point))
{
IntPtr hWnd = WindowFromPoint(point);
GetWindowThreadProcessId(hWnd, out uint pid);
process = Process.GetProcessById((int)pid);
}
return process.MainWindowHandle;
}
public static IntPtr GetHandle()
{
IntPtr hWnd = IntPtr.Zero;
if (GetCursorPos(out Point point))
{
hWnd = WindowFromPoint(point);
}
return hWnd;
}
As mentioned in the comments from Lars, it could be a WPF window.
I can see that i can only get the handle for the parent window as described for WPF behaviour but i get still null with the following code. Maybe i have something wrong? (Any hint would be great.)
public static void GetWPFObject(IntPtr handle)
{
HwndSource source = HwndSource.FromHwnd(handle) as HwndSource;
}
I have handles to the main form of the Winforms application, and the window that I'm trying to check (which may or may not be part of the application). I've tried iterating using GetParent, but it doesn't seem to work.
What I'm essentially trying to do is detect a modal window (such as a MsgBox), get it's controls, and send a button click message if the controls fulfill some requirements (like be a Button).
Now, while I can detect if a modal window is open, and can find the currently focused window, I have no idea if the currently focused window is the modal window that was detected. Essentially, if I open a model window and then open up a completely different program, it tries to find the controls of that external program.
The code is below:
if (pF.Visible && !pF.CanFocus) //Is a Modal Window
{
///TODO: Check if currently active window is a child of the main window
///Gets information of currently active window
string currentActiveWindow = GetActiveWindowTitle();
IntPtr currentActiveHandle = GetActiveWindowHandle();
///Gets 'children' or controls of currently active window
var hwndChild = EnumAllWindows(currentActiveHandle);
///Iterate over all child windows
foreach (IntPtr element in hwndChild) {
const int nChars = 256;
StringBuilder Buff = new StringBuilder(nChars);
IntPtr handle = GetForegroundWindow();
string windowElementType = GetWindowClassName(element);
///Check if the "windows" are buttons
if (GetWindowText(element, Buff, nChars) > 0 && windowElementType=="Button")
{
string windowElement = Buff.ToString();
if (windowElement.ToLower()=="ok")
{
///Send Button click message
const int BM_CLICK = 0x00F5;
SendMessage(element, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
}
}
}
}
A convenience function (C++) to determine whether two windows identified by their HWND belong to the same process would look like this:
bool OwnedBySameProcess(HWND hWnd1, HWND hWnd2) {
if ( ::IsWindow(hWnd1) && ::IsWindow(hWnd2) ) {
DWORD procId1 = 0x0;
DWORD procId2 = 0x0;
::GetWindowThreadProcessId(hWnd1, &procId1);
::GetWindowThreadProcessId(hWnd2, &procId2);
return ( procId1 == procId2 );
}
return false;
}
The GetWindowThreadProcessId is not subject to UIPI (User Interface Privilege Isolation) and will always succeed given valid input. The return values are IDs and do not need to be cleaned up.
Translated to C#:
public class Helper
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWindow(IntPtr hWnd);
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd,
out uint lpdwProcessId);
public static bool OwnedBySameProcess(IntPtr hWnd1, IntPtr hWnd2)
{
if ( !IsWindow(hWnd1) )
throw new ArgumentException("hWnd1");
if ( !IsWindow(hWnd2) )
throw new ArgumentException("hWnd2");
uint procId1 = 0;
GetWindowThreadProcessId(hWnd1, out procId1);
uint procId2 = 0;
GetWindowThreadProcessId(hWnd2, out procId2);
return ( procId1 == procId2 );
}
}
In one of my programs I need to test if the user is currently focusing the desktop/shell window. Currently I'm using GetShellWindow() from user32.dll and compare the result to GetForegroundWindow().
This approach is working until someone changes the desktop wallpaper, but as soon as the wallpaper is changed the handle from GetShellWindow() doesn't match the one from GetForegroundWindow() anymore and I don't quite get why that is. (OS: Windows 7 32bit)
Is there a better approach to check if the desktop is focused? Preferably one that won't be broken if the user changes the wallpaper?
EDIT: I designed a workaround: I'm testing the handle to have a child of class "SHELLDLL_DefView". If it has, the desktop is on focus. Whilst it's working at my PC that doesn't mean it will work all the timeā¦
The thing changed a little bit since there are slideshows as wallpaper available in Windows 7.
You are right with WorkerW, but this works only with wallpaper is set to slideshow effect.
When there is set the wallpaper mode to slideshow, you have to search for a window of class WorkerW and check the children, whether there is a SHELLDLL_DefView.
If there is no slideshow, you can use the good old GetShellWindow().
I had the same problem some months ago and I wrote a function for getting the right window. Unfortunately I can't find it. But the following should work. Only the Win32 Imports are missing:
public enum DesktopWindow
{
ProgMan,
SHELLDLL_DefViewParent,
SHELLDLL_DefView,
SysListView32
}
public static IntPtr GetDesktopWindow(DesktopWindow desktopWindow)
{
IntPtr _ProgMan = GetShellWindow();
IntPtr _SHELLDLL_DefViewParent = _ProgMan;
IntPtr _SHELLDLL_DefView = FindWindowEx(_ProgMan, IntPtr.Zero, "SHELLDLL_DefView", null);
IntPtr _SysListView32 = FindWindowEx(_SHELLDLL_DefView, IntPtr.Zero, "SysListView32", "FolderView");
if (_SHELLDLL_DefView == IntPtr.Zero)
{
EnumWindows((hwnd, lParam) =>
{
if (GetClassName(hwnd) == "WorkerW")
{
IntPtr child = FindWindowEx(hwnd, IntPtr.Zero, "SHELLDLL_DefView", null);
if (child != IntPtr.Zero)
{
_SHELLDLL_DefViewParent = hwnd;
_SHELLDLL_DefView = child;
_SysListView32 = FindWindowEx(child, IntPtr.Zero, "SysListView32", "FolderView"); ;
return false;
}
}
return true;
}, IntPtr.Zero);
}
switch (desktopWindow)
{
case DesktopWindow.ProgMan:
return _ProgMan;
case DesktopWindow.SHELLDLL_DefViewParent:
return _SHELLDLL_DefViewParent;
case DesktopWindow.SHELLDLL_DefView:
return _SHELLDLL_DefView;
case DesktopWindow.SysListView32:
return _SysListView32;
default:
return IntPtr.Zero;
}
}
In your case you would call GetDesktopWindow(DesktopWindow.SHELLDLL_DefViewParent); to get the top-level window for checking whether it is the foreground window.
Here is a workaround that uses GetClassName() to detect if the desktop is active:
When Windows first starts, the desktop's Class is "Progman"
After changing the wallpaper, the desktop's Class will be "WorkerW"
You can test against these to see if the desktop is focused.
[DllImport("user32.dll")]
static extern int GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetClassName(int hWnd, StringBuilder lpClassName, int nMaxCount);
public void GetActiveWindow() {
const int maxChars = 256;
int handle = 0;
StringBuilder className = new StringBuilder(maxChars);
handle = GetForegroundWindow();
if (GetClassName(handle, className, maxChars) > 0) {
string cName = className.ToString();
if (cName == "Progman" || cName == "WorkerW") {
// desktop is active
} else {
// desktop is not active
}
}
}
I would like to add a new section to the To-Do Bar in Outlook 2010 (or 2007). I found some code to create a new collapsible task pane and someone claiming you can't modify the To-Do bar, but I also found a product called Add-In Express that claims it can do it (although at $349 it's not worth it for a one-off project).
Is is possible to do that?
After some research (and after having seen the product documentation of Add-in Express), I figured that it is possible to customize the To-Do Bar in Outlook 2007.
There is a proof-poof-concept on CodeProject that embeds a "custom" (read self-written) pane into Outlooks main window. The article has been written by Lukas Neumann and is available here:
Additional custom panel in Microsoft Outlook
The principle is the following:
Search the Outlook window for the child window where you want to place your own window (i.e. the To-Do Bar child window)
Resize the contents of that window to make some space for your controls
Add your own window as a child
Subclass the To-Do Bar window to hook into the message loop of that window
There is basically only two modifications that need to be done to adjust the sample code:
Get the correct child window handles: The window class of the To-Do Bar is called "WUNDERBAR". This class is used for several child windows so make sure to also check for the correct window title ("ToDoBar") or search by window title only.
Get the resizing of the panel right (simple but not always easy ;-).
(And add some proper error handling if the To-Do Bar is not found etc).
It's a strong plus if you are familiar with Spy++ as it is needed to find out the class names and window titles of Outlook's child windows.
I suggest you to download the sample code and apply the following modifications:
In Connect.cs:
private const string SIBLING_WINDOW_CLASS = "NetUINativeHWNDHost";
public delegate bool EnumChildCallback(IntPtr hwnd, ref IntPtr lParam);
[DllImport("User32.dll")]
public static extern bool EnumChildWindows(IntPtr hWndParent, EnumChildCallback lpEnumFunc, ref IntPtr lParam);
[DllImport("User32.dll")]
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetWindowTextLength(IntPtr hWnd);
public static bool EnumChildProc(IntPtr hwndChild, ref IntPtr lParam)
{
StringBuilder className = new StringBuilder(128);
GetClassName(hwndChild, className, 128);
int length = GetWindowTextLength(hwndChild);
StringBuilder windowText = new StringBuilder(length + 1);
GetWindowText(hwndChild, windowText, windowText.Capacity);
if (className.ToString() == "WUNDERBAR" && windowText.ToString() == "ToDoBar")
{
lParam = hwndChild;
return false;
}
return true;
}
public void OnStartupComplete(ref System.Array custom)
{
if (_outlookApplication == null)
return; //We were not loaded into Outlook, so do nothing
//Get the instance of Outlook active explorer (= the main window) and start capturing selection changes
_outlookExplorer = _outlookApplication.ActiveExplorer();
_outlookExplorer.SelectionChange += new ExplorerEvents_10_SelectionChangeEventHandler(outlookExplorer_SelectionChange);
//Find Outlook window handle (HWND)
IntPtr outlookWindow = FindOutlookWindow();
if (outlookWindow == IntPtr.Zero)
return;
// Find ToDoBar window handle
IntPtr todoBarWindow = IntPtr.Zero;
EnumChildCallback cb = new EnumChildCallback(EnumChildProc);
EnumChildWindows(outlookWindow, cb, ref todoBarWindow);
if (todoBarWindow == IntPtr.Zero)
return;
//Find sibling window handle (HWND)
//Sibling window is the window which we are going to "cut" to make space for our own window
IntPtr siblingWindow = SafeNativeMethods.FindWindowEx(todoBarWindow, IntPtr.Zero, SIBLING_WINDOW_CLASS, null);
if (siblingWindow == IntPtr.Zero)
return;
//Initialise PanelManager and assign own panel to it
_panelManager = new PanelManager(outlookWindow, siblingWindow);
_customPanel = new MyPanel();
_panelManager.ShowBarControl(_customPanel);
}
In PanelManager.cs:
private void ResizePanels()
{
if (_changingSize)
return; //Prevent infinite loops
_changingSize = true;
try
{
//Get size of the sibling window and main parent window
Rectangle siblingRect = SafeNativeMethods.GetWindowRectangle(this.SiblingWindow);
Rectangle parentRect = SafeNativeMethods.GetWindowRectangle(this.ParentWindow);
//Calculate position of sibling window in screen coordinates
SafeNativeMethods.POINT topLeft = new SafeNativeMethods.POINT(siblingRect.Left, siblingRect.Top);
SafeNativeMethods.ScreenToClient(this.ParentWindow, ref topLeft);
//Decrease size of the sibling window
int newHeight = parentRect.Height - topLeft.Y - _panelContainer.Height;
SafeNativeMethods.SetWindowPos(this.SiblingWindow, IntPtr.Zero, 0, 0, siblingRect.Width, newHeight, SafeNativeMethods.SWP_NOMOVE | SafeNativeMethods.SWP_NOZORDER);
//Move the bar to correct position
_panelContainer.Left = topLeft.X;
_panelContainer.Top = topLeft.Y + newHeight;
//Set correct height of the panel container
_panelContainer.Width = siblingRect.Width;
}
finally
{
_changingSize = false;
}
}
The proof-of-concept is a managed COM Add-in and not using VSTO, but a very similar approach should also work for VSTO. Let me know in case you need any further help, as the proof-of-concept already requires some knowledge about subclassing and the Office add-in architecture (IDTExtensibility2).
Please also consider that this is just a proof-of-concept showing the basic technique how to customize the Outlook UI. And my edits are far from beautiful code ;-)
What you're looking for is called a TaskPane, not exactly a Form Region. TaskPanes work a little differently than Form Regions and they are only available in Office 2007 and higher (which doesn't look like it will be a problem for you since you need this for 2007-2010). If nothing else at least knowing the right term might make googling for this a bit easier.
Here's a Custom Task Panes Overview on MSDN.
Now, as far as adding a section to an existing TaskPane, I do not know for sure. But hopefully that gets you a bit closer.
BTW, as a side note, the Add-in Express libraries are awesome. Make this sort of thing a 2-minute task. Highly recommended - and it is likely something you'll use again since they make the job so easy.
Michael,
Take a look at Outlook form regions. http://msdn.microsoft.com/en-us/library/bb386301.aspx
You can add them using a VSTO addin.
Though Add-in express has a few more options to where you can add them.
There are quite a few tutorials on the net as well.
Marcus