How can I get MessageBox icons in Windows 8.1 - c#

I want to get the MessageBoxIcons, that get displayed when the user is presented with a MessageBox. Earlier I used SystemIcons for that purpose, but now it seems that it returns icons different than the ones on the MessageBox.
This leads to the conclusion that in Windows 8.1 SystemIcons and MessageBoxIcons are different. I know that icons are taken using WinApi MessageBox, but I can't seem to get the icons themselves in any way.
I would like to ask for a way of retrieving those icons.

Update:
You should use the SHGetStockIconInfo function.
To do that in C# you will have to define a few enums and structs (consult this excellent page for more information):
public enum SHSTOCKICONID : uint
{
//...
SIID_INFO = 79,
//...
}
[Flags]
public enum SHGSI : uint
{
SHGSI_ICONLOCATION = 0,
SHGSI_ICON = 0x000000100,
SHGSI_SYSICONINDEX = 0x000004000,
SHGSI_LINKOVERLAY = 0x000008000,
SHGSI_SELECTED = 0x000010000,
SHGSI_LARGEICON = 0x000000000,
SHGSI_SMALLICON = 0x000000001,
SHGSI_SHELLICONSIZE = 0x000000004
}
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SHSTOCKICONINFO
{
public UInt32 cbSize;
public IntPtr hIcon;
public Int32 iSysIconIndex;
public Int32 iIcon;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260/*MAX_PATH*/)]
public string szPath;
}
[DllImport("Shell32.dll", SetLastError = false)]
public static extern Int32 SHGetStockIconInfo(SHSTOCKICONID siid, SHGSI uFlags, ref SHSTOCKICONINFO psii);
After that you can easily get the required icon:
SHSTOCKICONINFO sii = new SHSTOCKICONINFO();
sii.cbSize = (UInt32)Marshal.SizeOf(typeof(SHSTOCKICONINFO));
Marshal.ThrowExceptionForHR(SHGetStockIconInfo(SHSTOCKICONID.SIID_INFO,
SHGSI.SHGSI_ICON ,
ref sii));
pictureBox1.Image = Icon.FromHandle(sii.hIcon).ToBitmap();
This is how the result will look like:
Please note:
If this function returns an icon handle in the hIcon member of the
SHSTOCKICONINFO structure pointed to by psii, you are responsible for
freeing the icon with DestroyIcon when you no longer need it.
i will not delete my original answer, as - I think - it contains useful information regarding this issue, and another way (or workaround) of retrieving this icon.
Original answer:
Quite interestingly the icons present in the SystemIcons differ from the ones displayed on the MessageBoxes in the case of Asterisk, Information and Question. The icons on the dialog look much flatter.
In all other cases they look exactly the same, e.g.: in case of Error:
When you try to get the icon using the SystemIcons you get the one on the left in the above images.
// get icon from SystemIcons
pictureBox1.Image = SystemIcons.Asterisk.ToBitmap();
If you try a little bit harder, using the LoadIcon method from user32.dll, you still get the same icon (as it can be seen in center of the above images).
[DllImport("user32.dll")]
static extern IntPtr LoadIcon(IntPtr hInstance, IntPtr lpIconName);
...
public enum SystemIconIds
{
...
IDI_ASTERISK = 32516,
...
}
...
// load icon by ID
IntPtr iconHandle = LoadIcon(IntPtr.Zero, new IntPtr((int)SystemIconIds.IDI_ASTERISK));
pictureBox2.Image = Icon.FromHandle(iconHandle).ToBitmap();
But when you show a MessagBox you get a different one (as seen in the MessageBox on the images). One has no other choice, but to get that very icon from the MessageBox.
For that we will need a few more DllImports:
// To be able to find the dialog window
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
// To be able to get the icon window handle
[DllImport("user32.dll")]
static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);
// To be able to get a handle to the actual icon
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
The idea is the following: First we display a MessageBox, after that (while it is still displayed) we find it's handle, using that handle we will get another handle, now to the static control which is containing the icon. In the end we will send a message to that control (an STM_GETICON message), which will return with a handle to the icon itself. Using that handle we can create an Icon, which we can use anywhere in our application.
In code:
// show a `MessageBox`
MessageBox.Show("test", "test caption", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
...
var hwnd = FindWindow(null, "test caption");
if (hwnd != IntPtr.Zero)
{
// we got the messagebox, get the icon from it
IntPtr hIconWnd = GetDlgItem(hwnd, 20);
if (hIconWnd != IntPtr.Zero)
{
var iconHandle = SendMessage(hIconWnd, 369/*STM_GETICON*/, IntPtr.Zero, IntPtr.Zero);
pictureBox3.Image = Icon.FromHandle(iconHandle).ToBitmap();
}
}
After the code runs the PictureBox called pictureBox3 will display the same image as the MessageBox (as it can be seen on the right on the image).
I really hope this helps.
For reference here is all the code (it's a WinForms app, the Form has three PicturBoxes and one Timer, their names can be deducted from the code...):
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern IntPtr LoadIcon(IntPtr hInstance, IntPtr lpIconName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
public enum SystemIconIds
{
IDI_APPLICATION = 32512,
IDI_HAND = 32513,
IDI_QUESTION = 32514,
IDI_EXCLAMATION = 32515,
IDI_ASTERISK = 32516,
IDI_WINLOGO = 32517,
IDI_WARNING = IDI_EXCLAMATION,
IDI_ERROR = IDI_HAND,
IDI_INFORMATION = IDI_ASTERISK,
}
public Form1()
{
InitializeComponent();
// Information, Question and Asterix differ from the icons displayed on MessageBox
// get icon from SystemIcons
pictureBox1.Image = SystemIcons.Asterisk.ToBitmap();
// load icon by ID
IntPtr iconHandle = LoadIcon(IntPtr.Zero, new IntPtr((int)SystemIconIds.IDI_ASTERISK));
pictureBox2.Image = Icon.FromHandle(iconHandle).ToBitmap();
}
private void pictureBox1_Click(object sender, EventArgs e)
{
MessageBox.Show("test", "test caption", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
private void timer1_Tick(object sender, EventArgs e)
{
var hwnd = FindWindow(null, "test caption");
if (hwnd != IntPtr.Zero)
{
// we got the messagebox, get the icon from it
IntPtr hIconWnd = GetDlgItem(hwnd, 20);
if (hIconWnd != IntPtr.Zero)
{
var iconHandle = SendMessage(hIconWnd, 369/*STM_GETICON*/, IntPtr.Zero, IntPtr.Zero);
pictureBox3.Image = Icon.FromHandle(iconHandle).ToBitmap();
}
}
}
}
}

Related

How stop Outlook alternative greeting pop-up, or close it automatically

Only 1 user in my company gets a pop-up every time she get's an email and she has her auto replies turned on. I can't post an image, but if you go to imgur, and add forward slash and 'xooZR8D' without the quotes, you will see the popup. The title of the popup is Microsoft Outlook and the body of the popup is: Your IMAP server wants to alert you to the following: [150] Your alternate greeting is currently being used.
Ideally, I'd like to know how to turn this off. Alternatively, does anyone have code for a .Net program (preferably VB, C# would be my second choice) to scan open windows and close the desired one? When I scanned for open processes and found one that had the title I wanted to close, calling process.close or process.closeMainWindow did nothing, and process.Kill closed the window and the Outlook application, which I don't want. TIA
This works, although it doesn't get to the root of the problem:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ClosePopupsCSharp
{
public partial class Form1 : Form
{
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWindowVisible(IntPtr hWnd);
static uint WM_CLOSE = 0x10;
public Form1()
{
InitializeComponent();
timer2.Enabled = true;
}
private void DeleteOutlookPopups()
{
timer2.Enabled = false;
IntPtr hWnd = FindWindowByCaption(IntPtr.Zero, "Microsoft Outlook");
if (hWnd != null)
{
if (IsWindowVisible(hWnd))
{
bool ret = CloseWindow(hWnd);
}
}
timer2.Enabled = true;
}
static bool CloseWindow(IntPtr hWnd)
{
SendMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
return true;
}
private void timer2_Tick(object sender, EventArgs e)
{
DeleteOutlookPopups();
}
}
}

how to hide Desktop Icons without restarting your computer [duplicate]

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.

c# i'm unable to get SendMessage to send a message to a notepad window

I have looked at this question and answer
How to send text to Notepad in C#/Win32?
A slight variation that I think shouldn't matter.. Is that I have a bunch of notepad windows.. So to test this I copied notepad.exe to be notepadd.exe and opened notepadd.exe, so only one of my notepad windows is the notepadd.exe process.
I have this code
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace testsendmessage
{
public partial class Form1 : Form
{
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
Process[] notepads = Process.GetProcessesByName("notepadd");
if (notepads.Length == 0) return;
if (notepads[0] != null)
{
IntPtr child = FindWindowEx(notepads[0].MainWindowHandle, new IntPtr(0), "Edit", null);
SendMessage(child, 0x000C, 0, "abcd");
}
}
}
}
It's not touching the notepad window though.
I tried debugging and I see that the notepads array has one item, which is certainly correct.
And it gets within the 'if' and it runs SendMessage(child, 0x000C, 0, "abcd");
But I see nothing appearing in the notepad window
I'm not getting an error from the code it's just nothing appearing in the notepad window.. And I don't really understand winapi stuff much, so i'm not sure how to proceed in trying to solve it?
As you can see it reaches that line, and I can use the watch window to look at the notepads Process array, and at 'child' but I don't know what I should be looking at to determine why it's not sending to the window
Added
New code based on Remy's suggestion
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace testsendmessage
{
public partial class Form1 : Form
{
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll", CharSet = CharSet.Unicode, EntryPoint = "SendMessageW")]
public static extern IntPtr SendMessageWStr(IntPtr hWnd, uint uMsg, IntPtr wParam, string lParam);
const uint WM_SETTEXT = 0x000C;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
Process[] notepads = Process.GetProcessesByName("notepadd");
if (notepads.Length == 0) return;
if (notepads[0] != null)
{
IntPtr child = FindWindowEx(notepads[0].MainWindowHandle, new IntPtr(0), "Edit", null);
SendMessageWStr(child, WM_SETTEXT, IntPtr.Zero, "abcd");
}
}
}
}
But I still get the same issue that the notepad window was blank before clicking button and is blank after too. It's not sending the text to the notepadd window. Despite the fact that it is reaching that line of code that is meant to send the text to it.
Further addition.
Current code,
I've changed FindWindowEx to FindWindowExW and i've changed new IntPtr(0) to IntPtr.Zero and it still is unresponsive.
I've opened up notepadd.exe from cmd, I see the window there. And of course notepadd.exe in task manger, But clicking the button in my application is not writing any text into that window.
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace testsendmessage
{
public partial class Form1 : Form
{
[DllImport("user32.dll", EntryPoint = "FindWindowExW")]
public static extern IntPtr FindWindowExW(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll", CharSet = CharSet.Unicode, EntryPoint = "SendMessageW")]
public static extern IntPtr SendMessageWStr(IntPtr hWnd, uint uMsg, IntPtr wParam, string lParam);
const uint WM_SETTEXT = 0x000C;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
Process[] notepads = Process.GetProcessesByName("notepadd");
if (notepads.Length == 0) return;
if (notepads[0] != null)
{
IntPtr child = FindWindowExW(notepads[0].MainWindowHandle, IntPtr.Zero, "Edit", null);
SendMessageWStr(child, WM_SETTEXT, IntPtr.Zero, "abcd");
}
}
}
}
Much credit to Remy. After some troubleshooting I ended up finding the code worked so it's a mystery why I was finding it didn't.
A good troubleshooting step is to try moving the window around with nircmd, so you know you have the handle.
To get the handle of the window, you can use nirsoft winlister, or winspector
You can use nircmd commands like nircmd win close handle 0x00230632 change that 0x00230632 to whatever you find the handle to be. Or better don't close it(otherwise you'll have to reopen it and the new window will have a new handle), so a command like nircmd win move handle 0x00B917AE 80 10 100 100 So you know the handle is right, regardless of any issue with the code.
Winspector also shows the child of the notepad window
Going back to the C# code, you can
skip the child and try writing into the parent, it should write into the title of the window
Try using SendMessage to specify a window directly. You write the handle in decimal rather than hex. e.g. if the handle is 3479948 then
e.g. SendMessage(new IntPtr(3479948), WM_SETTEXT, IntPtr.Zero, "abcdee");
You can also check that notepads[0].MainWindowHandle is picking up the correct value.. the handle shown in winspector.
You can look at the IntPtr child = FindWindowEx(...) line, make sure the 'child' is picking up the child handle.
You can try writing to that one directly with SendMessage. e.g. SendMessage(new IntPtr(1234567), WM_SETTEXT, IntPtr.Zero, "abcdee"); // (if winspector shows the child window to be that handle).

