Sequential Windows Clipboard Copy?

Doug

Active member
Joined
Oct 5, 2011
Messages
44
Programming Experience
Beginner
I want to be able to put two strings, one at a time, into the Windows clipboard in order so that users can just paste them twice.

The process would look like this:

1. User selects a row in a datagridview.
2. User clicks a button.
3. My application erases the current contents of the Windows clipboard and copies the 1st string to it.
4. User pastes the content from the clipboard by Ctrl-v or Right-click.
5. Application detects the paste and copies the 2nd string to the clipboard.
6. User pastes the content from the clipboard by Ctrl-v or Right-click.
7. Application clears the content of the clipboard.

I know how to do everything except step #5. How do I detect when the contents of the clipboard has been pasted?
 
That rather depends on where the paste is happening. Why can't you simply transfer the two strings to their targets automatically? Why does the user need to invoke trhe clipboard at all?
 
You can have a thread intercepting WinProc messages and looking for the appropriate WM_HOTKEY message. Look up examples on the RegisterHotKey API call.

Imports System.Runtime.InteropServices

Public Class HotKeyClass
    <DllImport("user32.dll", SetLastError:=True)> _
    Private Shared Function RegisterHotKey(ByVal hWnd As IntPtr, ByVal id As Integer, ByVal fsModifiers As UInteger, ByVal vk As UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function
    <DllImport("user32.dll", SetLastError:=True)> _
    Private Shared Function UnregisterHotKey(ByVal hwnd As IntPtr, ByVal id As Integer) As Integer
    End Function

    Private Const WM_HOTKEY = 786
    
    Public Enum Hotkeys As Integer
        MOD_NONE = 0
        MOD_ALT = 1
        MOD_CONTROL = 2
        MOD_SHIFT = 4
        MOD_WIN = 8
    End Enum
    
    Public Event HotkeyPressed()
    
    Public Sub RegisterKey(ByVal HotkeyMod As Hotkeys, ByVal HotkeyMain As System.Windows.Forms.Keys)
        RegisterHotKey(MyClass.Handle, 1, HotkeyMod, CUInt(HotkeyMain))
    End Sub
    
    Public Sub UnregisterKey()
        UnregisterHotKey(MyClass.Handle, 1)
    End Sub

    ' Override WindowProc to intercept our hotkey and raise an external event to signal client application.
    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        If m.Msg = WM_HOTKEY Then
            If m.WParam.ToInt64() = 1 Then
                RaiseEvent HotkeyPressed()
            End If
        End If
        MyBase.WndProc(m)
    End Sub
End Class
 
Last edited:
For detecting the clipboard paste you could use the delayed rendering feature in windows, which simplified means you use the unmanaged SetClipboardData function passing Nothing for data handle, and override WndProc where you handle WM_RENDERFORMAT message, after providing the first actual clipboard data you can set another delayed render to ready processing next paste etc. A basic example is shown here:
    Private Declare Function SetClipboardData Lib "user32.dll" (ByVal wFormat As Int32, ByVal hMem As IntPtr) As IntPtr
    Private Declare Function OpenClipboard Lib "user32.dll" (ByVal hwnd As IntPtr) As Boolean
    Private Declare Function CloseClipboard Lib "user32.dll" () As Boolean
    Private Declare Function EmptyClipboard Lib "user32.dll" () As Boolean
    Private Const CF_TEXT = 1 'for other formats refer to documentation
    Private Const WM_RENDERFORMAT As Int32 = &H305
    Private Const WM_RENDERALLFORMATS As Int32 = &H306

    Private clips() As String
    Private index As Integer

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        Select Case m.Msg
            Case WM_RENDERFORMAT
                If m.WParam.ToInt32 = CF_TEXT Then
                    Dim h = Runtime.InteropServices.Marshal.StringToHGlobalAnsi(clips(index))
                    SetClipboardData(CF_TEXT, h)
                    index += 1
                    If index = clips.Length Then
                        Me.BeginInvoke(New MethodInvoker(AddressOf Clipboard.Clear))
                    Else
                        Me.BeginInvoke(New MethodInvoker(AddressOf DelayRenderClipboard))
                    End If
                End If
            Case WM_RENDERALLFORMATS
                Clipboard.Clear()
        End Select        
        MyBase.WndProc(m)
    End Sub

    Private Sub DelayRenderClipboard()
        If OpenClipboard(Me.Handle) Then
            EmptyClipboard()
            Dim h = SetClipboardData(CF_TEXT, Nothing)
            CloseClipboard()
        End If
    End Sub

starting a delay render could be done so:
        clips = {"first", "second"}
        index = 0
        DelayRenderClipboard()
 
Thank you for the suggestions guys. I am going to give them a try.

The reason why I can't copy directly to the target is because I don't know what the target is going to be. It could be anything.
 
Back
Top