22 Commits
v1.0 ... 1.6.0

Author SHA1 Message Date
Jed Laundry
911d06154c restore SendKeys as an option 2025-06-14 13:40:33 +12:00
Jed Laundry
16cd8c89f8 rebuild the project from VS 2022 template using .NET 4.7.2 2025-06-14 12:52:52 +12:00
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
Jed Laundry
6877f6642a minor tweaks 2020-05-13 16:50:41 +12:00
Jed Laundry
3b4f802a4e Windows Store packaging 2020-05-03 21:42:06 +12:00
Jed Laundry
731fc744d7 Create LICENSE 2020-05-03 20:10:28 +12:00
Jed Laundry
04705ecbb8 Create PRIVACY.md 2020-05-03 20:08:36 +12:00
Jed Laundry
aa57a2aaf9 Icons v2 2020-05-03 16:38:21 +12:00
Jed Laundry
48cd49cfd5 Icon update 2020-05-03 16:24:21 +12:00
Jed Laundry
aa1d0376b4 F8 hotkey, clipboard change update 2020-05-03 09:24:26 +12:00
31 changed files with 1494 additions and 108 deletions

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Jed Laundry
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

12
PRIVACY.md Normal file
View File

@@ -0,0 +1,12 @@
# Privacy Policy
TypeClipboard does one thing, and one thing only: type (through a virtual keyboard) the contents of the clipboard.
It does not:
- store clipboard information
- process clipboard information
- send your data over the internet, especially not to dodgy hackers
That's it.
Sorry this isn't 333 pages long...

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

@@ -1,20 +1,76 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28306.52
# Visual Studio Version 17
VisualStudioVersion = 17.14.36202.13 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TypeClipboard", "TypeClipboard\TypeClipboard.csproj", "{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}"
EndProject
Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "TypeClipboardAppx", "TypeClipboardAppx\TypeClipboardAppx.wapproj", "{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Debug|ARM.ActiveCfg = Debug|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Debug|ARM.Build.0 = Debug|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Debug|ARM64.Build.0 = Debug|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Debug|x64.ActiveCfg = Debug|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Debug|x64.Build.0 = Debug|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Debug|x86.ActiveCfg = Debug|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Debug|x86.Build.0 = Debug|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Release|Any CPU.Build.0 = Release|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Release|ARM.ActiveCfg = Release|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Release|ARM.Build.0 = Release|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Release|ARM64.ActiveCfg = Release|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Release|ARM64.Build.0 = Release|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Release|x64.ActiveCfg = Release|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Release|x64.Build.0 = Release|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Release|x86.ActiveCfg = Release|Any CPU
{AFF27396-3E91-471E-B46B-2D2DA30FE6DB}.Release|x86.Build.0 = Release|Any CPU
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Debug|ARM.ActiveCfg = Debug|ARM
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Debug|ARM.Build.0 = Debug|ARM
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Debug|ARM.Deploy.0 = Debug|ARM
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Debug|ARM64.ActiveCfg = Debug|ARM64
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Debug|ARM64.Build.0 = Debug|ARM64
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Debug|ARM64.Deploy.0 = Debug|ARM64
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Debug|x64.ActiveCfg = Debug|x64
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Debug|x64.Build.0 = Debug|x64
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Debug|x64.Deploy.0 = Debug|x64
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Debug|x86.ActiveCfg = Debug|x86
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Debug|x86.Build.0 = Debug|x86
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Debug|x86.Deploy.0 = Debug|x86
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Release|Any CPU.Build.0 = Release|Any CPU
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Release|Any CPU.Deploy.0 = Release|Any CPU
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Release|ARM.ActiveCfg = Release|ARM
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Release|ARM.Build.0 = Release|ARM
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Release|ARM.Deploy.0 = Release|ARM
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Release|ARM64.ActiveCfg = Release|ARM64
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Release|ARM64.Build.0 = Release|ARM64
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Release|ARM64.Deploy.0 = Release|ARM64
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Release|x64.ActiveCfg = Release|x64
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Release|x64.Build.0 = Release|x64
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Release|x64.Deploy.0 = Release|x64
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Release|x86.ActiveCfg = Release|x86
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Release|x86.Build.0 = Release|x86
{D041BCC6-3C9D-4BDC-8443-2BEC9B290D1A}.Release|x86.Deploy.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -1,6 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="TypeClipboard.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
</configuration>
<userSettings>
<TypeClipboard.Properties.Settings>
<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

