Introduction
The Windows clipboard is probably the tool that gets used the most. And, it is probably the tool that is loved the most. But, it has its limitations. Nothing is perfect.
Today, you will learn how to make a clipboard that can hold multiple objects at the same time, as well as how to connect it to a global hot key. A global hot key is a key, such as Ctrl + C, from anywhere, and it functions accordingly by copying the selected item to the clipboard. We will replicate this behaviour, but we will incorporate different channels for the clipboard.
Let’s play!
Create a new Windows Forms project in either C# or VB.NET. After the project has loaded, add the following fields.
C#
private int intChannel = 1; private object[] objData = new object[5]; private ClipboardDataType[] cdtType = new ClipboardDataType[5]; private enum ClipboardDataType { Empty, Text, Image, Audio, FileList }
VB.NET
Private intChannel As Integer = 1 Private objData(4) As Object Private cdtType(4) As ClipboardDataType Private Enum ClipboardDataType Empty Text Image Audio FileList End Enum
Here, we created a channel object. We will use this to keep track of the number of channels being used. Then, we created two arrays: one for the data to be placed on and retrieved from the clipboard, and one for the data type which depends on the enumeration type.
Add the following Windows APIs.
C#
[System.Runtime.InteropServices.DllImport("user32")] private static extern int RegisterHotKey(IntPtr hwnd, int id, int fsModifiers, int vk); [System.Runtime.InteropServices.DllImport("user32")] private static extern int UnregisterHotKey(IntPtr hwnd, int id); [System.Runtime.InteropServices.DllImport("kernel32")] private static extern short GlobalAddAtom(string lpString); [System.Runtime.InteropServices.DllImport("kernel32")] private static extern short GlobalDeleteAtom(short nAtom); private const int MOD_CONTROL = 2; private short[] stKeyID = new short[5]; private Keys[] kKeyValue = new[] { Keys.D1, Keys.D2, Keys.D3, Keys.D4, Keys.D5 };
VB.NET
Private Declare Function RegisterHotKey Lib "user32" _ (ByVal hwnd As IntPtr, ByVal id As Integer, ByVal fsModifiers As Integer, ByVal vk As Integer) As Integer Private Declare Function UnregisterHotKey Lib "user32" _ (ByVal hwnd As IntPtr, ByVal id As Integer) As Integer Private Declare Function GlobalAddAtom Lib "kernel32" _ Alias "GlobalAddAtomA" (ByVal lpString As String) As Short Private Declare Function GlobalDeleteAtom Lib "kernel32" _ (ByVal nAtom As Short) As Short Private Const MOD_CONTROL As Integer = 2 Dim stKeyID(4) As Short Dim kKeyValue() As Keys = {Keys.D1, Keys.D2, Keys.D3, Keys.D4, _ Keys.D5}
The preceding Windows APIs create and dispose a global hot key. It must be registered through a Windows API; else, all other applications will not know about it and override the supposed hot key. When the application closes, UnregisterHotKey releases the designated hot keys from use, thus freeing them up and allowing the other applications to make use of them. The keys being assigned hot keys are Ctrl + 1, Ctrl + 2, Ctrl + 3, Ctrl + 4, and Ctrl + 5. Add the Hot key functions below:
C#
public void RegisterGlobalHotKey(int intID) { try { Keys kKey = kKeyValue[intID]; int intModifier = MOD_CONTROL; string strName = Thread.CurrentThread.ManagedThreadId .ToString("X8") + this.Name + intID; stKeyID[intID] = GlobalAddAtom(strName); if (stKeyID[intID] == 0) throw new Exception("Error"); if (RegisterHotKey(this.Handle, stKeyID[intID], intModifier, System.Convert.ToInt32(kKey)) == 0) throw new Exception("Error"); } catch (Exception ex) { UnregisterGlobalHotKey(); } } public void UnregisterGlobalHotKey() { for (int i = 0; i <= stKeyID.Length - 1; i++) { if (stKeyID[i] != 0) { UnregisterHotKey(this.Handle, stKeyID[i]); GlobalDeleteAtom(stKeyID[i]); stKeyID[i] = 0; } } }
VB.NET
Sub RegisterGlobalHotKey(ByVal intID As Integer) Try Dim kKey As Keys = kKeyValue(intID) Dim intModifier As Integer = MOD_CONTROL Dim strName As String = Thread.CurrentThread.Managed _ ThreadId.ToString("X8") & Me.Name & intID stKeyID(intID) = GlobalAddAtom(strName) If stKeyID(intID) = 0 Then Throw New Exception("Error") End If If RegisterHotKey(Me.Handle, stKeyID(intID), _ intModifier, CInt(kKey)) = 0 Then Throw New Exception("Error") End If Catch ex As Exception UnregisterGlobalHotKey() End Try End Sub Sub UnregisterGlobalHotKey() For i As Integer = 0 To stKeyID.Length - 1 If stKeyID(i) <> 0 Then UnregisterHotKey(Me.Handle, stKeyID(i)) GlobalDeleteAtom(stKeyID(i)) stKeyID(i) = 0 End If Next End Sub
Add the Overriden WndProc procedure to accept the hotkeys, identify which ones have been pressed, and then determine what type of data needs to be copied or pasted.
C#
protected override void WndProc(ref Message mes) { base.WndProc(ref mes); const int WM_HOTKEY = 0x312; if (mes.Msg == WM_HOTKEY) { for (int i = 0; i <= stKeyID.Length - 1; i++) { if (mes.WParam.Equals(stKeyID[i])) Swap(i); } } } private void Swap(int iChannel) { if (Clipboard.ContainsText()) { objData[intChannel] = Clipboard.GetText(); cdtType[intChannel] = ClipboardDataType.Text; } else if (Clipboard.ContainsImage()) { objData[intChannel] = Clipboard.GetImage(); cdtType[intChannel] = ClipboardDataType.Image; } else if (Clipboard.ContainsAudio()) { objData[intChannel] = Clipboard.GetAudioStream(); cdtType[intChannel] = ClipboardDataType.Audio; } else if (Clipboard.ContainsFileDropList()) { objData[intChannel] = Clipboard.GetFileDropList(); cdtType[intChannel] = ClipboardDataType.FileList; } else { objData[intChannel] = null; cdtType[intChannel] = ClipboardDataType.Empty; } intChannel = iChannel; switch (cdtType[intChannel]) { case ClipboardDataType.Text: { Clipboard.SetText((string)objData[intChannel]); break; } case ClipboardDataType.Image: { Clipboard.SetImage((Image)objData[intChannel]); break; } case ClipboardDataType.FileList: { Clipboard.SetFileDropList((System.Collections .Specialized.StringCollection)objData [intChannel]); break; } case ClipboardDataType.Audio: { Clipboard.SetAudio((System.IO.Stream)objData [intChannel]); break; } case ClipboardDataType.Empty: { Clipboard.Clear(); break; } } } public void RegisterAllKeys() { for (int i = 0; i <= stKeyID.Length - 1; i++) RegisterGlobalHotKey(i); } private void Form1_Load(object sender, EventArgs e) { RegisterAllKeys(); } private void Form1_FormClosed(object sender, FormClosedEventArgs e) { UnregisterGlobalHotKey(); }
VB.NET
Protected Overrides Sub WndProc(ByRef mes As Message) MyBase.WndProc(mes) Const WM_HOTKEY As Integer = &H312 If mes.Msg = WM_HOTKEY Then For i As Integer = 0 To stKeyID.Length - 1 If mes.WParam.ToInt32 = stKeyID(i) Then Swap(i) End If Next End If End Sub Private Sub Swap(ByVal iChannel As Integer) If Clipboard.ContainsText Then objData(intChannel) = Clipboard.GetText cdtType(intChannel) = ClipboardDataType.Text ElseIf Clipboard.ContainsImage Then objData(intChannel) = Clipboard.GetImage cdtType(intChannel) = ClipboardDataType.Image ElseIf Clipboard.ContainsAudio Then objData(intChannel) = Clipboard.GetAudioStream cdtType(intChannel) = ClipboardDataType.Audio ElseIf Clipboard.ContainsFileDropList Then objData(intChannel) = Clipboard.GetFileDropList cdtType(intChannel) = ClipboardDataType.FileList Else objData(intChannel) = Nothing cdtType(intChannel) = ClipboardDataType.Empty End If intChannel = iChannel Select Case cdtType(intChannel) Case ClipboardDataType.Text Clipboard.SetText(DirectCast(objData(intChannel), _ String)) Case ClipboardDataType.Image Clipboard.SetImage(DirectCast(objData(intChannel), _ Image)) Case ClipboardDataType.FileList Clipboard.SetFileDropList(DirectCast(objData _ (intChannel), Specialized.StringCollection)) Case ClipboardDataType.Audio Clipboard.SetAudio(DirectCast(objData(intChannel), _ IO.Stream)) Case ClipboardDataType.Empty Clipboard.Clear() End Select End Sub Public Sub RegisterAllKeys() For i As Integer = 0 To stKeyID.Length - 1 RegisterGlobalHotKey(i) Next End Sub Private Sub Form1_FormClosed(sender As Object, e As _ FormClosedEventArgs) Handles MyBase.FormClosed UnregisterGlobalHotKey() End Sub Private Sub Form1_Load(sender As Object, e As EventArgs) _ Handles MyBase.Load RegisterAllKeys() End Sub
When run (obviously, there is no user interface), you can press Ctrl + 1, then Ctrl + C, to add an item to the first channel of the clipboard. Press Ctrl + 2, then Ctrl + C to add to the second channel—and so on. To paste, follow the same process except for substituting Ctrl + C with Ctrl + V.
Conclusion
It is always fun to reinvent the wheel. In this article, you have learned not only about the clipboard, but more importantly, you have learned about global hot keys which are system wide and affect all programs. Happy copying, and happy coding!