13 Commits

Author SHA1 Message Date
Jed Laundry
429f683619 prepare for v1.5 packaging release 2025-06-07 09:05:28 +12:00
Jed Laundry
0875422116 Update AssemblyInfo.cs 2025-04-06 10:37:16 +12:00
Jed Laundry
a63b268fb8 wip: refactor SendKeys to user32 SendInput 2025-04-06 10:18:08 +12:00
Jed Laundry
c5e9ab752a Fix F8 hotkey not respecting Enter option
The Typer._typeEnter property was not being updated, because the LowLevelKeyboardListener was creating it's own instance, without reading settings
2025-04-06 10:05:07 +12:00
Jed Laundry
0722af4b74 v1.4.0: Add option to type newlines 2022-06-25 17:31:17 +12:00
Jed Laundry
34fed5e5ba Add files via upload 2021-10-05 12:36:30 +13:00
Jed Laundry
f3cc28e379 Delete screenshot.png 2021-10-05 12:36:18 +13:00
Jed Laundry
2a714f8bef Add files via upload 2021-10-05 12:35:51 +13:00
Jed Laundry
66029e7b3a Update README.md 2021-10-05 12:35:41 +13:00
Jed Laundry
741821c2a3 Add files via upload 2021-10-05 12:33:58 +13:00
Jed Laundry
154a7fd63e update for 1.3.8 2021-08-18 10:15:39 +12:00
Jed Laundry
264ba03421 Update 1.3.7.0
Add macro text box, allow minimizing
2021-08-14 12:44:11 +12:00
Jed Laundry
60e7804aba Create README.md 2020-09-16 16:50:57 +12:00
18 changed files with 651 additions and 80 deletions

14
README.md Normal file
View File

@@ -0,0 +1,14 @@
# TypeClipboard
For IT professionals everywhere who are sick of typing long complex passwords into remote consoles. Just copy the password into your clipboard, select the password box, and press F8 or click Type!
![Screenshot of Type Clipboard](/screenshot.png)
(Doesn't have to be a password either, also useful for URLs, Base64-encoded things, etc.)
Also, if you don't want to compile yourself, this is also available in the Windows Store:
https://www.microsoft.com/en-us/p/type-clipboard/9p5r4jk7r8h5
Tested with a wide variety of consoles, including vSphere, Horizon (HTML5), and Citrix. There is a known issue where VMWare Horizon's client seems to be using a very low-level keyboard driver, so it doesn't work with it. Sorry.

View File

@@ -13,6 +13,9 @@
<setting name="enableHotkey" serializeAs="String">
<value>False</value>
</setting>
<setting name="enableEnter" serializeAs="String">
<value>False</value>
</setting>
</TypeClipboard.Properties.Settings>
</userSettings>
</configuration>

View File

@@ -59,21 +59,4 @@ namespace TypeClipboard
}
}
}
internal static class NativeMethods
{
// See http://msdn.microsoft.com/en-us/library/ms649021%28v=vs.85%29.aspx
public const int WM_CLIPBOARDUPDATE = 0x031D;
public static IntPtr HWND_MESSAGE = new IntPtr(-3);
// See http://msdn.microsoft.com/en-us/library/ms632599%28VS.85%29.aspx#message_only
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AddClipboardFormatListener(IntPtr hwnd);
// See http://msdn.microsoft.com/en-us/library/ms633541%28v=vs.85%29.aspx
// See http://msdn.microsoft.com/en-us/library/ms649033%28VS.85%29.aspx
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
}
}

View File