Sending Click with user32.dll, messages appear in spy++ but button isn't clicked

I have problems with sending a Click to an application with the user32.dll. the button does not be clicked, but in spy++ the message do appear.
i'm using win7 x64
The code is written in c#:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr FindWindow(string strClassName, string strWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lpClassName, string lpWindowTitle);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hwnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern IntPtr GetWindow(IntPtr hWnd, uint wCmd);
//
// Finds a window in the whole tree of childs for a parent window.
//
static IntPtr FindWindowRecursive(IntPtr hParent, string szClass, string szCaption)
{
IntPtr hResult = FindWindowEx(hParent, IntPtr.Zero, szClass, szCaption);
if (hResult != IntPtr.Zero)
return hResult; // found it
// enumerate all childs and if found one that has childs go in
IntPtr hChild = FindWindowEx(hParent, IntPtr.Zero, null, null); // first child
if (hChild != IntPtr.Zero)
{
// let's enumerate
do
{
hResult = FindWindowRecursive(hChild, szClass, szCaption);
if (hResult != IntPtr.Zero)
return hResult; // found it
} while ((hChild = GetWindow(hChild, GW_HWNDNEXT)) != IntPtr.Zero);
}
return IntPtr.Zero; // no childs, so no window was found
}
static void Main(string[] args)
{
IntPtr win = FindWindow("Omnipage17_MainWnd_Class", "Unbenanntes OmniPage-Dokument 1 - OmniPage");
SetForegroundWindow(win);
ShowWindowAsync(win, SW_RESTORE);
IntPtr ButtonHandle = FindWindowRecursive(win, "BarButton", "c");
SetActiveWindow(win);
//sEND Lbuttondown
IntPtr ptr = SendMessage(ButtonHandle, 0x0201, new IntPtr(0x0001), MakeLParam(81,28));
//Thread.Sleep(10);
//Mousemove
ptr = SendMessage(ButtonHandle, 0x0200, new IntPtr(0x0001), MakeLParam(86,24));
//lbuttonup
ptr = SendMessage(ButtonHandle, 0x0202, new IntPtr(0x0001), MakeLParam(81, 28));
//SendMessage(ButtonHandle, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
}
Here are the messages of spy++ of that button:
If i send the the messages i get following:
i don't if that is that problem but the lbuttondown, buttoup appears 2 times ( S + R ) and if i'm clicking it manuelly it gets 1 message (P)
also tried to do it with WM_CLICK but then i have the same problem with it
Edit:
Using now PostMessage so spy++ shows the same messages as i click it manually, but still the button seems not to be clicked
With this library I have the same problem.
code:
SetForegroundWindow(win);
Rectangle re;
GetWindowRect(ButtonHandle, out re);
Cursor.Position = new Point((re.X + re.Width)/2, (re.Y + re.Height)/2);
WindowsInput.InputSimulator.SimulateKeyDown(WindowsInput.VirtualKeyCode.LBUTTON);
WindowsInput.InputSimulator.SimulateKeyUp(WindowsInput.VirtualKeyCode.LBUTTON);
Message are sent, but button isn't be clicked
Edit:
thanks for that link (http://www.hanselman.com/blog/IntroducingLync2010SuperSimpleAutoAnswerVideoKioskWithFullScreen.aspx), but also with this library i have the same problem :/
code:
SetForegroundWindow(win);
Rectangle re;
GetWindowRect(ButtonHandle, out re);
Cursor.Position = new Point((re.X + re.Width)/2, (re.Y + re.Height)/2);
WindowsInput.InputSimulator.SimulateKeyDown(WindowsInput.VirtualKeyCode.LBUTTON);
WindowsInput.InputSimulator.SimulateKeyUp(WindowsInput.VirtualKeyCode.LBUTTON);
Message are sent, but button isn't be clicked
Edit2:
Answer of user was deleted, because i posted my comment as an answer:
This is not an answer, this belongs in your question. It doesn't match
your code, clearly you are still posting BM_CLICK. Which is wrong, it
should be sent and you should either send BM_CLICK or post the mouse
messages. And you are looking at the wrong window, it is button's
parent that gets the BN_CLICK notification and acts on it. Having the
processs' keyboard state wrong would be a typical failure mode. – Hans
Passant 18 hours ago
Regarding to that, why should it be the parents windows? bcs in spy++ (screenshot below i serached for that button (Class: BarButton) and the handle i get from user32.dll is also the same as that one in spy++
Scott Hanselman recent blogged with something similar to this http://www.hanselman.com/blog/IntroducingLync2010SuperSimpleAutoAnswerVideoKioskWithFullScreen.aspx

How can I get functionality similar to Spy++ in my C# app?

I'm interested in working on a plugin for Keepass, the open-source password manager. Right now, Keepass currently detects what password to copy/paste for you based off of the window title. This prevents Keepass from detecting the current password you need for apps that don't actively update their window title based on the current site (Chrome for instance).
How can I walk through another processes window elements (buttons, labels, textbox) similar to how Spy++ works? When you run Spy++ you can hover over other programs windows and get all kinds of information about various properties concerning various controls (labels, textboxes, etc). Ideally, I'd like my Keepass plugin to enhance the current window detection by walking through the active window's elements in an effort to find a matching account to copy/paste the password.
How can I walk other processes window elements and be able to retrieve label and textbox values using C#?
I've being answering similar questions like this here: How can I detect if a thread has windows handles?. Like it states, the main idea is to enumerate through process windows and their child windows using EnumWindows and EnumChildWindows API calls to get window handles and then call GetWindowText or SendDlgItemMessage with WM_GETTEXT to get window text. I've modified code to make an example which should be doing what you need (sorry it's a bit long :). It iterates through processes and their windows and dumps window text into console.
static void Main(string[] args)
{
foreach (Process procesInfo in Process.GetProcesses())
{
Console.WriteLine("process {0} {1:x}", procesInfo.ProcessName, procesInfo.Id);
foreach (ProcessThread threadInfo in procesInfo.Threads)
{
// uncomment to dump thread handles
//Console.WriteLine("\tthread {0:x}", threadInfo.Id);
IntPtr[] windows = GetWindowHandlesForThread(threadInfo.Id);
if (windows != null && windows.Length > 0)
foreach (IntPtr hWnd in windows)
Console.WriteLine("\twindow {0:x} text:{1} caption:{2}",
hWnd.ToInt32(), GetText(hWnd), GetEditText(hWnd));
}
}
Console.ReadLine();
}
private static IntPtr[] GetWindowHandlesForThread(int threadHandle)
{
_results.Clear();
EnumWindows(WindowEnum, threadHandle);
return _results.ToArray();
}
// enum windows
private delegate int EnumWindowsProc(IntPtr hwnd, int lParam);
[DllImport("user32.Dll")]
private static extern int EnumWindows(EnumWindowsProc x, int y);
[DllImport("user32")]
private static extern bool EnumChildWindows(IntPtr window, EnumWindowsProc callback, int lParam);
[DllImport("user32.dll")]
public static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);
private static List<IntPtr> _results = new List<IntPtr>();
private static int WindowEnum(IntPtr hWnd, int lParam)
{
int processID = 0;
int threadID = GetWindowThreadProcessId(hWnd, out processID);
if (threadID == lParam)
{
_results.Add(hWnd);
EnumChildWindows(hWnd, WindowEnum, threadID);
}
return 1;
}
// get window text
[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);
private static string GetText(IntPtr hWnd)
{
int length = GetWindowTextLength(hWnd);
StringBuilder sb = new StringBuilder(length + 1);
GetWindowText(hWnd, sb, sb.Capacity);
return sb.ToString();
}
// get richedit text
public const int GWL_ID = -12;
public const int WM_GETTEXT = 0x000D;
[DllImport("User32.dll")]
public static extern int GetWindowLong(IntPtr hWnd, int index);
[DllImport("User32.dll")]
public static extern IntPtr SendDlgItemMessage(IntPtr hWnd, int IDDlgItem, int uMsg, int nMaxCount, StringBuilder lpString);
[DllImport("User32.dll")]
public static extern IntPtr GetParent(IntPtr hWnd);
private static StringBuilder GetEditText(IntPtr hWnd)
{
Int32 dwID = GetWindowLong(hWnd, GWL_ID);
IntPtr hWndParent = GetParent(hWnd);
StringBuilder title = new StringBuilder(128);
SendDlgItemMessage(hWndParent, dwID, WM_GETTEXT, 128, title);
return title;
}
hope this helps, regards
Have a look at this article here which contains information about the Managed Spy and why the author wrote the tool.
You can use EnumWindows to find every top-level Chrome window and then call EnumChildWindows recursively (see Jeroen Wiert Pluimers' comment) to get every child of the main window. Alternatively, once you have the main Chrome window, you can use GetWindow to manually navigate the tree since you probably know what you're looking for (3rd child's children collection or something similar).
Once you find your window, you can use SendMessage with a WM_GETTEXT parameter to read the window's label.
You can use HWndSpy. Source code is here.
For the functionality of pointing to a window. You need to SetCapture() so that you get mouse messages that are outside of your window. Then use WindowFromPoint() to convert a mouse position to a Window. You will need to convert the moust position from client coordinates to window coordinates first.
If you try an call SetCapture() anywhere but on a mouse click message, you will probably be ignored. This is the reason that Spy++ makes you click on an Icon and drag and drop it on the window you want to point to.

Categories

Resources