@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TypeClipboard
{
//
// https://gist.github.com/glombard/7986317
// from: http://stackoverflow.com/questions/2226920/how-to-monitor-clipboard-content-changes-in-c
//
/// <summary>
/// Provides notifications when the contents of the clipboard is updated.
/// </summary>
public sealed class ClipboardNotification
{
/// <summary>
/// Occurs when the contents of the clipboard is updated.
/// </summary>
public static event EventHandler ClipboardUpdate;
private static NotificationForm _form = new NotificationForm();
/// <summary>
/// Raises the <see cref="ClipboardUpdate"/> event.
/// </summary>
/// <param name="e">Event arguments for the event.</param>
private static void OnClipboardUpdate(EventArgs e)
{
var handler = ClipboardUpdate;
if (handler != null)
{
handler(null, e);
}
}
/// <summary>
/// Hidden form to recieve the WM_CLIPBOARDUPDATE message.
/// </summary>
private class NotificationForm : Form
{
public NotificationForm()
{
NativeMethods.SetParent(Handle, NativeMethods.HWND_MESSAGE);
NativeMethods.AddClipboardFormatListener(Handle);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == NativeMethods.WM_CLIPBOARDUPDATE)
{
OnClipboardUpdate(null);
}
base.WndProc(ref m);
}
}
}
}

View File