@@ -28,9 +28,15 @@
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.textBox1 = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.chkHotkey = new System.Windows.Forms.CheckBox();
this.button2 = new System.Windows.Forms.Button();
this.textBox2 = new System.Windows.Forms.TextBox();
this.button3 = new System.Windows.Forms.Button();
this.chkEnter = new System.Windows.Forms.CheckBox();
this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
this.SuspendLayout();
//
// textBox1
@@ -55,27 +61,77 @@
// chkHotkey
//
this.chkHotkey.AutoSize = true;
this.chkHotkey.Location = new System.Drawing.Point(12, 41);
this.chkHotkey.Location = new System.Drawing.Point(172, 42);
this.chkHotkey.Name = "chkHotkey";
this.chkHotkey.Size = new System.Drawing.Size(109, 17);
this.chkHotkey.Size = new System.Drawing.Size(73, 17);
this.chkHotkey.TabIndex = 2;
this.chkHotkey.Text = "Enable F8 hotkey";
this.chkHotkey.Text = "F8 hotkey";
this.toolTip1.SetToolTip(this.chkHotkey, "Enables the F8 hotkey");
this.chkHotkey.UseVisualStyleBackColor = true;
this.chkHotkey.CheckedChanged += new System.EventHandler(this.chkHotkey_CheckedChanged);
//
// button2
//
this.button2.Location = new System.Drawing.Point(219, 65);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(103, 22);
this.button2.TabIndex = 4;
this.button2.Text = "Type (2s delay)";
this.button2.UseVisualStyleBackColor = true;
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// textBox2
//
this.textBox2.Font = new System.Drawing.Font("Consolas", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.textBox2.Location = new System.Drawing.Point(12, 65);
this.textBox2.Name = "textBox2";
this.textBox2.ReadOnly = true;
this.textBox2.Size = new System.Drawing.Size(201, 22);
this.textBox2.TabIndex = 5;
//
// button3
//
this.button3.Location = new System.Drawing.Point(12, 39);
this.button3.Name = "button3";
this.button3.Size = new System.Drawing.Size(154, 22);
this.button3.TabIndex = 6;
this.button3.Text = "Copy clipboard to buffer";
this.button3.UseVisualStyleBackColor = true;
this.button3.Click += new System.EventHandler(this.button3_Click);
//
// chkEnter
//
this.chkEnter.AutoSize = true;
this.chkEnter.Location = new System.Drawing.Point(244, 42);
this.chkEnter.Name = "chkEnter";
this.chkEnter.Size = new System.Drawing.Size(78, 17);
this.chkEnter.TabIndex = 7;
this.chkEnter.Text = "Type Enter";
this.toolTip1.SetToolTip(this.chkEnter, "If set, Type will type newline (\\n) as Enter, which is useful for large blobs of " +
"text.\r\n\r\nIf unset, Type will stop before the first newline, which is useful for " +
"passwords.");
this.chkEnter.UseVisualStyleBackColor = true;
this.chkEnter.CheckedChanged += new System.EventHandler(this.chkEnter_CheckedChanged);
//
// toolTip1
//
this.toolTip1.ShowAlways = true;
//
// Form1
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.ClientSize = new System.Drawing.Size(334, 66);
this.ClientSize = new System.Drawing.Size(334, 99);
this.Controls.Add(this.chkEnter);
this.Controls.Add(this.button3);
this.Controls.Add(this.textBox2);
this.Controls.Add(this.button2);
this.Controls.Add(this.chkHotkey);
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Form1";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.Text = "Type Clipboard";
this.TopMost = true;
this.Activated += new System.EventHandler(this.Form1_Activated);
@@ -93,6 +149,11 @@
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.CheckBox chkHotkey;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.Button button3;
private System.Windows.Forms.CheckBox chkEnter;
private System.Windows.Forms.ToolTip toolTip1;
}
}

View File

@@ -89,11 +89,13 @@ namespace TypeClipboard
private void Form1_Load(object sender, EventArgs e)
{
_listener = new LowLevelKeyboardListener();
// Changing the Checked property also hooks the listener
_tc = new Typer();
_listener = new LowLevelKeyboardListener(_tc);
// Changing the chkHotkey.Checked property also hooks the listener
chkHotkey.Checked = Properties.Settings.Default.enableHotkey;
_tc = new Typer();
// Changing the chkEnter.Checked property also changes _tc.TypeEnter property
chkEnter.Checked = Properties.Settings.Default.enableEnter;
ClipboardNotification.ClipboardUpdate += delegate (object cb_sender, EventArgs cb_e) {
UpdateTextbox();
@@ -121,5 +123,23 @@ namespace TypeClipboard
Properties.Settings.Default.Save();
}
private void button2_Click(object sender, EventArgs e)
{
_tc.Type(textBox2.Text);
}
private void button3_Click(object sender, EventArgs e)
{
String clipboard = Clipboard.GetText(TextDataFormat.UnicodeText);
textBox2.Text = clipboard;
}
private void chkEnter_CheckedChanged(object sender, EventArgs e)
{
Properties.Settings.Default.enableEnter = chkEnter.Checked;
_tc.TypeEnter = chkEnter.Checked;
Properties.Settings.Default.Save();
}
}
}

