How can I change text on a win32 window? - c#

Looking for hints, tips and search terms for changing the text on a win32 window from C#.
More specifically, I'm trying to change the text on the print dialog from "Print" to "OK", as I am using the dialog to create a print ticket and not do any printing.
How can I find the dialog's window handle? Once I've got it, how would I go about finding the button in the child windows of the form? Once I've found that, how would I change the text on the button? And how can I do all this before the dialog is shown?
There's a similar question here, but it points to a CodeProject article that is waaay more complex than needed and is taking me a bit longer to parse through than I'd like to spend on this. TIA.

You should use Spy++ to take a look at the dialog. The class name is important and the control ID of the button. If it is a native Windows dialog then the class name should be "#32770". In which case you'll have a lot of use for my post in this thread. Here is another in C#. You change the button text by P/Invoking SetWindowText() on the button handle.
using System;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class SetDialogButton : IDisposable {
private Timer mTimer = new Timer();
private int mCtlId;
private string mText;
public SetDialogButton(int ctlId, string txt) {
mCtlId = ctlId;
mText = txt;
mTimer.Interval = 50;
mTimer.Enabled = true;
mTimer.Tick += (o, e) => findDialog();
}
private void findDialog() {
// Enumerate windows to find the message box
EnumThreadWndProc callback = new EnumThreadWndProc(checkWindow);
if (!EnumThreadWindows(GetCurrentThreadId(), callback, IntPtr.Zero)) mTimer.Enabled = false;
}
private bool checkWindow(IntPtr hWnd, IntPtr lp) {
// Checks if <hWnd> is a dialog
StringBuilder sb = new StringBuilder(260);
GetClassName(hWnd, sb, sb.Capacity);
if (sb.ToString() != "#32770") return true;
// Got it, get the STATIC control that displays the text
IntPtr hCtl = GetDlgItem(hWnd, mCtlId);
SetWindowText(hCtl, mText);
// Done
return true;
}
public void Dispose() {
mTimer.Enabled = false;
}
// P/Invoke declarations
private const int WM_SETFONT = 0x30;
private const int WM_GETFONT = 0x31;
private delegate bool EnumThreadWndProc(IntPtr hWnd, IntPtr lp);
[DllImport("user32.dll")]
private static extern bool EnumThreadWindows(int tid, EnumThreadWndProc callback, IntPtr lp);
[DllImport("kernel32.dll")]
private static extern int GetCurrentThreadId();
[DllImport("user32.dll")]
private static extern int GetClassName(IntPtr hWnd, StringBuilder buffer, int buflen);
[DllImport("user32.dll")]
private static extern IntPtr GetDlgItem(IntPtr hWnd, int item);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool SetWindowText(IntPtr hWnd, string txt);
}
Usage:
using (new SetDialogButton(1, "Okay")) {
printDialog1.ShowDialog();
}

Related

How can I get the word under the mouse cursor in Powerpoint 2013 using C#?

I would like to know the word under the mouse cursor in Powerpoint so that it can be used for a screen reader. Accessibility solutions are acceptable if it can distinguish between different words (vs a block).
This is actually really hard, if you do not know what you are doing. There is a easy way and a hard way to do this. Easy way would be to use Microsoft UI automation framework (that includes Powerpoint automation). Alternative frameworks can also be used.
Hard way wold be to directly use win api.
For example: To get window title currently under the mouse.
public static class dllRef
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetCursorPos(out Point lpPoint);
[DllImport("user32.dll")]
private static extern IntPtr WindowFromPoint(Point point);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int RegisterWindowMessage(string lpString);
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
public static extern bool SendMessage(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SendMessage(int hWnd, int Msg, int wparam, int lparam);
public const int WM_USER = 0x400;
public const int WM_COPYDATA = 0x4A;
public const int WM_GETTEXT = 0x000D;
public const int WM_GETTEXTLENGTH = 0x000E;
public static void RegisterControlforMessages()
{
RegisterWindowMessage("WM_GETTEXT");
}
public static string GetText()
{
StringBuilder title = new StringBuilder();
Point p = dllRef.getMousePosition();
var lhwnd = dllRef.WindowFromPoint(p);
var lTextlen = dllRef.SendMessage((int)lhwnd, dllRef.WM_GETTEXTLENGTH, 0, 0).ToInt32();
if (lTextlen > 0)
{
title = new StringBuilder(lTextlen + 1);
SendMessage(lhwnd, WM_GETTEXT, title.Capacity, title);
}
return title.ToString();
}
public static Point getMousePosition()
{
Point p = new Point();
GetCursorPos(out p);
return p;
}
}
and
private void Form1_Load(object sender, EventArgs e)
{
Timer t = new Timer();
t.Interval = 25;
t.Tick += new EventHandler(Timer_Tick);
t.Start();
}
public void Timer_Tick(object sender, EventArgs eArgs)
{
this.label1.Text = dllRef.GetText();
}
In addition you can use Microsoft Spy++
to find if information you are looking for is exposed. Other then that I can really recommend you use automation framework that is layer built on top of this. Google has more then enough examples on this (as well as how to build sophisticated keyloggers).
The same solutions as Margus came to mind. Either UI Automation or PowerPoint interop. Luckily UI Automation works.
The below works in my test putting the mouse over a PowerPoint 2013 text box. Let me know if you think something is missing.
using System.Windows.Automation;
using UIAutomationClient;
String TextUnderCursor()
{
System.Windows.Point point = new System.Windows.Point(Cursor.Position.X, Cursor.Position.Y);
AutomationElement element = AutomationElement.FromPoint(point);
object patternObj;
if (element.TryGetCurrentPattern(TextPattern.Pattern, out patternObj))
{
var textPattern = (TextPattern)patternObj;
return textPattern.DocumentRange.GetText(-1).TrimEnd('\r'); // often there is an extra '\r' hanging off the end.)
} else
{
return "no text found";
}
}
Update Sample http://download.veodin.com/misc/PowerPoint_Screen_Reader.zip
Focus Visual Studio, put the mouse over the PowerPoint then use F5 to run the code

