4

I was trying to accomplish this but did not get good results. I used GetForegroundWindow(), AttachThreadInput(uint,uint,bool) and GetFocus() functions to send the strings to another window. It works with Notepad, Wordpad and other applications, but not with Microsoft Word.

int foregroundWindowHandle = GetForegroundWindow();
uint remoteThreadId = GetWindowThreadProcessId(foregroundWindowHandle, 0);
uint currentThreadId = GetCurrentThreadId();
bool b = AttachThreadInput(remoteThreadId, currentThreadId, true);
int focused = GetFocus();
int d = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
b = AttachThreadInput(remoteThreadId, currentThreadId, false);
SendMessage(focused , WM_GETTEXT, builder.Capacity, builder);
clip = builder.ToString();

//Text operations...

SendMessage(focused, WM_SETTEXT, 0, builder);

That's the code I have, but it doesn't work with Word. I know that Word uses Custom Controls, but I think there should be another way to handle this.

For example: Windows Speech Recognition sends text to every application which has focus even if it is Word. I don't think they made the inputs manually.

I thought to use the SendInputs function but I don't know how to make that.

Aurelio De Rosa
  • 21,856
  • 8
  • 48
  • 71
Francerz
  • 43
  • 1
  • 1
  • 4
  • For what it's worth, you can use GetGUIThreadInfo to get the focused HWND without having to AttachThreadInput. But in any case, SendInput is the simplest way to send keystrokes to the currently focused window. – BrendanMcK Jan 17 '12 at 12:50

2 Answers2

15

Check whether the following works; you just need to call SendString. This should work for any application that can receive text whose window is currently active.

All code except the first method is courtesy of pinvoke.net.

/// <summary>
/// Synthesizes keystrokes corresponding to the specified Unicode string,
/// sending them to the currently active window.
/// </summary>
/// <param name="s">The string to send.</param>
public static void SendString(string s)
{
    // Construct list of inputs in order to send them through a single SendInput call at the end.
    List<INPUT> inputs = new List<INPUT>();

    // Loop through each Unicode character in the string.
    foreach (char c in s)
    {
        // First send a key down, then a key up.
        foreach (bool keyUp in new bool[] { false, true })
        {
            // INPUT is a multi-purpose structure which can be used 
            // for synthesizing keystrokes, mouse motions, and button clicks.
            INPUT input = new INPUT
            {
                // Need a keyboard event.
                type = INPUT_KEYBOARD,
                u = new InputUnion
                {
                    // KEYBDINPUT will contain all the information for a single keyboard event
                    // (more precisely, for a single key-down or key-up).
                    ki = new KEYBDINPUT
                    {
                        // Virtual-key code must be 0 since we are sending Unicode characters.
                        wVk = 0,

                        // The Unicode character to be sent.
                        wScan = c,

                        // Indicate that we are sending a Unicode character.
                        // Also indicate key-up on the second iteration.
                        dwFlags = KEYEVENTF_UNICODE | (keyUp ? KEYEVENTF_KEYUP : 0),

                        dwExtraInfo = GetMessageExtraInfo(),
                    }
                }
            };

            // Add to the list (to be sent later).
            inputs.Add(input);
        }
    }

    // Send all inputs together using a Windows API call.
    SendInput((uint)inputs.Count, inputs.ToArray(), Marshal.SizeOf(typeof(INPUT)));
}

const int INPUT_MOUSE = 0;
const int INPUT_KEYBOARD = 1;
const int INPUT_HARDWARE = 2;
const uint KEYEVENTF_EXTENDEDKEY = 0x0001;
const uint KEYEVENTF_KEYUP = 0x0002;
const uint KEYEVENTF_UNICODE = 0x0004;
const uint KEYEVENTF_SCANCODE = 0x0008;
const uint XBUTTON1 = 0x0001;
const uint XBUTTON2 = 0x0002;
const uint MOUSEEVENTF_MOVE = 0x0001;
const uint MOUSEEVENTF_LEFTDOWN = 0x0002;
const uint MOUSEEVENTF_LEFTUP = 0x0004;
const uint MOUSEEVENTF_RIGHTDOWN = 0x0008;
const uint MOUSEEVENTF_RIGHTUP = 0x0010;
const uint MOUSEEVENTF_MIDDLEDOWN = 0x0020;
const uint MOUSEEVENTF_MIDDLEUP = 0x0040;
const uint MOUSEEVENTF_XDOWN = 0x0080;
const uint MOUSEEVENTF_XUP = 0x0100;
const uint MOUSEEVENTF_WHEEL = 0x0800;
const uint MOUSEEVENTF_VIRTUALDESK = 0x4000;
const uint MOUSEEVENTF_ABSOLUTE = 0x8000;