View File

@@ -117,4 +117,13 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View File

@@ -51,11 +51,12 @@ namespace TypeClipboard
private LowLevelKeyboardProc _proc;
private IntPtr _hookID = IntPtr.Zero;
private Typer _tc = new Typer();
public Typer _tc;
public LowLevelKeyboardListener()
public LowLevelKeyboardListener(Typer tc)
{
_proc = HookCallback;
_tc = tc;
}
public void HookKeyboard()

View File

@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Jed Laundry")]
[assembly: AssemblyProduct("Type Clipboard")]
[assembly: AssemblyCopyright("Copyright © Jed Laundry, 2020")]
[assembly: AssemblyCopyright("Copyright © Jed Laundry, 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.3.6.0")]
[assembly: AssemblyFileVersion("1.3.6.0")]
[assembly: AssemblyVersion("1.5.0.0")]
[assembly: AssemblyFileVersion("1.5.0.0")]

View File

@@ -12,7 +12,7 @@ namespace TypeClipboard.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.5.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.2.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -34,5 +34,17 @@ namespace TypeClipboard.Properties {
this["enableHotkey"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool enableEnter {
get {
return ((bool)(this["enableEnter"]));
}
set {
this["enableEnter"] = value;
}
}
}
}

View File

@@ -5,5 +5,8 @@
<Setting Name="enableHotkey" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="enableEnter" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
</Settings>
</SettingsFile>

View File

@@ -25,7 +25,7 @@
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.1.0.%2a</ApplicationVersion>
<ApplicationVersion>1.5.0.0</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
@@ -37,7 +37,7 @@
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<WarningLevel>0</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">

View File

