66

I'm trying to add a button to a spreadsheet that when clicked will copy a specific URL to my clipboard.

I had a bit of knowledge of Excel VBA but it's been a while and I'm struggling.

Community
  • 1
  • 1
user1958738
  • 661
  • 1
  • 5
  • 3

9 Answers9

105

EDIT - MSForms is deprecated, so you should no longer use my answer. Instead use this answer: https://stackoverflow.com/a/60896244/692098

I leave my original answer here only for reference:

Sub CopyText(Text As String)
    'VBA Macro using late binding to copy text to clipboard.
    'By Justin Kay, 8/15/2014
    Dim MSForms_DataObject As Object
    Set MSForms_DataObject = CreateObject("new:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
    MSForms_DataObject.SetText Text
    MSForms_DataObject.PutInClipboard
    Set MSForms_DataObject = Nothing
End Sub

Usage:

Sub CopySelection()
    CopyText Selection.Text
End Sub
Jroonk
  • 1,376
  • 1
  • 9
  • 12
  • 1
    This is very helpful for long queries that can't be entirely seen with a spy. Thanks a lot! – Lionel T. Jul 09 '15 at 20:34
  • 1
    great help and altho relying on magic strings, neatly saves an aditional reference to 3rd party dll's etc. plus 1 – jim tollan Nov 02 '15 at 16:46
  • Thanks for the code, worked in outlook without modification – Michael Aug 08 '16 at 12:55
  • Simple and elegant! – ChrisB May 12 '18 at 01:35
  • 6
    This code has a bug, eventually it will stop copying your text and [copy only 2 question marks.](https://www.thespreadsheetguru.com/blog/2015/1/13/how-to-use-vba-code-to-copy-text-to-the-clipboard) – cyberponk Oct 01 '18 at 15:31
  • @gordon613, I get that it's a Microsoft bug. It was 5 years ago that I put this post together, so I'm not going to get too defensive of it. I am curious though. Under what circumstances does it fail? Are you writing a repeating copy/paste routine that fails after a certain number, lets say 250 iterations? Does it reset by just restarting the Excel or whatever VBA provider you're using? Do you have to reboot windows to reset it? Or does it just happen intermittently and randomly? – Jroonk Mar 05 '19 at 02:12
  • Hi @jroonk. I used your code to copy one Excel cell to the clipboard, and it worked well for me for years - so thank you! One day it suddenly stopped working on a consistent basis - I assume after some Microsoft update. So I just replaced it with something like `Range("A1").copy` – gordon613 Mar 06 '19 at 11:49
  • That works if your text is in cell A1. However, sometimes you want to push a string out to the clipboard directly from your VBA code. Also this works in all VBA. Range("A1") is only available in Excel. – Jroonk Mar 20 '19 at 01:24
  • 3
    @Jroonk this method fails (at least in Windows 10, not sure about other Windows versions) if File Explorer is open to a folder. Depending on the encoding of the paste destination it may paste as `??` or `\xEF\xBF\xBF\xEF\xBF\xBF`. – ChrisB Jul 29 '19 at 20:16
  • I was unable to find a reference for the Microsoft Forms 2 Object Library from Tools >References but this worked for me. Thanks for posting this method with late binding – SendETHToThisAddress Oct 29 '20 at 16:24
  • @SendETHToThisAddress, really should use this other answer now for copying to clipboard in VBA: https://stackoverflow.com/a/60896244/692098 He's doing the same thing, but he's using htmlfile object to access the clipboard, instead of the deprecated MSForms object. – Jroonk Apr 27 '21 at 20:19
45

To write text to (or read text from) the Windows clipboard use this VBA function:

Function Clipboard$(Optional s$)
    Dim v: v = s  'Cast to variant for 64-bit VBA support
    With CreateObject("htmlfile")
    With .parentWindow.clipboardData
        Select Case True
            Case Len(s): .setData "text", v
            Case Else:   Clipboard = .getData("text")
        End Select
    End With
    End With
End Function

'Three examples of copying text to the clipboard:
Clipboard "Excel Hero was here."
Clipboard var1 & vbLF & var2
Clipboard 123

'To read text from the clipboard:
MsgBox Clipboard

This is a solution that does NOT use MS Forms nor the Win32 API. Instead it uses the Microsoft HTML Object Library which is fast and ubiquitous and NOT deprecated by Microsoft like MS Forms. And this solution respects line feeds. This solution also works from 64-bit Office. Finally, this solution allows both writing to and reading from the Windows clipboard. No other solution on this page has these benefits.

Excel Hero
  • 14,253
  • 4
  • 33
  • 40
  • What is your setup? Are you using 64-bit Excel? – Excel Hero Jul 14 '20 at 03:28
  • I just tested it again on multiple computers with a variety of setups. It worked perfectly in every case. It does NOT require any references and it has the advantage that it does not need to use the Forms 2.0 Data Object, which has has been deprecated by MS. Perhaps it's something odd about your data? If you would like to send me a sample workbook where it does not work for you, I'd be happy to take a look: daniel.ferry@gmail.com – Excel Hero Jul 14 '20 at 14:53
  • @FreeSoftwareServers Please let me know if you would like me to look at a sample workbook of yours that shows the problem. It works fine here no matter how I test it. – Excel Hero Jul 15 '20 at 16:01
  • I have been looking at the HTML library too, and like the idea of getting away from Forms 2.0 Data Object, but I hadn't found a way to make it work. I was hoping that you found it, but I'm having the similar issues. The objects work all the way out to clipboardData, but I get a "Run-time error '5': Invalid procedure call or argument." when it hits the setData method. – Jroonk Oct 19 '20 at 11:20
  • @Jroonk It works for me... every time. Do you possibly have 64-bit Excel? I use 32-bit. – Excel Hero Oct 19 '20 at 15:57
  • I do... I'll give 32 bit a shot and see if that makes a difference. – Jroonk Nov 06 '20 at 20:13
  • 3
    Solution to `Invalid Argument` problem (works in 64-bit Excel 2010): The second argument to .setData has to be a Variant (a string literal like "test" also works), not a variable of type String. Create a Variant variable, assign s to it, and use it instead of s in the .setData and it works fine. – Bill Dimm Dec 18 '20 at 17:23
  • @BillDimm I don't have 64-bit here, but I updated my answer based on your comment. Is my change what you meant? – Excel Hero Dec 18 '20 at 17:39
  • @FreeSoftwareServers The issue with 64-bit VBA has been resolved. Try it again. This solution works well. – Excel Hero Dec 19 '20 at 02:43
  • @Jroonk The issue with 64-bit VBA has been resolved. Try it again. This solution works well. – Excel Hero Dec 19 '20 at 02:43
  • @FreeSoftwareServers That `[A1:Z999]` was from an earlier version. I updated the answer. – Excel Hero Dec 24 '20 at 16:11
  • @ExcelHero, it's looking like nothing with CreateObject() will work properly on Mac OS X. No matter how I try to use it, I get Run-time error '429': ActiveX component can't create object. Any ideas? – nollaf126 Apr 27 '21 at 17:41
  • @ExcelHero, yah this solution works great now, and I love that it combines the read and write (copy/paste) in a single function. It has my vote. – Jroonk Apr 27 '21 at 17:42
  • I also edited my solution to hopefully redirect people to look at yours. Thanks. – Jroonk Apr 27 '21 at 17:50
  • @ExcelHero, I am curious on your use of a select-case. It's my understanding that you're just checking if there is data in s (i.e. if len(s) > 0, then set, else get). What you did works for me, but I don't understand it, and I'm not a novice. – Jroonk Apr 27 '21 at 20:36
  • I found my answer here: https://stackoverflow.com/questions/794036/select-case-true, looks like its only a preference. I've lived in C# .net for so long now. You can't do that with c# switch-cases. – Jroonk Apr 27 '21 at 20:49
  • @Jroonk Thanks for the redirect from your answer! Yeah, I've used the `Select Case True` pattern in VB since the beginning... three decades now. Honestly, it's overkill here since there's only two possible branches, but I still prefer it over `If... Else...` – Excel Hero Apr 28 '21 at 04:54
  • @nollaf126 Sorry. I don't use a Mac. – Excel Hero Apr 28 '21 at 04:56
  • @nollaf126, I do have a Mac. It may be a while before I get around to this, but I'll take a look and see if I can figure something out. – Jroonk May 03 '21 at 23:18
  • What is the meaning of the $ at the end of the function name Clipboard$ and variable name s$ – Shodan Jun 23 '21 at 23:24
  • 1
    @Shodan. The `$` means STRING. – Excel Hero Jun 24 '21 at 00:06
  • 1
    @Shodan It's exactly the same as: `Function Clipboard() As String` – Excel Hero Jun 24 '21 at 00:13
  • 1
    @Shodan That notation has been native to BASIC since before VBA or even Visual Basic existed. – Excel Hero Jun 24 '21 at 00:17
  • Thanks, Then I will add the "as string" for readability as I had never encountered this before – Shodan Jun 24 '21 at 00:29
  • The `dollar` in the function name and the parameter means that it returns a string receives a input-string. So if you do `clipboard 1`, 1 will be interpreted as string and not integer. – Timo Oct 01 '21 at 05:50
  • 1
    @Timo Indeed and that is why I described this solution as writing and reading TEXT. – Excel Hero Oct 01 '21 at 06:22
  • This solution runs without error, however nothing is written to the clipboard on the .setData call. Whatever was in the clipboard before remains there. I'm working with Excel 365 64 bit version 2208 (Build 15601.20456). Has anybody encountered a similar problem? – PKB Apr 11 '23 at 09:44
22

The simplest (Non Win32) way is to add a UserForm to your VBA project (if you don't already have one) or alternatively add a reference to Microsoft Forms 2 Object Library, then from a sheet/module you can simply:

With New MSForms.DataObject
    .SetText "http://zombo.com"
    .PutInClipboard
End With
Alex K.
  • 171,639
  • 30
  • 264
  • 288
  • 7
    This is the method I use, but I found it fails if a folder is open in File Explorer in Windows 10. I can't comment on other Windows versions. – ChrisB Jul 29 '19 at 18:38
  • 1
    Wow - great find @ChrisB - I have been trying to troubleshoot that issue for years! – Stack Man Sep 24 '20 at 12:34
  • MSForms is deprecated, so you should no longer it. Instead use this answer: https://stackoverflow.com/a/60896244/692098 – Jroonk May 13 '21 at 07:59
12

If the url is in a cell in your workbook, you can simply copy the value from that cell:

Private Sub CommandButton1_Click()
    Sheets("Sheet1").Range("A1").Copy
End Sub

(Add a button by using the developer tab. Customize the ribbon if it isn't visible.)

If the url isn't in the workbook, you can use the Windows API. The code that follows can be found here: http://support.microsoft.com/kb/210216

After you've added the API calls below, change the code behind the button to copy to the clipboard:

Private Sub CommandButton1_Click()
    ClipBoard_SetData ("http:\\stackoverflow.com")
End Sub

Add a new module to your workbook and paste in the following code:

Option Explicit

Declare Function GlobalUnlock Lib "kernel32" (ByVal hMem As Long) _
   As Long
Declare Function GlobalLock Lib "kernel32" (ByVal hMem As Long) _
   As Long
Declare Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, _
   ByVal dwBytes As Long) As Long
Declare Function CloseClipboard Lib "User32" () As Long
Declare Function OpenClipboard Lib "User32" (ByVal hwnd As Long) _
   As Long
Declare Function EmptyClipboard Lib "User32" () As Long
Declare Function lstrcpy Lib "kernel32" (ByVal lpString1 As Any, _
   ByVal lpString2 As Any) As Long
Declare Function SetClipboardData Lib "User32" (ByVal wFormat _
   As Long, ByVal hMem As Long) As Long

Public Const GHND = &H42
Public Const CF_TEXT = 1
Public Const MAXSIZE = 4096

Function ClipBoard_SetData(MyString As String)
   Dim hGlobalMemory As Long, lpGlobalMemory As Long
   Dim hClipMemory As Long, X As Long

   ' Allocate moveable global memory.
   '-------------------------------------------
   hGlobalMemory = GlobalAlloc(GHND, Len(MyString) + 1)

   ' Lock the block to get a far pointer
   ' to this memory.
   lpGlobalMemory = GlobalLock(hGlobalMemory)

   ' Copy the string to this global memory.
   lpGlobalMemory = lstrcpy(lpGlobalMemory, MyString)

   ' Unlock the memory.
   If GlobalUnlock(hGlobalMemory) <> 0 Then
      MsgBox "Could not unlock memory location. Copy aborted."
      GoTo OutOfHere2
   End If

   ' Open the Clipboard to copy data to.
   If OpenClipboard(0&) = 0 Then
      MsgBox "Could not open the Clipboard. Copy aborted."
      Exit Function
   End If

   ' Clear the Clipboard.
   X = EmptyClipboard()

   ' Copy the data to the Clipboard.
   hClipMemory = SetClipboardData(CF_TEXT, hGlobalMemory)

OutOfHere2:

   If CloseClipboard() = 0 Then
      MsgBox "Could not close Clipboard."
   End If

End Function
Jon Crowell
  • 21,695
  • 14
  • 89
  • 110
  • 1
    From the KB, this code targets Access 2000. The code was probably meant to work on older OS that did not support Unicode properly (Win95 anyone?). Unfortunately, we keep using the same piece of code that won't work if your string contains any characters outside the extended ASCII range, so no Unicode for you! – Renaud Bompuis Sep 29 '13 at 15:10
  • Although it doesn't answer the OP's question on getting a URL to the clipboard that may or may not be in a cell, +1 for the .Copy method reference. Duh, sometimes the simplest answers are the best! – GlennFromIowa Aug 16 '18 at 15:13
  • This is my new preferred method! It doesn't fail when File Explorer is open to a folder. – ChrisB Jul 29 '19 at 20:19
  • New location for this is: https://learn.microsoft.com/en-us/office/vba/access/Concepts/Windows-API/send-information-to-the-clipboard – Jroonk Oct 19 '20 at 11:50
  • The first approach (`Range.Copy`) won't copy text but cells: Excel will add quotation marks (`"`) at the start and the end of the text and replace quotation marks by double-quotation-marks – Martin Rosenau Aug 31 '22 at 08:40
9

Add a reference to the Microsoft Forms 2.0 Object Library and try this code. It only works with text, not with other data types.

Dim DataObj As New MSForms.DataObject

'Put a string in the clipboard
DataObj.SetText "Hello!"
DataObj.PutInClipboard

'Get a string from the clipboard
DataObj.GetFromClipboard
Debug.Print DataObj.GetText

Here you can find more details about how to use the clipboard with VBA.

Community
  • 1
  • 1
stenci
  • 8,290
  • 14
  • 64
  • 104
  • 1
    This code has a bug, eventually it will stop copying your text and copy only 2 question marks – cyberponk Mar 26 '19 at 03:07
  • MSForms is deprecated, so you should no longer use it. Instead use this answer: https://stackoverflow.com/a/60896244/692098 – Jroonk May 13 '21 at 08:01
5

If you want to put a variable's value in the clipboard using the Immediate window, you can use this single line to easily put a breakpoint in your code:

Set MSForms_DataObject = CreateObject("new:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}"): MSForms_DataObject.SetText VARIABLENAME: MSForms_DataObject.PutInClipboard: Set MSForms_DataObject = Nothing
Marcucciboy2
  • 3,156
  • 3
  • 20
  • 38
F_Face
  • 81
  • 1
  • 1
0

If the place you're gonna paste have no problem with pasting a table formating (like the browser URL bar), I think the easiest way is this:

Sheets(1).Range("A1000").Value = string
Sheets(1).Range("A1000").Copy
MsgBox "Paste before closing this dialog."
Sheets(1).Range("A1000").Value = ""
Maycow Moura
  • 6,471
  • 2
  • 22
  • 19
  • 1
    Note that that "dirties" the workbook. Another problem is that if there's cell or sheet protection, you may have to cumbersomely deal with that. An alternative without those issues is workbooks.add, cells(1)=string, cells(1).copy, and close with SaveChanges:=False – MicrosoftShouldBeKickedInNuts Aug 16 '19 at 11:06
0

I tested this code in excel 365 and it worked

Dim str as String
str = "Hello Copied"
Windows.Parent.Clipboard str

Note: I created the variable because the code does not process string concatenation

Wendel
  • 2,809
  • 29
  • 28
-1

The code given at the Microsoft site works in Excel too, even though it is under Access VBA. I tried it in Excel 365 on a 64 bit Windows 10.

Microsoft Site Link: https://learn.microsoft.com/en-us/office/vba/access/Concepts/Windows-API/send-information-to-the-clipboard

Copying here for answer completeness.

Option Explicit
Private Declare Function OpenClipboard Lib "user32.dll" (ByVal hWnd As Long) As Long
Private Declare Function EmptyClipboard Lib "user32.dll" () As Long
Private Declare Function CloseClipboard Lib "user32.dll" () As Long
Private Declare Function IsClipboardFormatAvailable Lib "user32.dll" (ByVal wFormat As Long) As Long
Private Declare Function GetClipboardData Lib "user32.dll" (ByVal wFormat As Long) As Long
Private Declare Function SetClipboardData Lib "user32.dll" (ByVal wFormat As Long, ByVal hMem As Long) As Long
Private Declare Function GlobalAlloc Lib "kernel32.dll" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
Private Declare Function GlobalLock Lib "kernel32.dll" (ByVal hMem As Long) As Long
Private Declare Function GlobalUnlock Lib "kernel32.dll" (ByVal hMem As Long) As Long
Private Declare Function GlobalSize Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function lstrcpy Lib "kernel32.dll" Alias "lstrcpyW" (ByVal lpString1 As Long, ByVal lpString2 As Long) As Long

Public Sub SetClipboard(sUniText As String)
    Dim iStrPtr As Long
    Dim iLen As Long
    Dim iLock As Long
    Const GMEM_MOVEABLE As Long = &H2
    Const GMEM_ZEROINIT As Long = &H40
    Const CF_UNICODETEXT As Long = &HD
    OpenClipboard 0&
    EmptyClipboard
    iLen = LenB(sUniText) + 2&
    iStrPtr = GlobalAlloc(GMEM_MOVEABLE Or GMEM_ZEROINIT, iLen)
    iLock = GlobalLock(iStrPtr)
    lstrcpy iLock, StrPtr(sUniText)
    GlobalUnlock iStrPtr
    SetClipboardData CF_UNICODETEXT, iStrPtr
    CloseClipboard
End Sub

Public Function GetClipboard() As String
    Dim iStrPtr As Long
    Dim iLen As Long
    Dim iLock As Long
    Dim sUniText As String
    Const CF_UNICODETEXT As Long = 13&
    OpenClipboard 0&
    If IsClipboardFormatAvailable(CF_UNICODETEXT) Then
        iStrPtr = GetClipboardData(CF_UNICODETEXT)
        If iStrPtr Then
            iLock = GlobalLock(iStrPtr)
            iLen = GlobalSize(iStrPtr)
            sUniText = String$(iLen \ 2& - 1&, vbNullChar)
            lstrcpy StrPtr(sUniText), iLock
            GlobalUnlock iStrPtr
        End If
        GetClipboard = sUniText
    End If
    CloseClipboard
End Function

The above code can be called from a Custom Macro as follows:

Sub TestClipboard()
    Dim Val1 As String: Val1 = "Hello Clipboard " & vbLf & "World!"
    SetClipboard Val1
    MsgBox GetClipboard
End Sub

To Show a button on a form, you can find a good example by a quick serach. To show a button in the Excel Custom Ribbon (One that shows only in the current Excel workbook) you can use CustomUI.

CustomUI links:

https://bettersolutions.com/vba/ribbon/custom-ui-editor.htm

https://learn.microsoft.com/en-us/office/open-xml/how-to-add-custom-ui-to-a-spreadsheet-document

imageMSO List with Icons (Used in CustomUI):

https://bert-toolkit.com/imagemso-list.html

Thanks.