Minimize a folder

I want to minimize a window using C#
Ex : I have opened this path E:\ using
process.start(E:\)
And I want to minimize this path on a certain event.
How can I make that possible?
The following sample Console Application code will minimize all shell explorer views that are opened on E:\ :
class Program
{
static void Main(string[] args)
{
// add a reference to "Microsoft Shell Controls and Automation" COM component
// also add a 'using Shell32;'
Shell shell = new Shell();
dynamic windows = shell.Windows(); // this is a ShellWindows object
foreach (dynamic window in windows)
{
// window is an WebBrowser object
Uri uri = new Uri((string)window.LocationURL);
if (uri.LocalPath == #"E:\")
{
IntPtr hwnd = (IntPtr)window.HWND; // WebBrowser is also an IWebBrowser2 object
MinimizeWindow(hwnd);
}
}
}
static void MinimizeWindow(IntPtr handle)
{
const int SW_MINIMIZE = 6;
ShowWindow(handle, SW_MINIMIZE);
}
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
}
It's using the Shell Objects for Scripting. Note the usage of the dynamic keyword that's mandatory here because there is no cool typelib, and therefore no intellisense either.
Shell32.Shell objShell = new Shell32.Shell();
objShell.MinimizeAll();
this will help you to minimize all the window Not only Folders all(something like windows + M!!!
Your question is not very clear. If you are using a TreeView control see
MSDN Treeview class. You can then: Expand or Collapse items at will.
You can use the configuration file or a Variable
This is a possible solution and only minimizes the window u opened:
private int explorerWindowNumber;
public const int WM_SYSCOMMAND = 0x0112;
public const int SC_MINIMIZE = 0xF020;
[DllImport("user32.dll", SetLastError = true)]
public static extern int GetForegroundWindow();
[DllImport("user32.dll")]
public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
public void button1_Click(object sender, EventArgs e)
{
//Start my window
StartMyExplorer();
}
private void StartMyExplorer()
{
Process.Start("D:\\");
Thread.Sleep(1000);
//Get the window id (int)
explorerWindowNumber = GetForegroundWindow();
}
private void button2_Click(object sender, EventArgs e)
{
//Minimize the window i created
SendMessage(explorerWindowNumber, WM_SYSCOMMAND, SC_MINIMIZE, 0);
}

In a WPF app how do you override the Console Close command?

In my WPF application I use the Console Window as a debug/message window - I have options setup to show and hide the window as the user desires and all that works great. The only issue is that when the user closes the Console window it exits the entire program. How can I override this so that when the user click on the X it just hides the Console instead of exiting the application?
this is what I have so far:
const int SW_HIDE = 0;
const int SW_SHOW = 5;
public static bool ConsoleVisible { get; private set; }
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public static void HideConsole()
{
var handle = GetConsoleWindow();
ShowWindow(handle, SW_HIDE);
ConsoleVisible = false;
}
public static void ShowConsole()
{
var handle = GetConsoleWindow();
ShowWindow(handle, SW_SHOW);
ConsoleVisible = true;
}
** For people wanting to utilize this you need: using System.Runtime.InteropServices;
This code was derived from this answer: https://stackoverflow.com/a/3571628/649382
** Edit **
Looking around a bit more it seems like this is not possible. I saw an answer here: https://stackoverflow.com/a/12015131/649382 that talks about removing the exit button which would also be acceptable, except it looks like the code is in C++ and I can't figure out it's C# alternative.
** Edit 2 **
I was able to figure out the conversion to C# and have written it as the answer below.
So as has been discussed there is no way to prevent the Console Window from closing the WPF/Application Window. Prior to Windows Vista there were some workarounds, but since then they have been removed (probably for security reasons). The work around I was able to come up with was to disable the Exit button on the Console Window and place show/hide options into my application. The application start class looks like this:
using System;
using System.Windows;
using System.Runtime.InteropServices;
namespace MyApp
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public App()
{
ConsoleVisible = true;
DisableConsoleExit();
}
#region Console Window Commands
// Show/Hide
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);
const uint SW_HIDE = 0;
const uint SW_SHOWNORMAL = 1;
const uint SW_SHOWNOACTIVATE = 4; // Show without activating
public static bool ConsoleVisible { get; private set; }
public static void HideConsole()
{
IntPtr handle = GetConsoleWindow();
ShowWindow(handle, SW_HIDE);
ConsoleVisible = false;
}
public static void ShowConsole(bool active = true)
{
IntPtr handle = GetConsoleWindow();
if (active) { ShowWindow(handle, SW_SHOWNORMAL); }
else { ShowWindow(handle, SW_SHOWNOACTIVATE); }
ConsoleVisible = true;
}
// Disable Console Exit Button
[DllImport("user32.dll")]
static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
static extern IntPtr DeleteMenu(IntPtr hMenu, uint uPosition, uint uFlags);
const uint SC_CLOSE = 0xF060;
const uint MF_BYCOMMAND = (uint)0x00000000L;
public static void DisableConsoleExit()
{
IntPtr handle = GetConsoleWindow();
IntPtr exitButton = GetSystemMenu(handle, false);
if (exitButton != null) DeleteMenu(exitButton, SC_CLOSE, MF_BYCOMMAND);
}
#endregion
}
}
Hope this helps everyone out who may have a similar issue.
I think you should look into creating the console using AllocConsole and releasing it using FreeConsole. That way you may be able to give the user the ability to close the console window while keeping your WPF application running.