@@ -5,60 +5,512 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Input;
namespace TypeClipboard
{
class Typer
public class Typer
{
private const int INTERKEY_DELAY = 20;
private const int INTERKEY_DELAY = 50;
private bool _typeEnter = false;
public bool TypeEnter { get => _typeEnter; set => _typeEnter = value; }
public void Type(String str, int delay = 2000)
{
Thread.Sleep(delay);
NativeMethods.BlockInput(true);
//KeyboardTyper.Reset();
KeyboardTyper.Type(str, _typeEnter, INTERKEY_DELAY);
//KeyboardTyper.Press(Key.LeftShift);
//KeyboardTyper.Type("hello, capitalized world");
//KeyboardTyper.Release(Key.LeftShift);
NativeMethods.BlockInput(false);
}
public void TypeClipboard(int delay = 2000)
{
if (Clipboard.ContainsText(TextDataFormat.UnicodeText))
{
String clipboard = Clipboard.GetText(TextDataFormat.UnicodeText);
Thread.Sleep(delay);
foreach (Char c in clipboard.ToCharArray())
{
// Some characters have special meaning
// https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/sendkeys-statement
switch (c)
{
case '\n':
return;
case '\r':
return;
case '{':
SendKeys.Send("{{}");
break;
case '}':
SendKeys.Send("{}}");
break;
case '+':
SendKeys.Send("{+}");
break;
case '^':
SendKeys.Send("{^}");
break;
case '%':
SendKeys.Send("{%}");
break;
case '~':
SendKeys.Send("{~}");
break;
case '(':
SendKeys.Send("{(}");
break;
case ')':
SendKeys.Send("{)}");
break;
default:
SendKeys.Send(c.ToString());
break;
}
Thread.Sleep(INTERKEY_DELAY);
}
this.Type(clipboard, delay);
}
}
}
// https://gist.github.com/obviliontsk/90403a0fea8c24258570f3a577704864
/// <summary>
/// Native methods
/// </summary>
public static partial class NativeMethods
{
// See http://msdn.microsoft.com/en-us/library/ms649021%28v=vs.85%29.aspx
public const int WM_CLIPBOARDUPDATE = 0x031D;
public static IntPtr HWND_MESSAGE = new IntPtr(-3);
// See http://msdn.microsoft.com/en-us/library/ms632599%28VS.85%29.aspx#message_only
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AddClipboardFormatListener(IntPtr hwnd);
// See http://msdn.microsoft.com/en-us/library/ms633541%28v=vs.85%29.aspx
// See http://msdn.microsoft.com/en-us/library/ms649033%28VS.85%29.aspx
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
//User32 wrappers cover API's used for Mouse input
#region User32
// Two special bitMasks we define to be able to grab
// shift and character information out of a VKey.
internal const int VKeyShiftMask = 0x0100;
internal const int VKeyCharMask = 0x00FF;
// Various Win32 constants
internal const int KeyeventfExtendedkey = 0x0001;
internal const int KeyeventfKeyup = 0x0002;
internal const int KeyeventfUnicode = 0x0004;
internal const int KeyeventfScancode = 0x0008;
internal const int MouseeventfVirtualdesk = 0x4000;
internal const int SMXvirtualscreen = 76;
internal const int SMYvirtualscreen = 77;
internal const int SMCxvirtualscreen = 78;
internal const int SMCyvirtualscreen = 79;
internal const int XButton1 = 0x0001;
internal const int XButton2 = 0x0002;
internal const int WheelDelta = 120;
internal const int InputMouse = 0;
internal const int InputKeyboard = 1;
// Various Win32 data structures
[StructLayout(LayoutKind.Sequential)]
internal struct INPUT
{
internal int type;
internal INPUTUNION union;
};
[StructLayout(LayoutKind.Explicit)]
internal struct INPUTUNION
{
[FieldOffset(0)]
internal MOUSEINPUT mouseInput;
[FieldOffset(0)]
internal KEYBDINPUT keyboardInput;
};
[StructLayout(LayoutKind.Sequential)]
internal struct MOUSEINPUT
{
internal int dx;
internal int dy;
internal int mouseData;
internal int dwFlags;
internal int time;
internal IntPtr dwExtraInfo;
};
[StructLayout(LayoutKind.Sequential)]
internal struct KEYBDINPUT
{
internal short wVk;
internal short wScan;
internal int dwFlags;
internal int time;
internal IntPtr dwExtraInfo;
};
[StructLayout(LayoutKind.Sequential)]
internal struct POINT
{
public readonly int X;
public readonly int Y;
}
[Flags]
internal enum SendMouseInputFlags
{
Move = 0x0001,
LeftDown = 0x0002,
LeftUp = 0x0004,
RightDown = 0x0008,
RightUp = 0x0010,
MiddleDown = 0x0020,
MiddleUp = 0x0040,
XDown = 0x0080,
XUp = 0x0100,
Wheel = 0x0800,
Absolute = 0x8000,
};
// Importing various Win32 APIs that we need for input
[DllImport("user32.dll")]
public static extern int GetSystemMetrics(int nIndex);
// DllImport CharSet = CharSet.Auto -> Unicode / Utf.16 for windows, so 'W'
[DllImport("user32.dll", EntryPoint = "MapVirtualKeyW")]
public static extern int MapVirtualKey(int nVirtKey, int nMapType);
[DllImport("user32.dll", SetLastError = true)]
internal static extern int SendInput(int nInputs, ref INPUT mi, int cbSize);
[DllImport("user32.dll", EntryPoint = "BlockInput")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool BlockInput([MarshalAs(UnmanagedType.Bool)] bool fBlockIt);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(out POINT lpPoint);
#endregion
}
/// <summary>
/// Exposes a simple interface to common keyboard operations, allowing the user to simulate keyboard input.
/// </summary>
/// <example>
/// The following code types "Hello world" with the specified casing,
/// and then types "hello, capitalized world" which will be in all caps because
/// the left shift key is being held down.
/// To send input with hotkeys use BlockInput method (app will need admin rights) to block all users inputs
/// and reset inputs state, so they won't interfere
/// <code>
/**
NativeMethods.BlockInput(true);
Keyboard.Reset();
Keyboard.Type("Hello world");
Keyboard.Press(Key.LeftShift);
Keyboard.Type("hello, capitalized world");
Keyboard.Release(Key.LeftShift);
NativeMethods.BlockInput(false);
*/
/// </code>
/// </example>
public static class KeyboardTyper
{
#region Public Members
/// <summary>
/// Presses down a key.
/// </summary>
/// <param name="key">The key to press.</param>
public static void Press(Key key)
{
SendKeyboardInput(key, true);
}
/// <summary>
/// Releases a key.
/// </summary>
/// <param name="key">The key to release.</param>
public static void Release(Key key)
{
SendKeyboardInput(key, false);
}
/// <summary>
/// Presses down a key.
/// </summary>
/// <param name="input">The key to press.</param>
public static void Press(char input)
{
SendKeyboardInput(input, true);
}
/// <summary>
/// Releases a key.
/// </summary>
/// <param name="input">The key to release.</param>
public static void Release(char input)
{
SendKeyboardInput(input, false);
}
/// <summary>
/// Resets the system keyboard to a clean state.
/// </summary>
public static void Reset()
{
return;
foreach (Key key in Enum.GetValues(typeof(Key)))
{
// TODO System.Windows.Input.Keyboard exists from WPF, this is WinForms...
//if (key != Key.None && (System.Windows.Input.Keyboard.GetKeyStates(key) & KeyStates.Down) > 0)
//{
// Release(key);
//}
}
}
/// <summary>
/// Performs a press-and-release operation for the specified key, which is effectively equivalent to typing.
/// </summary>
/// <param name="key">The key to press.</param>
/// <param name="releaseDelayMs">Delay before key release</param>
/// <exception cref="ArgumentOutOfRangeException">releaseDelayMs less than 0</exception>
public static void Type(Key key, int releaseDelayMs = 0)
{
if (releaseDelayMs < 0) throw new ArgumentOutOfRangeException(nameof(releaseDelayMs), releaseDelayMs, "Less than zero");
Press(key);
if (releaseDelayMs > 0)
Thread.Sleep(releaseDelayMs);
Release(key);
}
/// <summary>
/// Performs a press-and-release operation for the specified key specific amount of times.
/// </summary>
/// <param name="key">The key to press.</param>
/// <param name="amountToType"></param>
/// <param name="inputDelayMs">Delay after releasing key</param>
/// <param name="releaseDelayMs">Delay before key release</param>
/// <exception cref="ArgumentOutOfRangeException">releaseDelayMs less than 0 or amountToType less than 1 or inputDelayMs less than 0</exception>
public static void TypeKeyNTimes(Key key, int amountToType = 1, int inputDelayMs = 0, int releaseDelayMs = 0)
{
if (amountToType < 1) throw new ArgumentOutOfRangeException(nameof(amountToType), amountToType, "Less than one");
if (inputDelayMs < 0) throw new ArgumentOutOfRangeException(nameof(inputDelayMs), releaseDelayMs, "Less than zero");
for (int i = 0; i < amountToType; i++)
{
Type(key, releaseDelayMs);
if (inputDelayMs > 0)
Thread.Sleep(releaseDelayMs);
}
}
/// <summary>
/// Types the specified text.
/// </summary>
/// <param name="text">The text to type.</param>
/// <param name="inputDelayMs">Delay between typing each key</param>
/// <param name="releaseDelayMs">Delay before key release</param>
/// <exception cref="ArgumentOutOfRangeException">releaseDelayMs less than 0 or inputDelayMs less than 0</exception>
public static void Type(string text, bool typeEnter = false, int inputDelayMs = 0, int releaseDelayMs = 0)
{
int delay = 0;
if (text.Length > 1)
delay = inputDelayMs;
foreach (char c in text)
{
switch (c)
{
case '\n':
if (typeEnter)
{
Press(Key.Return);
if (releaseDelayMs > 0) Thread.Sleep(releaseDelayMs);
Release(Key.Return);
break;
}
else
// Stop at the first line break
return;
case '\r':
break;
default:
Type(c, releaseDelayMs);
break;
}
if (delay > 0) Thread.Sleep(delay);
}
}
/// <summary>
/// Types the specified char.
/// </summary>
/// <param name="input">The char to type.</param>
/// <param name="releaseDelayMs">Delay before key release</param>
/// <exception cref="ArgumentOutOfRangeException">releaseDelayMs less than 0 or inputDelayMs less than 0</exception>
public static void Type(char input, int releaseDelayMs = 0)
{
if (releaseDelayMs < 0) throw new ArgumentOutOfRangeException(nameof(releaseDelayMs), releaseDelayMs, "Less than zero");
Press(input);
if (releaseDelayMs > 0) Thread.Sleep(releaseDelayMs);
Release(input);
}
/// <summary>
/// Types a key while a set of modifier keys are being pressed. Modifer keys
/// are pressed in the order specified and released in reverse order.
/// </summary>
/// <param name="key">Key to type.</param>
/// <param name="modifierKeys">Set of keys to hold down with key is typed.</param>
/// <param name="releaseDelayMs">Delay before key release</param>
public static void Type(Key key, Key[] modifierKeys, int releaseDelayMs = 0)
{
foreach (Key modifierKey in modifierKeys)
{
if (modifierKey == Key.None)
continue;
Press(modifierKey);
}
Type(key, releaseDelayMs);
foreach (Key modifierKey in modifierKeys.Reverse())
{
if (modifierKey == Key.None)
continue;
Release(modifierKey);
}
}
/// <summary>
/// Types a key while a set of modifier keys are being pressed. Modifer keys
/// are pressed in the order Ctrl->Shift->Alt->Win.
/// </summary>
/// <param name="key">Key to type.</param>
/// <param name="modifierKeys">Set of ModifierKeys enum flags to hold down with key is typed. </param>
/// <param name="releaseDelayMs">Delay before key release</param>
public static void Type(Key key, ModifierKeys modifierKeys, int releaseDelayMs = 0)
{
var modifierKeysArray = ModifierKeysToArrayConverter(modifierKeys);
Type(key, modifierKeysArray, releaseDelayMs);
}
#endregion
#region Private Members
/// <summary>
/// Helper method to convert ModifierKeys enum flags to Key array
/// </summary>
/// <param name="modifierKeys">ModifierKeys enum flags</param>
private static Key[] ModifierKeysToArrayConverter(ModifierKeys modifierKeys)
{
Key[] keys = new Key[4];
if (modifierKeys.HasFlag(ModifierKeys.Control))
keys.SetValue(Key.LeftCtrl, 0);
if (modifierKeys.HasFlag(ModifierKeys.Shift))
keys.SetValue(Key.LeftShift, 1);
if (modifierKeys.HasFlag(ModifierKeys.Alt))
keys.SetValue(Key.LeftAlt, 2);
if (modifierKeys.HasFlag(ModifierKeys.Windows))
keys.SetValue(Key.LWin, 3);
return keys;
}
/// <summary>
/// Injects keyboard input into the system.
/// </summary>
/// <param name="key">Indicates the key pressed or released. Can be one of the constants defined in the Key enum.</param>
/// <param name="press">True to inject a key press, false to inject a key release.</param>
private static void SendKeyboardInput(Key key, bool press)
{
NativeMethods.INPUT ki = new NativeMethods.INPUT
{
type = NativeMethods.InputKeyboard
};
ki.union.keyboardInput.wVk = (short)KeyInterop.VirtualKeyFromKey(key);
ki.union.keyboardInput.wScan = (short)NativeMethods.MapVirtualKey(ki.union.keyboardInput.wVk, 0);
int dwFlags = 0;
if (ki.union.keyboardInput.wScan > 0)
{
dwFlags |= NativeMethods.KeyeventfScancode;
}
if (!press)
{
dwFlags |= NativeMethods.KeyeventfKeyup;
}
ki.union.keyboardInput.dwFlags = dwFlags;
if (ExtendedKeys.Contains(key))
{
ki.union.keyboardInput.dwFlags |= NativeMethods.KeyeventfExtendedkey;
}
ki.union.keyboardInput.time = 0;
ki.union.keyboardInput.dwExtraInfo = new IntPtr(0);
if (NativeMethods.SendInput(1, ref ki, Marshal.SizeOf(ki)) == 0)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
/// <summary>
/// Injects keyboard input into the system.
/// </summary>
/// <param name="input">Indicates the character pressed or released..</param>
/// <param name="press">True to inject a key press, false to inject a key release.</param>
private static void SendKeyboardInput(char input, bool press)
{
NativeMethods.INPUT ki = new NativeMethods.INPUT
{
type = NativeMethods.InputKeyboard
};
ki.union.keyboardInput.wVk = 0;
ki.union.keyboardInput.wScan = (short)input;
int dwFlags = 0;
dwFlags |= NativeMethods.KeyeventfUnicode;
if (!press)
{
dwFlags |= NativeMethods.KeyeventfKeyup;
}
ki.union.keyboardInput.dwFlags = dwFlags;
ki.union.keyboardInput.time = 0;
ki.union.keyboardInput.dwExtraInfo = new IntPtr(0);
if (NativeMethods.SendInput(1, ref ki, Marshal.SizeOf(ki)) == 0)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
// From the SDK:
// The extended-key flag indicates whether the keystroke message originated from one of
// the additional keys on the enhanced keyboard. The extended keys consist of the ALT and
// CTRL keys on the right-hand side of the keyboard; the INS, DEL, HOME, END, PAGE UP,
// PAGE DOWN, and arrow keys in the clusters to the left of the numeric keypad; the NUM LOCK
// key; the BREAK (CTRL+PAUSE) key; the PRINT SCRN key; and the divide (/) and ENTER keys in
// the numeric keypad. The extended-key flag is set if the key is an extended key.
//
// - docs appear to be incorrect. Use of Spy++ indicates that break is not an extended key.
// Also, menu key and windows keys also appear to be extended.
private static readonly Key[] ExtendedKeys = {
Key.RightAlt,
Key.RightCtrl,
Key.NumLock,
Key.Insert,
Key.Delete,
Key.Home,
Key.End,
Key.Prior,
Key.Next,
Key.Up,
Key.Down,
Key.Left,
Key.Right,
Key.Apps,
Key.RWin,
Key.LWin
};
// Note that there are no distinct values for the following keys:
// numpad divide
// numpad enter
#endregion
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -9,7 +9,7 @@
<Identity
Name="3373JedLaundry.TypeClipboard"
Publisher="CN=CE320BA0-58BC-4F47-AF5B-94DB661147CC"
Version="1.3.5.0" />
Version="1.5.0.0" />
<Properties>
<DisplayName>TypeClipboard</DisplayName>

View File

@@ -63,7 +63,7 @@
<AppxBundlePlatforms>neutral</AppxBundlePlatforms>
<AppInstallerUri>C:\temp</AppInstallerUri>
<HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks>
<PackageCertificateThumbprint>F1AC865F3B57360B33C17D9B7ECE4A4BFD2EC9CC</PackageCertificateThumbprint>
<PackageCertificateKeyFile>TypeClipboardAppx_TemporaryKey.pfx</PackageCertificateKeyFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<AppxBundle>Always</AppxBundle>
@@ -108,8 +108,8 @@
<Content Include="Images\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Images\StoreLogo.png" />
<Content Include="Images\Wide310x150Logo.scale-200.png" />
<None Include="Package.StoreAssociation.xml" />
<None Include="TypeClipboardAppx_TemporaryKey.pfx" />
<None Include="Package.StoreAssociation.xml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TypeClipboard\TypeClipboard.csproj" />

5
global.json Normal file
View File

@@ -0,0 +1,5 @@
{
"sdk": {
"version": "4.7.1"
}
}

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

8
teststrings.txt Normal file
View File

@@ -0,0 +1,8 @@
~!@#$%^&*()_+
`1234567890-=
qwertyuiop[]\
QWERTYUIOP{}|
asdfghjkl;'
ASDFGHJKL:"
zxcvbnm,./
ZXCVBNM<>?