struct INPUT
{
    public int type;
    public InputUnion u;
}

[StructLayout(LayoutKind.Explicit)]
struct InputUnion
{
    [FieldOffset(0)]
    public MOUSEINPUT mi;
    [FieldOffset(0)]
    public KEYBDINPUT ki;
    [FieldOffset(0)]
    public HARDWAREINPUT hi;
}

[StructLayout(LayoutKind.Sequential)]
struct MOUSEINPUT
{
    public int dx;
    public int dy;
    public uint mouseData;
    public uint dwFlags;
    public uint time;
    public IntPtr dwExtraInfo;
}

[StructLayout(LayoutKind.Sequential)]
struct KEYBDINPUT
{
    /*Virtual Key code.  Must be from 1-254.  If the dwFlags member specifies KEYEVENTF_UNICODE, wVk must be 0.*/
    public ushort wVk;
    /*A hardware scan code for the key. If dwFlags specifies KEYEVENTF_UNICODE, wScan specifies a Unicode character which is to be sent to the foreground application.*/
    public ushort wScan;
    /*Specifies various aspects of a keystroke.  See the KEYEVENTF_ constants for more information.*/
    public uint dwFlags;
    /*The time stamp for the event, in milliseconds. If this parameter is zero, the system will provide its own time stamp.*/
    public uint time;
    /*An additional value associated with the keystroke. Use the GetMessageExtraInfo function to obtain this information.*/
    public IntPtr dwExtraInfo;
}

[StructLayout(LayoutKind.Sequential)]
struct HARDWAREINPUT
{
    public uint uMsg;
    public ushort wParamL;
    public ushort wParamH;
}

[DllImport("user32.dll")]
static extern IntPtr GetMessageExtraInfo();

[DllImport("user32.dll", SetLastError = true)]
static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
Douglas
  • 53,759
  • 13
  • 140
  • 188
  • Thank you, it works perfect. I found the pinvoke.net a bit different and I didn't found how to send especifical chars, your method did, again **Thank you.** – Francerz Jan 16 '12 at 19:56
  • Um... But how does it answer he question of sending input *to another application*? Where is the part that deals with another application? – AnT stands with Russia Aug 25 '21 at 19:54
  • @AnT: The content of the OP's question makes it clear that they want to send input to the _foreground_ window. It's "another application" because it's not the .NET app running this code. – Douglas Aug 27 '21 at 16:20
1

Word doesn't use a standard edit control that responds to WM_TEXT. To poke text into Word you could use SendInput to fake input, or COM automation, or UI Automation.

The last of these options, UI Automation, would be my choice and is the way MS intend you to do this nowadays.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • UI Automation requires you to have prior knowledge about the control structure of your application; you would need to code a different solution for each new application you target. – Douglas Jan 16 '12 at 20:08
  • …which is ideal for some scenarios, such as when you want to send your text directly to specific text-boxes or controls in the UI. But I assume the question was for something more generic. – Douglas Jan 16 '12 at 20:10
  • @Douglas On the other hand, SendInput requires the other app to have the input focus and that input focus to be on the right control. So it's horses for courses really. The code in the Q assumed prior knowledge of the app of course, but that could easily just be inadvertent. – David Heffernan Jan 16 '12 at 20:12
  • Input focus on the right control is presumably the same requirement that Windows Speech Recognition implies. The best approach depends on what the purpose of the text-sending application is. – Douglas Jan 16 '12 at 20:14
  • I'd opt for SendInput: UIAutomation is great for extracting information from UI, but not so great for fine-controlled modification of UI content: you can perhaps change all the text in an Edit control in one go (ValuePattern.SetValue()), but not easily append additional characters. SendInput is likely most reliable way to go here, as it also ensures the new text goes through the same processing as text from keyboard. (Most if not nearly all input-centric accessibility tools end up using SendInput to send mouse/keyboard input at the end of the day, I suspect it's also what WSR uses.) – BrendanMcK Jan 17 '12 at 12:48