@@ -1,4 +1,7 @@
namespace TypeClipboard
using System.Drawing;
using System.Windows.Forms;
namespace TypeClipboard
{
partial class Form1
{
@@ -23,48 +26,151 @@
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </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.comboBox1 = new System.Windows.Forms.ComboBox();
this.linkLabel1 = new System.Windows.Forms.LinkLabel();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.Font = new System.Drawing.Font("Consolas", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.textBox1.Location = new System.Drawing.Point(12, 12);
this.textBox1.Name = "textBox1";
this.textBox1.ReadOnly = true;
this.textBox1.Size = new System.Drawing.Size(184, 20);
this.textBox1.Size = new System.Drawing.Size(201, 22);
this.textBox1.TabIndex = 0;
//
// button1
//
this.button1.Location = new System.Drawing.Point(202, 10);
this.button1.Location = new System.Drawing.Point(219, 12);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(103, 23);
this.button1.Size = new System.Drawing.Size(103, 22);
this.button1.TabIndex = 1;
this.button1.Text = "Type (2s delay)";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// chkHotkey
//
this.chkHotkey.AutoSize = true;
this.chkHotkey.Location = new System.Drawing.Point(102, 98);
this.chkHotkey.Name = "chkHotkey";
this.chkHotkey.Size = new System.Drawing.Size(73, 17);
this.chkHotkey.TabIndex = 2;
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(177, 98);
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;
//
// comboBox1
//
this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBox1.FormattingEnabled = true;
this.comboBox1.Items.AddRange(new object[] {
"SendInput",
"SendKeys"});
this.comboBox1.Location = new System.Drawing.Point(12, 95);
this.comboBox1.Margin = new System.Windows.Forms.Padding(5);
this.comboBox1.Name = "comboBox1";
this.comboBox1.Size = new System.Drawing.Size(82, 21);
this.comboBox1.TabIndex = 9;
this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged);
//
// linkLabel1
//
this.linkLabel1.AutoSize = true;
this.linkLabel1.Location = new System.Drawing.Point(293, 98);
this.linkLabel1.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
this.linkLabel1.Name = "linkLabel1";
this.linkLabel1.Size = new System.Drawing.Size(29, 13);
this.linkLabel1.TabIndex = 10;
this.linkLabel1.TabStop = true;
this.linkLabel1.Text = "Help";
this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked);
//
// Form1
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.ClientSize = new System.Drawing.Size(317, 44);
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(332, 124);
this.Controls.Add(this.linkLabel1);
this.Controls.Add(this.comboBox1);
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);
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
this.Load += new System.EventHandler(this.Form1_Load);
this.Enter += new System.EventHandler(this.Form1_Enter);
this.MouseEnter += new System.EventHandler(this.Form1_MouseEnter);
this.ResumeLayout(false);
@@ -74,8 +180,15 @@
#endregion
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Button button1;
private TextBox textBox1;
private Button button1;
private CheckBox chkHotkey;
private Button button2;
private TextBox textBox2;
private Button button3;
private CheckBox chkEnter;
private ToolTip toolTip1;
private ComboBox comboBox1;
private LinkLabel linkLabel1;
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Windows.Forms;
@@ -9,6 +10,9 @@ namespace TypeClipboard
{
const int WS_EX_NOACTIVATE = 0x08000000;
private LowLevelKeyboardListener _listener;
private Typer _tc;
public Form1()
{
InitializeComponent();
@@ -29,70 +33,38 @@ namespace TypeClipboard
}
}
public void TypeClipboard()
{
if (Clipboard.ContainsText(TextDataFormat.UnicodeText))
{
String clipboard = Clipboard.GetText(TextDataFormat.UnicodeText);
Thread.Sleep(2000);
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(10);
}
}
}
private void button1_Click(object sender, EventArgs e)
{
TypeClipboard();
_tc.TypeClipboard(2000);
}
public void UpdateTextbox()
public void UpdateTextbox(EventArgs e = null)
{
if (Clipboard.ContainsText(TextDataFormat.UnicodeText))
{
String clipboard = Clipboard.GetText(TextDataFormat.UnicodeText);
StringBuilder sb = new StringBuilder();
sb.Append(clipboard.Substring(0, Math.Min(3, clipboard.Length)));
sb.Append(" ... ");
sb.Append(clipboard.Substring(clipboard.Length - Math.Min(clipboard.Length, 3)));
sb.Append(" (" + clipboard.Length.ToString() + " characters)");
if (clipboard.Length <= 4)
{
sb.Append(" (" + clipboard.Length.ToString() + " characters)");
}
else if (clipboard.Length <= 9)
{
sb.Append(clipboard.Substring(0, 1));
sb.Append("...");
sb.Append(clipboard.Substring(clipboard.Length - 1));
sb.Append(" (" + clipboard.Length.ToString() + " characters)");
}
else
{
sb.Append(clipboard.Substring(0, 3));
sb.Append("...");
sb.Append(clipboard.Substring(clipboard.Length - 3));
sb.Append(" (" + clipboard.Length.ToString() + " characters)");
}
textBox1.Text = sb.ToString();
}
else
@@ -115,5 +87,82 @@ namespace TypeClipboard
{
UpdateTextbox();
}
private void Form1_Load(object sender, EventArgs e)
{
_tc = new Typer();
_listener = new LowLevelKeyboardListener(_tc);
// Changing the chkHotkey.Checked property also hooks the listener
chkHotkey.Checked = Properties.Settings.Default.enableHotkey;
// Changing the chkEnter.Checked property also changes _tc.TypeEnter property
chkEnter.Checked = Properties.Settings.Default.enableEnter;
comboBox1.SelectedItem = Properties.Settings.Default.typeMethod;
ClipboardNotification.ClipboardUpdate += delegate (object cb_sender, EventArgs cb_e)
{
UpdateTextbox();
};
UpdateTextbox();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
_listener.UnHookKeyboard();
}
private void chkHotkey_CheckedChanged(object sender, EventArgs e)
{
Properties.Settings.Default.enableHotkey = chkHotkey.Checked;
if (Properties.Settings.Default.enableHotkey)
{
_listener.HookKeyboard();
}
else
{
_listener.UnHookKeyboard();
}
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();
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
try
{
Process.Start(new ProcessStartInfo { FileName = @"https://github.com/jlaundry/TypeClipboard/wiki/Help", UseShellExecute = true });
}
catch (Exception ex)
{
MessageBox.Show("Unable to open default browser. Go to https://github.com/jlaundry/TypeClipboard/wiki/Help");
}
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Properties.Settings.Default.typeMethod = (string)comboBox1.SelectedItem;
_tc.TypeMethod = (string)comboBox1.SelectedItem;
Properties.Settings.Default.Save();
}
}
}

View File

@@ -117,4 +117,7 @@
<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>
</root>

View File

