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!