How to programmatically minimize opened window folders

How can I get the list of opened of folders, enumerate through it and minimize each folder programmatically?
At times some opened folders do steal focus from the tool when jumping from one form in the application to another. Preventing this is of high priority for our client. The customers are visually impaired people, so they access the machine only via screen readers. Minimizing other windows (folders) is not at all a problem, in fact a requirement.
I tried this:
foreach (Process p in Process.GetProcessesByName("explorer"))
{
p.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
}
As expected it did no good.
Update:
From the answers here, I tried this:
delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
static IEnumerable<IntPtr> EnumerateProcessWindowHandles(int processID)
{
List<IntPtr> handles = new List<IntPtr>();
EnumThreadDelegate addWindowHandle = delegate(IntPtr hWnd, IntPtr param)
{
handles.Add(hWnd);
return true;
};
foreach (ProcessThread thread in Process.GetProcessById(processID).Threads)
EnumThreadWindows(thread.Id, addWindowHandle, IntPtr.Zero);
return handles;
}
const int SW_MINIMIZED = 6;
[DllImport("user32.dll")]
static extern int ShowWindow(IntPtr hWnd, int nCmdShow);
private void button1_Click(object sender, EventArgs e)
{
foreach (IntPtr handle in EnumerateProcessWindowHandles(Process.GetProcessesByName("explorer")[0].Id))
ShowWindow(handle, SW_MINIMIZED);
}
This creates a whole lot of invisible explorer windows to be suddenly listed in the taksbar out of no where. I am bit noob in dealing with Windows API, so the code itself will actually help.
Please try this (the code is somewhat messy but for the purpose you should be able to go through it ;))
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Text;
using System.Globalization;
namespace WindowsFormsApplication20
{
static class Program
{
[STAThread]
static void Main()
{
foreach (IntPtr handle in EnumerateProcessWindowHandles(Process.GetProcessesByName("explorer")[0].Id))
{
SendMessage(handle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
}
}
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
static string GetDaClassName(IntPtr hWnd)
{
int nRet;
StringBuilder ClassName = new StringBuilder(100);
//Get the window class name
nRet = GetClassName(hWnd, ClassName, ClassName.Capacity);
if (nRet != 0)
{
return ClassName.ToString();
}
else
{
return null;
}
}
delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
static IEnumerable<IntPtr> EnumerateProcessWindowHandles(int processID)
{
List<IntPtr> handles = new List<IntPtr>();
EnumThreadDelegate addWindowHandle = delegate(IntPtr hWnd, IntPtr param)
{
string className = GetDaClassName(hWnd);
switch (className)
{
case null:
break;
case "ExploreWClass":
handles.Add(hWnd);
break;
case "CabinetWClass":
handles.Add(hWnd);
break;
default:
break;
}
return true;
};
foreach (ProcessThread thread in Process.GetProcessById(processID).Threads)
EnumThreadWindows(thread.Id, addWindowHandle, IntPtr.Zero);
return handles;
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
const int WM_SYSCOMMAND = 274;
const int SC_MINIMIZE = 0xF020;
}
}
Best regards,
Żubrówka
//Create Instance Of Shell Class by referencing COM Library "Microsoft Shell Controls And Automation" -shell32.dll
Shell32.ShellClass objShell = new Shell32.ShellClass();
//Show Desktop
((Shell32.IShellDispatch4)objShell).ToggleDesktop();
Edit: to show your application (Activate or Maximize/Restore) after toggling actually turned out to be quite difficult:
I tried:
Application.DoEvents();
System.Threading.Thread.Sleep(5000);
Even overriding the WndProc didn't manage to capture the event:
private const Int32 WM_SYSCOMMAND = 0x112;
private const Int32 SC_MINIMIZE = 0xf020;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_SYSCOMMAND)
{
if (m.WParam.ToInt32() == SC_MINIMIZE)
return;
}
base.WndProc(ref m);
}
So I suggest instead of Minimising all other windows, just stick yours on top during the operation, then once your finished turn off Always On Top:
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;
public static void MakeTopMost (IntPtr hWnd)
{
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
}
There is a less 'hacky' solution than the accepted answer available here: Minimize a folder
It's based on the Shell Objects for Scripting. Sample:
const int SW_SHOWMINNOACTIVE = 7;
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
static void MinimizeWindow(IntPtr handle)
{
ShowWindow(handle, SW_SHOWMINNOACTIVE);
}
//call it like:
foreach (IWebBrowser2 window in new Shell().Windows())
{
if (window.Name == "Windows Explorer")
MinimizeWindow((IntPtr)window.HWND);
}
The same thing can be achieved using the Internet Explorer Object Model
// add a reference to "Microsoft Internet Controls" COM component
// also add a 'using SHDocVw;'
foreach (IWebBrowser2 window in new ShellWindows())
{
if (window.Name == "Windows Explorer")
MinimizeWindow((IntPtr)window.HWND);
}
If you are willing to use p-invoke you can use EnumThreadWindows to enumerate all windows of a process. Then use ShowWindow to minimize them.
I know this is an old post, but here is a much shorter, simpler way in case people are still looking for a solution:
Using Windows API:
Declare a windows handle: (minimizes the calling executable)
HWND wHandle; //can be any scope - I use it in main
Call the following anywhere (depending on scope of wHandle):
wHandle = GetActiveWindow();
ShowWindow(wHandle, SW_SHOWMINNOACTIVE);

