How to send a string to other application including Microsoft Word

11,093

Solution 1

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);

Solution 2

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.

Share:
11,093
Francerz
Author by

Francerz

Updated on June 06, 2022

Comments

  • Francerz
    Francerz almost 2 years

    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.

  • Francerz
    Francerz over 12 years
    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.
  • Douglas
    Douglas over 12 years
    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
    Douglas over 12 years
    …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.
  • David Heffernan
    David Heffernan over 12 years
    @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.
  • Douglas
    Douglas over 12 years
    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.
  • David Heffernan
    David Heffernan over 12 years
    @user1152446 If this answers your question then please mark it as accepted: meta.stackexchange.com/questions/5234
  • BrendanMcK
    BrendanMcK over 12 years
    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.)
  • AnT stands with Russia
    AnT stands with Russia over 2 years
    Um... But how does it answer he question of sending input to another application? Where is the part that deals with another application?
  • Douglas
    Douglas over 2 years
    @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.