Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5618b6f210 | ||
|
|
461f4285f3 | ||
|
|
4930d92c20 | ||
|
|
429f683619 | ||
|
|
7a2049ed02 | ||
|
|
0875422116 | ||
|
|
a63b268fb8 | ||
|
|
c5e9ab752a | ||
|
|
0722af4b74 | ||
|
|
34fed5e5ba | ||
|
|
f3cc28e379 | ||
|
|
2a714f8bef | ||
|
|
66029e7b3a | ||
|
|
741821c2a3 | ||
|
|
154a7fd63e |
19
README.md
19
README.md
@@ -2,11 +2,24 @@
|
||||
|
||||
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!
|
||||
|
||||

|
||||
|
||||
(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:
|
||||
This is also available in the Windows Store:
|
||||
|
||||
https://www.microsoft.com/en-us/p/type-clipboard/9p5r4jk7r8h5
|
||||
https://apps.microsoft.com/detail/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.
|
||||
Tested with a wide variety of consoles, including vSphere, Horizon (HTML5), and Citrix.
|
||||
|
||||
## Known Issues
|
||||
|
||||
* **VMWare vSphere Web Console** doesn't work with Chrome/Edge due to the way vSphere processes vScanCode values and what Chrome/Edge provide. [#6](https://github.com/jlaundry/TypeClipboard/issues/6).
|
||||
* I don't have a fix for this. I recommend you use Firefox or VMRC instead.
|
||||
|
||||
## Resolved Issues
|
||||
|
||||
* **VMWare / Omnissa Horizon desktop client** uses a low-level keyboard driver, which SendKeys can't send to.
|
||||
* This may be fixed in v1.5 - I need feedback please!
|
||||
* **Using different keyboard layouts** between source and destination causes the sent keys to be mapped to the destination key location, not the intended source value. [#2](https://github.com/jlaundry/TypeClipboard/issues/2) [#3](https://github.com/jlaundry/TypeClipboard/issues/3)
|
||||
* Fixed in v1.5: https://github.com/jlaundry/TypeClipboard/releases/tag/v1.5.0
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
70
TypeClipboard/Form1.Designer.cs
generated
70
TypeClipboard/Form1.Designer.cs
generated
@@ -28,11 +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.textBox2 = new System.Windows.Forms.TextBox();
|
||||
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
|
||||
@@ -57,26 +61,18 @@
|
||||
// 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);
|
||||
//
|
||||
// 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, 64);
|
||||
this.textBox2.Name = "textBox2";
|
||||
this.textBox2.Size = new System.Drawing.Size(201, 22);
|
||||
this.textBox2.TabIndex = 3;
|
||||
this.textBox2.Text = "(non-clipboard macro)";
|
||||
//
|
||||
// button2
|
||||
//
|
||||
this.button2.Location = new System.Drawing.Point(219, 64);
|
||||
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;
|
||||
@@ -84,12 +80,51 @@
|
||||
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, 99);
|
||||
this.Controls.Add(this.button2);
|
||||
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);
|
||||
@@ -114,8 +149,11 @@
|
||||
private System.Windows.Forms.TextBox textBox1;
|
||||
private System.Windows.Forms.Button button1;
|
||||
private System.Windows.Forms.CheckBox chkHotkey;
|
||||
private System.Windows.Forms.TextBox textBox2;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
@@ -126,5 +128,18 @@ namespace TypeClipboard
|
||||
{
|
||||
_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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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()
|
||||
|
||||
@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Jed Laundry")]
|
||||
[assembly: AssemblyProduct("Type Clipboard")]
|
||||
[assembly: AssemblyCopyright("Copyright © Jed Laundry, 2021")]
|
||||
[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.7.0")]
|
||||
[assembly: AssemblyFileVersion("1.3.7.0")]
|
||||
[assembly: AssemblyVersion("1.5.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.5.0.0")]
|
||||
|
||||
14
TypeClipboard/Properties/Settings.Designer.cs
generated
14
TypeClipboard/Properties/Settings.Designer.cs
generated
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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' ">
|
||||
|
||||
@@ -5,56 +5,33 @@ 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);
|
||||
foreach (Char c in str.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);
|
||||
}
|
||||
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)
|
||||
@@ -66,4 +43,474 @@ namespace TypeClipboard
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<Identity
|
||||
Name="3373JedLaundry.TypeClipboard"
|
||||
Publisher="CN=CE320BA0-58BC-4F47-AF5B-94DB661147CC"
|
||||
Version="1.3.7.0" />
|
||||
Version="1.5.0.0" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>TypeClipboard</DisplayName>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<AppxBundlePlatforms>neutral</AppxBundlePlatforms>
|
||||
<AppInstallerUri>C:\temp</AppInstallerUri>
|
||||
<HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks>
|
||||
<PackageCertificateThumbprint>DFF25B896771BFB75D948AD107C34534A21A08B1</PackageCertificateThumbprint>
|
||||
<PackageCertificateKeyFile>TypeClipboardAppx_TemporaryKey.pfx</PackageCertificateKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<AppxBundle>Always</AppxBundle>
|
||||
@@ -108,6 +108,7 @@
|
||||
<Content Include="Images\Square44x44Logo.targetsize-24_altform-unplated.png" />
|
||||
<Content Include="Images\StoreLogo.png" />
|
||||
<Content Include="Images\Wide310x150Logo.scale-200.png" />
|
||||
<None Include="TypeClipboardAppx_TemporaryKey.pfx" />
|
||||
<None Include="Package.StoreAssociation.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
5
global.json
Normal file
5
global.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "4.7.1"
|
||||
}
|
||||
}
|
||||
BIN
screenshot.png
Normal file
BIN
screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.1 KiB |
8
teststrings.txt
Normal file
8
teststrings.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
~!@#$%^&*()_+
|
||||
`1234567890-=
|
||||
qwertyuiop[]\
|
||||
QWERTYUIOP{}|
|
||||
asdfghjkl;'
|
||||
ASDFGHJKL:"
|
||||
zxcvbnm,./
|
||||
ZXCVBNM<>?
|
||||
Reference in New Issue
Block a user