@@ -0,0 +1,119 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Input;
/*
Portions Copyright (c) 2019 dylansweb.com, http://www.dylansweb.com/2014/10/low-level-global-keyboard-hook-sink-in-c-net/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
namespace TypeClipboard
{
public class LowLevelKeyboardListener
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private const int WM_SYSKEYDOWN = 0x0104;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
public event EventHandler<KeyPressedArgs> OnKeyPressed;
private LowLevelKeyboardProc _proc;
private IntPtr _hookID = IntPtr.Zero;
public Typer _tc;
public LowLevelKeyboardListener(Typer tc)
{
_proc = HookCallback;
_tc = tc;
}
public void HookKeyboard()
{
_hookID = SetHook(_proc);
}
public void UnHookKeyboard()
{
if(_hookID != IntPtr.Zero)
{
UnhookWindowsHookEx(_hookID);
_hookID = IntPtr.Zero;
}
}
private IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
}
}
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
Key keyPressed = KeyInterop.KeyFromVirtualKey(vkCode);
// Explicitly putting this here because we need to return before CallNextHookEx
if (keyPressed == Key.F8)
{
// Call Type Clipboard
_tc.TypeClipboard(100);
// Prevent key bring pressed further into the app
return new IntPtr(1);
}
if (OnKeyPressed != null) { OnKeyPressed(this, new KeyPressedArgs(keyPressed)); }
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
}
public class KeyPressedArgs : EventArgs
{
public Key KeyPressed { get; private set; }
public KeyPressedArgs(Key key)
{
KeyPressed = key;
}
}
}

View File

@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TypeClipboard
{
static class Program
internal static class Program
{
/// <summary>
/// The main entry point for the application.

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, 2019")]
[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.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.6.0.0")]
[assembly: AssemblyFileVersion("1.6.0.0")]

View File

@@ -8,10 +8,10 @@
// </auto-generated>
//------------------------------------------------------------------------------
namespace TypeClipboard.Properties {
using System;
namespace TypeClipboard.Properties
{
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
@@ -19,43 +19,51 @@ namespace TypeClipboard.Properties {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
internal class Resources
{
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
internal Resources()
{
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
internal static global::System.Resources.ResourceManager ResourceManager
{
get
{
if ((resourceMan == null))
{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TypeClipboard.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
internal static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set {
set
{
resourceCulture = value;
}
}

View File

@@ -12,7 +12,7 @@ namespace TypeClipboard.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -22,5 +22,50 @@ namespace TypeClipboard.Properties {
return defaultInstance;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool enableHotkey
{
get
{
return ((bool)(this["enableHotkey"]));
}
set
{
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;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("SendInput")]
public string typeMethod
{
get
{
return ((string)(this["typeMethod"]));
}
set
{
this["typeMethod"] = value;
}
}
}
}

View File

@@ -1,7 +1,12 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="TypeClipboard.Properties" GeneratedClassName="Settings">
<Profiles />
<Settings>
<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

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
@@ -8,11 +8,28 @@
<OutputType>WinExe</OutputType>
<RootNamespace>TypeClipboard</RootNamespace>
<AssemblyName>TypeClipboard</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
<IsWebBootstrapper>false</IsWebBootstrapper>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.6.0.0</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
<ForceDesignerDpiUnaware>true</ForceDesignerDpiUnaware>
<!-- Otherwise System.Windows.Input has no methods in .NET 8... -->
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -23,7 +40,6 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -33,7 +49,9 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>typeclipboard.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
@@ -47,16 +65,20 @@
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="ClipboardNotification.cs" />
<Compile Include="Form1.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Form1.Designer.cs">
<DependentUpon>Form1.cs</DependentUpon>
</Compile>
<Compile Include="LowLevelKeyboardListener.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Typer.cs" />
<EmbeddedResource Include="Form1.resx">
<DependentUpon>Form1.cs</DependentUpon>
</EmbeddedResource>
@@ -68,7 +90,6 @@
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
@@ -83,5 +104,20 @@
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Content Include="typeclipboard.ico" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.7.2">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.7.2 %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

584
TypeClipboard/Typer.cs Normal file
View File

@@ -0,0 +1,584 @@
using System;
using System.Collections.Generic;
using System.Linq;
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
{
public class Typer
{
private const int INTERKEY_DELAY = 50;
private bool _typeEnter = false;
private string _typeMethod = "SendInput";
public bool TypeEnter { get => _typeEnter; set => _typeEnter = value; }
public string TypeMethod { get => _typeMethod; set => _typeMethod = value; }
public void Type(String str, int delay = 2000)
{
Thread.Sleep(delay);
NativeMethods.BlockInput(true);
//KeyboardTyper.Reset();
KeyboardTyper.Type(str, _typeEnter, INTERKEY_DELAY);
NativeMethods.BlockInput(false);
}
public void TypeWithSendKeys(String str, int delay)
{
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':
if (_typeEnter)
{
SendKeys.Send("{ENTER}");
break;
}
else
{
return;
}
case '\r':
if (_typeEnter)
{
break;
}
else
{
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);
}
}
public void TypeClipboard(int delay = 2000)
{
if (Clipboard.ContainsText(TextDataFormat.UnicodeText))
{
String clipboard = Clipboard.GetText(TextDataFormat.UnicodeText);
if (_typeMethod == "SendKeys")
{
TypeWithSendKeys(clipboard, delay);
}
else
{
// SendInput is default
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.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="512"
height="512"
version="1.1"
id="svg3739"
sodipodi:docname="typeclipboard.svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
inkscape:export-filename="C:\Users\jlaundry\Downloads\typeclipboard.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<metadata
id="metadata3745">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs3743" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1017"
id="namedview3741"
showgrid="false"
showguides="false"
inkscape:zoom="1.65625"
inkscape:cx="157.88679"
inkscape:cy="297.0566"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg3739" />
<rect
style="fill:none;stroke:#ffffff;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none"
x="36"
y="37"
width="439.99997"
height="440"
id="svg_1"
rx="50.671612"
ry="50.671612" />
<rect
style="fill:#ffffff;stroke:#ffffff;stroke-width:4.99999952;stroke-miterlimit:4;stroke-dasharray:none"
x="168.5"
y="14.5"
width="175"
height="75"
id="svg_3"
rx="4.2934704"
ry="4.2934704" />
<rect
style="fill:#ffffff;stroke:#ffffff;stroke-width:5"
x="268.5"
y="130.5"
width="145"
height="145"
id="svg_7"
rx="10"
ry="10" />
<rect
style="fill:#ffffff;stroke:#ffffff;stroke-width:5"
x="178.5"
y="300.5"
width="145"
height="145"
id="svg_8"
rx="10"
ry="10.000001" />
<rect
style="fill:#ffffff;stroke:#ffffff;stroke-width:5"
x="98.5"
y="130.5"
width="145"
height="145"
id="svg_9"
rx="10"
ry="10" />
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 780 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
<Identity
Name="3373JedLaundry.TypeClipboard"
Publisher="CN=CE320BA0-58BC-4F47-AF5B-94DB661147CC"
Version="1.6.0.0" />
<Properties>
<DisplayName>TypeClipboard</DisplayName>
<PublisherDisplayName>Jed Laundry</PublisherDisplayName>
<Logo>Images\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14393.0" MaxVersionTested="10.0.14393.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App"
Executable="$targetnametoken$.exe"
EntryPoint="$targetentrypoint$">
<uap:VisualElements
DisplayName="TypeClipboard"
Description="TypeClipboard"
BackgroundColor="transparent"
Square150x150Logo="Images\Square150x150Logo.png"
Square44x44Logo="Images\Square44x44Logo.png">
<uap:DefaultTile Wide310x150Logo="Images\Wide310x150Logo.png" ShortName="TypeClipboard">
<uap:ShowNameOnTiles>
<uap:ShowOn Tile="square150x150Logo"/>
<uap:ShowOn Tile="wide310x150Logo"/>
</uap:ShowNameOnTiles>
</uap:DefaultTile >
<uap:SplashScreen Image="Images\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
</Package>

View File

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '15.0'">
<VisualStudioVersion>15.0</VisualStudioVersion>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x86">
<Configuration>Debug</Configuration>
<Platform>x86</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x86">
<Configuration>Release</Configuration>
<Platform>x86</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM">
<Configuration>Release</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|AnyCPU">
<Configuration>Debug</Configuration>
<Platform>AnyCPU</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|AnyCPU">
<Configuration>Release</Configuration>
<Platform>AnyCPU</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup>
<WapProjPath Condition="'$(WapProjPath)'==''">$(MSBuildExtensionsPath)\Microsoft\DesktopBridge\</WapProjPath>
</PropertyGroup>
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.props" />
<PropertyGroup>
<ProjectGuid>d041bcc6-3c9d-4bdc-8443-2bec9b290d1a</ProjectGuid>
<TargetPlatformVersion>10.0.26100.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<DefaultLanguage>en-US</DefaultLanguage>
<AppxPackageSigningEnabled>True</AppxPackageSigningEnabled>
<NoWarn>$(NoWarn);NU1702</NoWarn>
<PackageCertificateThumbprint>FEEF43DFCBAB446B040886D53A9FF02C8C201CBA</PackageCertificateThumbprint>
</PropertyGroup>
<ItemGroup>
<AppxManifest Include="Package.appxmanifest">
<SubType>Designer</SubType>
</AppxManifest>
</ItemGroup>
<ItemGroup>
<Content Include="Images\SplashScreen.scale-200.png" />
<Content Include="Images\LockScreenLogo.scale-200.png" />
<Content Include="Images\Square150x150Logo.scale-200.png" />
<Content Include="Images\Square44x44Logo.scale-200.png" />
<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>
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.targets" />
<ItemGroup>
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" PrivateAssets="all" />
</ItemGroup>
</Project>

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<>?