Get key pressed from other application C#

I would like to get the key pressed by user on other application. For example, in notepad, not the program itself. Here is my coding that using PostMessage method to continuously send key to notepad but however, I wish to stop it when some key is pressed.
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(
string ClassName,
string WindowName);
[DllImport("User32.dll")]
public static extern IntPtr FindWindowEx(
IntPtr Parent,
IntPtr Child,
string lpszClass,
string lpszWindows);
[DllImport("User32.dll")]
public static extern Int32 PostMessage(
IntPtr hWnd,
int Msg,
int wParam,
int lParam);
private const int WM_KEYDOWN = 0x100;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(Test));
t.Start();
}
Boolean ControlKeyDown = true;
public void Test()
{
// retrieve Notepad main window handle
IntPtr Notepad = FindWindow("Notepad", "Untitled - Notepad");
if (!Notepad.Equals(IntPtr.Zero))
{
// retrieve Edit window handle of Notepad
IntPtr Checking = FindWindowEx(Notepad, IntPtr.Zero, "Edit", null);
if (!Checking.Equals(IntPtr.Zero))
{
while (ControlKeyDown)
{
Thread.Sleep(100);
PostMessage(Checking, WM_KEYDOWN, (int)Keys.A, 0);
}
}
}
}
Hence, my idea is set the ControlKeyDown to false when user pressed X key in notepad. After research through internet, I found out this code and edited:
protected override void OnKeyDown(KeyEventArgs kea)
{
if (kea.KeyCode == Keys.X)
ControlKeyDown = false;
}
Yes, by this, it definitely will stop the looping but this is not I want as it will stop the loop when user presses X key on the program but not in notepad. This is because the KeyEventArgs is System.Windows.Forms.KeyEventArgs and not Notepad.
Need help :(
I guess you are looking for keyboard hooking. See this article, it is c++ but you appear to be agile in p/invoke so you probably get how to port it easily.
You might want to take a look at the SetWindowsHookEx function in the user32.dll.
I also user the gma.UserActivity library to do it in a project.

Categories