I'm going to share two solutions:
First, the quick & dirty solution:
- Create a multi-line textbox
- Paste to that
- Parse the result
Note that pasting text results in it being pasted as a TSV (tab-separated values) file. This can cause problems if you have values that can't be represented in the TSV format.
Second, the overcomplicated, WinAPI clipboard answer. I recommend the first answer for novice users since it's much easier to understand, the second is mainly useful for advanced users
First, we need declarations to a lot of clipboard functions, global memory handling functions, and string functions. My declarations are VBA6 incompatible, but should be 64-bit compatible:
'Global mem functions
Public Declare PtrSafe Function GlobalSize Lib "Kernel32" (ByVal hMem As LongPtr) As LongPtr
Public Declare PtrSafe Function GlobalLock Lib "Kernel32" (ByVal hMem As LongPtr) As LongPtr
Public Declare PtrSafe Function GlobalUnlock Lib "Kernel32" (ByVal hMem As LongPtr) As Boolean
'Clipboard functions
Public Declare PtrSafe Function GetClipboardData Lib "User32" (ByVal uFormat As Long) As LongPtr
Public Declare PtrSafe Function GetClipboardFormatNameW Lib "User32" (ByVal format As Long, ByVal lpszFormatName As LongPtr, ByVal cchMaxCount As Long) As Long
Public Declare PtrSafe Function OpenClipboard Lib "User32" (Optional ByVal hWndNewOwner As LongPtr) As Boolean
Public Declare PtrSafe Function CloseClipboard Lib "User32" () As Boolean
Public Declare PtrSafe Function EnumClipboardFormats Lib "User32" (ByVal format As Long) As Long
Public Declare PtrSafe Function CountClipboardFormats Lib "User32" () As Long
'String functions
Public Declare PtrSafe Function lstrcpyA Lib "Kernel32" (ByVal lpString1 As LongPtr, ByVal lpString2 As LongPtr) As LongPtr
Then, we're going to get the XML spreadsheet from the clipboard, in 2 steps:
Public Function GetClipboardSpreadsheetFormat() As Long
'Requires clipboard to be open, doesn't close it, returns 0 if not exists
Dim format As Long
Dim b As String
Dim l As Long
For l = 1 To CountClipboardFormats
b = String(100, vbNullChar) 'Initialize string buffer
format = EnumClipboardFormats(format) 'Get next format
GetClipboardFormatNameW format, StrPtr(b), 100 'Copy name to buffer
If Left(b, 15) = "XML Spreadsheet" Then
GetClipboardSpreadsheetFormat = format
Exit Function
End If
Next
End Function
Public Function GetClipboardSpreadsheetXML() As String
OpenClipboard 'Open (lock) the clipboard
Dim format As Long
format = GetClipboardSpreadsheetFormat
If format = 0 Then
Debug.Print "Spreadsheet data not available, display message here"
GoTo ExitHandler
End If
Dim hMem As LongPtr
hMem = GetClipboardData(format) 'Get handle to clipboard data
Dim memSize As LongPtr
memSize = GlobalSize(hMem) 'Get memory size
Dim buf() As Byte
Redim buf(0 To memSize - 1) 'Initialize buffer to hold XML
Dim memPtr As Long
memPtr = GlobalLock(hmem) 'Lock memory, get pointer to address
lstrcpyA VarPtr(buf(0)), memPtr 'Copy memory to buffer
GetClipboardSpreadsheetXML = StrConv(buf, vbUnicode) 'Convert ASCII string to unicode, return as result
ExitHandler:
If memPtr <> 0 Then GlobalUnlock (hMem) 'If memory pointer has been acquired, unlock global
CloseClipboard 'Close and unlock clipboard
End Function
Since you intend to convert the spreadsheet to XML, you can retrieve the spreadsheet XML by calling GetClipboardSpreadsheetXML
, and then use an XSLT to convert it to the desired format.
For non-Excel data sources, you can either use HTML Format instead of XML Spreadsheet to retrieve HTML, if available, and else you can use 1 (CF_TEXT
) as your format and not iterate through all possible formats to retrieve plain text from the clipboard.
You can read more about the Clipboard API on Microsoft Docs