How to Create Clipboard Manager and Get Custom Spike Functionality

Custom Clipboard Manager

   
 
In this article I explain how to use system Clipboard API capabilities, and get an application functionality such as Spike in MS Word.
 
Recently I got a task to develop an application that enables users to monitor all clipboard text transactions made through Cut/Copy operation inside one application, and to transfer selected data to another application, through a custom paste operation. There was also a request to add two “ENTER” keystrokes at the end of every paste operation
 
By default, operating system Clipboard API enables the user, through Cut/Copy/Paste operations, to temporarily store selected data inside a dedicated part of memory, and then to transfer that data into the same or a different application. It also enables only a single Clipboard data transaction, and to get something more, there is a need to develop and use a custom Clipboard Manager application.
 
 
According to demands for the app that should be developed, there are several things that Clipboard Manager should do:
  • The prime task is to monitor all data changes inside the system Clipboard made by Cut/Copy operations. When that event occurs the app should get Clipboard data and if it contains plain text, it should store it inside a custom data table.
  • The second task is to enable the user to select textual data from the data table and transfer it to the selected program trough custom Paste operation.
  • The third task is to give the user some extra Hotkey functionalities, like a Hotkey for deleting all memorized data, selecting data for Paste operations, minimizing and maximizing app, pasting all data at once, etc. 
There are many ways to resolve those three tasks. The one applied here is by using Window Procedures with its WndProc function and User32 dynamic link library, all parts of Windows 32 API. 
 
 
Windows operating system and all applications developed to run under it are event-driven. All events that happen at operating system level such as Clipboard changes, Hot Key pressed event, Foreground window has changed, etc., are registered by the operating system and if necessary, the operating system creates a message about that event and sends that message to the application window that is registered for monitoring such an event.
 
Window Procedures with its WndProc function enables communication between the operating systems and applications. Any created window has its own window procedure. It enables sending, receiving and processing Windows messages.
 
If there is a need to observe Clipboard changes, such as in this case, it is necessary to register application window Handle to the operating system Clipboard format listener list. That means that every time the content of Clipboard is changed, the operating system shall send a message about that event to the registered application's Window Procedure.
 
To get messages about what combination of keys is pressed, it is necessary to register the application's Window Handle and Hot Key combination to the system, and every time this key combination is pressed, the system shall send a message about that event to the registered application's Window Procedure.
 
What is important here is to notice that the system knows where to send a message about a certain event over the registered application window Handle. Also, the system message contains the Handle of the window that raised the observed event and code of the message.
 
Some of the message codes: 
  1. // WinProc Message/Event Codes    
  2. const int WM_HOTKEY = 0x0312;    
  3. const int WM_CLIPBOARDUPDATE = 0x031D;     
For more about message codes, thoroughly read the article About Messages And Message Queues
 
All messages sent to the window by the operating system can be processed by the WndProc function.
  1. #region-   WndProc   -    
  2. protected override void WndProc(ref Message m)    
  3. {    
  4.     // Listen for operating system Hot Key messages    
  5.     if (m.Msg == WM_HOTKEY)    
  6.     {    
  7.         // Do something    
  8.     }    
  9.     // Listen for operating system Clipboard changes    
  10.     else if (m.Msg == WM_CLIPBOARDUPDATE)    
  11.     {    
  12.         // Do something    
  13.     }    
  14.     else    
  15.         base.WndProc(ref m);    
  16. }    
  17. #endregion    
Of course, before using this function, there is a need to register an application to the operating system for getting the desired messages. 
 
 
This library contains several functions that are needed for registering the application for monitoring different events.  
 
 
This function makes an attempt to register window handle to system Clipboard format listener list. If succeeded, the window shall receive WM_CLIPBOARDUPDATE message every time when the system Clipboard changes its content.
  1. // Set Clipboard listener    
  2. bool Is_Set = AddClipboardFormatListener(this.Handle);    
bool RemoveClipboardFormatListener(IntPtr hwnd)
 
This function removes window handle from Clipboard format listener. If succeeded, the window shall not receive any more messages about system Clipboard changes. 
 
 
This function makes an attempt to register window handle and Hotkey combination to the operating system. If succeeded, the window shall receive a WM_HOTKEY message every time when the selected key combination is pressed.
  1. // Hot Key KeyModifiers ID-s    
  2. const int None = 0x0000;    
  3. const int Alt = 0x0001;    
  4. const int Control = 0x0002;    
  5. const int Shift = 0x0004;    
  6. const int WinKey = 0x0008;    
  7.     
  8. const int MOD_NOREPEAT = 0x4000;    
  9.     
  10. // Hot Key ID and Hot Key KeyCode    
  11. const int V = (int)Keys.V;    
  12. // Register Hot Key Combination Control + V    
  13. bool Is_Registered = RegisterHotKey(this.Handle, V, Control | MOD_NOREPEAT, V);     
 
This function removes registered Window Handle and Hotkey combination from the operating system. If succeeded, the window shall not receive any more messages when the Hotkey combination is pressed. 
 
 
This function gets the handle of the currently active/selected/focused window.
  1. // Current Foreground Window Pointer    
  2. IntPtr handle = new IntPtr();    
  3.     
  4. // Get Foreground Window Handle    
  5. handle = GetForegroundWindow();     
 
This function makes an attempt to set active/selected/focused window with the selected handles.
  1. // Set Foreground Window Handle    
  2. bool Is_Set = SetForegroundWindow(handle);    
Usage of all those functions are considered unsafe, and according to that, they should be declared as an instance of UnsafeNativeMethods class, with necessary security attribute information. 
  1. #region-    DllImport section   -    
  2. [SuppressUnmanagedCodeSecurityAttribute]    
  3. internal static class UnsafeNativeMethods    
  4. {    
  5.     [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]    
  6.     internal static extern IntPtr GetForegroundWindow();    
  7.     
  8.     [DllImport("user32.dll")]    
  9.     internal static extern int SetForegroundWindow(IntPtr point);    
  10.     
  11.     [DllImport("user32.dll", SetLastError = true)]    
  12.     [return: MarshalAs(UnmanagedType.Bool)]    
  13.     internal static extern bool AddClipboardFormatListener(IntPtr hwnd);    
  14.     
  15.     [DllImport("user32.dll", SetLastError = true)]    
  16.     [return: MarshalAs(UnmanagedType.Bool)]    
  17.     internal static extern bool RemoveClipboardFormatListener(IntPtr hwnd);    
  18.     
  19.     [DllImport("user32.dll")]    
  20.     internal static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);    
  21.     
  22.     [DllImport("user32.dll")]    
  23.     internal static extern bool UnregisterHotKey(IntPtr hWnd, int id);    
  24. }    
  25. #endregion    
Every time before using any of them, there is a need to demand a new Security permission.
  1. // Demand new security permission    
  2. new UIPermission(UIPermissionWindow.AllWindows).Demand();  
Now that we know the necessary theory let's create an application.
 
Download full solution developed in MS Visual Studio 2019, with open source code inside.
 
Open it in Visual Studio and build a complete project. After that thoroughly examine and analyze the whole project with program code.
 
There is a video showing how the program works at https://www.youtube.com/watch?v=EtVpi4Q3MNU
 
All the best,
 
Željko Perić