4

I have a macro to open a Word document in the backend and pull all the data tables into an Excel template.

When I tested it in some of my colleagues' machines, they get a "VBA PasteSpecial error".

I checked the references and add ins in my colleagues' machines are that of mine.

Dim sht As Worksheet
Dim WordDoc As Word.Document
Dim WordApp As Word.Application
Dim i As Long, r As Long, c As Long
Dim rng As Range, t As Word.Table
Dim ia As Integer
Dim OpenForms
Dim target As Range
ia = 1
Set WordApp = CreateObject("Word.Application")
WordApp.Visible = False
Set WordDoc = WordApp.Documents.Open(DOC_PATH, ReadOnly:=True)
Set sht = Sheets("test")
Set rng = sht.Range("A5")
sht.Activate
For Each t In WordDoc.Tables
    OpenForms = DoEvents
    t.Range.Copy
    OpenForms = DoEvents
    ThisWorkbook.Sheets.Add(After:=Sheets(Sheets.Count)).Name = "Table_" & ia ' add new sheet
    Range("a1").Select ' paste table
    ActiveSheet.PasteSpecial Format:="Text", Link:=False, DisplayAsIcon:=False
    OpenForms = DoEvents
Community
  • 1
  • 1
Rin
  • 43
  • 1
  • 1
  • 4
  • The error occurs at the below code: ActiveSheet.PasteSpecial Format:="Text", Link:=False, DisplayAsIcon:=False – Rin Oct 04 '17 at 18:28
  • Please add your comment to your post. `PasteSpecial` is a method of the `Range` object, not the `Sheet`. – SJR Oct 04 '17 at 18:35
  • Issue with a run-time error 1004: PasteSpecial method of Rang Class failed. The macro works perfectly for me. But when try running in some other systems, the bug occurs – Rin Oct 04 '17 at 18:37
  • @Rin - Make this line: `ActiveSheet.Range("A1").PasteSpecial ...` and remove `Range("a1").Select` – Scott Holtzman Oct 04 '17 at 18:39
  • I tries as suggested. But no luck. Now its showing application defined error – Rin Oct 04 '17 at 18:45
  • What about `ActiveSheet.Paste` or `ActiveSheet.Paste Destination:=Range("A1")`? – u8it Oct 04 '17 at 19:02
  • @u8it - the data table should be pasted as paste special format – Rin Oct 04 '17 at 19:06
  • What about directly accessing the clipboard like this https://stackoverflow.com/a/9024237/3546415? I know sometimes PasteSpecial can act funny, case in point. At the very least it'd let you troubleshoot at the clipboard data level. – u8it Oct 04 '17 at 19:12
  • Thank you very much for your reply. The wierd part is like it works perfectly for me and couple of others. its only one person who is facing this bug. Is that something that can be done with laptop of the person who is getting the bug. Also, as u said, if we try pasting it directly from the clipboard of that person? not sure about it. Just a thought – Rin Oct 04 '17 at 19:25
  • Yes, there are system/OS/software factors that are probably causing this. Without knowing anything about the working and non-working systems though, it's hard to say. So any info like OS and version, version of Office, virtual or RDP session, KVM switch, etc could be a factor. I'd list whatever info you can find between your machines, but also try to separate your two major variables here: PasteSpecial vs. Clipboard behavior. By testing against the clipboard directly you can verify that the problem is PasteSpecial, and it may be better anyway in the long run. – u8it Oct 04 '17 at 19:58
  • @Rin, did you try the first code snippet I provided in my answer, the one with `On Error Resume Next`? Try that and if it works try placing breakpoints after every `PasteSpecial` attempt to see which ones are failing. Also, I'm adding to my answer how to check clipboard content type in Excel. – u8it Oct 05 '17 at 16:00

3 Answers3

3

EDIT

The OP diagnosed the problem as being a concurrency issue where the clipboard was not copied to in time for the paste operation. The code below will solve this issue while maintaining a responsive UI and reasonable timeout and notification.

On Error Resume Next
Dim TimeoutLimit
TimeoutLimit = 300
Dim TimeoutCounter
TimeoutCounter = 0
Do
    Err.Clear
    DoEvents 'Yield thread execution
    ActiveSheet.PasteSpecial Format:="Text", Link:=False, DisplayAsIcon:=False
    TimeoutCounter = TimeoutCounter + 1
Loop Until (Err.Number = 0 Or TimeoutCounter > TimeoutLimit )
On Error GoTo 0
If TimeoutCounter > TimeoutLimit Then
    MsgBox "Paste failed because of operation timeout", vbCritical
    'Is this fatal? Abort by proper exiting...
    'Exit Sub
    'Exit Function
End If

I think your use of ActiveSheet.PasteSpecial is probably fine, based on the testing you've done and this MSDN Documentation. However, your problem might be that you're getting data incompatable with a text format as discussed below.

Regarding the method, the Worksheet.PasteSpecial method is entirely different from the Range.PasteSpecial. I find it odd that the error you're getting says that a range method failed if you're calling the worksheet method. If that's accurate, I suspect that the Worksheet method calls the range method at some point.

I can reproduce the specific error

PasteSpecial method of Range class failed

for Range.PasteSpecial under the following conditions:

  • the clipboard is empty
  • the data was not copied from an Office Application.

Turns out, there is an office clipboard and a Windows clipboard. For instance, Application.CutCopyMode only controls the office clipboard. Range.PasteSpecial works off of the Office clipboard, while Workbook.PasteSpecial works off of the system or Windows clipboard. So it doesn't throw error if the office clipboard is empty, in fact, it throws an error for the opposite condition where an excel range is copied if it's being forced to use Text as its format.

PasteSpecial method of Worksheet class failed

It also throws this error if the clipboard data cannot be converted to text (like a picture). You can handle this by not specifying a format and a default format will be used. It won't necessarily be text then, but this resolves the error for pasting an Excel Range.

To illistrate this and check to see how your clipboard content is stored, view it from Excel as shown below.

enter image description here

Since it is possible that one thing or the other might be giving you different problems under different conditions, you could try to progress through your options like this...

On Error Resume Next
ActiveSheet.Range("A1").PasteSpecial Paste:=xlPasteValues
If Err > 0 Then
    Err.Clear
    ActiveSheet.PasteSpecial Format:="Text", Link:=False, DisplayAsIcon:=False
    If Err > 0 Then
        Err.Clear
        'You could also try to manually retrieve data from clipboard at this point
        ActiveSheet.PasteSpecial
    End If
End If
On Error GoTo 0

Like I said in my comments, PasteSpecial can be finicky. Thus I reccomended removing that as a factor and testing if you can directly access clipboard content like the code below (copied from here)...

Sub GetClipBoardText()
   Dim DataObj As MSForms.DataObject
   Set DataObj = New MsForms.DataObject '<~~ Amended as per jp's suggestion

   On Error GoTo Whoa

   '~~> Get data from the clipboard.
   DataObj.GetFromClipboard

   '~~> Get clipboard contents
   myString = DataObj.GetText(1)
   MsgBox myString

   Exit Sub
Whoa:
   If Err <> 0 Then MsgBox "Data on clipboard is not text or is empty"
End Sub

See this documented case of the same error that was being caused by the clipboard being empty and how easily this can happen with Office Macros. You are copying within your macro, so I wouldn't expect this to be your problem. Also, this code snippet will protect against a null for the Range method but not for the Worksheet method, since it only checks the application's clipboard and not the system's.

If Application.CutCopyMode = True Then
  ActiveSheet.PasteSpecial Format:="Text", Link:=False, DisplayAsIcon:=False
Else
  MsgBox("There is no data on the clipboard to be pasted.")
End If
u8it
  • 3,956
  • 1
  • 20
  • 33
  • 1
    Hi all, Thank you very much for all the help and support. The template works perfect for me now. The issue was caused by concurrency management between applications: the word app has not filled the clipboard before the Excel app calls the clipboard to paste. I confirmed this by investigating the clipboard at run-time using another object type. I tried with • Have Excel hang for a specific amount of time before continuing to the paste function. Application.Wait Now + (TimeValue("00:00:01")) – Rin Oct 05 '17 at 19:55
  • also, I used On Error Resume Next Do Err.Clear ActiveSheet.PasteSpecial Format:="Text", Link:=False, DisplayAsIcon:=False Loop Until Err.Number = 0 On Error GoTo 0 – Rin Oct 05 '17 at 19:55
  • @Rin Concurrency issue definitely makes sense. I like where you're going with the code you posted but you can use a couple improvements there. I'll edit my answer with these. – u8it Oct 05 '17 at 20:05
  • @Rin Also, if this answer has helped you solve your problem it'd be great if you could up-vote and accept it. To accept it, click the check mark beside the answer. Thanks! – u8it Oct 05 '17 at 20:17
  • @ u8it - Yes. Done! Thank you very much for the support. I can use your edited code which you gonna post for future references. Thank you. – Rin Oct 06 '17 at 13:01
  • @Rin Just so you know, I already posted the edited code at the top of my answer. I won't be adding anything else to that. – u8it Oct 06 '17 at 13:06
  • @ u8it - Ok.Thank you – Rin Oct 06 '17 at 17:55
0

.PasteSpecial is a method of a Range object, not a Worksheet object. .Paste is a method of the Worksheet object.

Try replacing,

ThisWorkbook.Sheets.Add(After:=Sheets(Sheets.Count)).Name = "Table_" & ia ' add new sheet
Range("a1").Select ' paste table
ActiveSheet.PasteSpecial Format:="Text", Link:=False, DisplayAsIcon:=False

... with,

ThisWorkbook.Sheets.Add(After:=Sheets(Sheets.Count)).Name = "Table_" & ia ' add new sheet
ActiveSheet.Range("a1").PasteSpecial Format:="Text", Link:=False, DisplayAsIcon:=False
  • This is exactly what Scott Holtzman suggested and the OP said didn't work... Also, if you look in the object browser, PasteSpecial is both a Range as well as Worksheet method, and these methods are actually different. Apparently, the [Worksheet method is better for pasting between applications](https://msdn.microsoft.com/en-us/vba/excel-vba/articles/worksheet-pastespecial-method-excel).... There's also a [TextRange.PasteSpecial](https://msdn.microsoft.com/en-us/vba/powerpoint-vba/articles/textrange-pastespecial-method-powerpoint) – u8it Oct 04 '17 at 20:45
  • This problem has been asked before and the range method solved it. Beyond the OP's use of a default parent worksheet as the new worksheet and use of select to achieve an activecell, this should resolve the problem. If it doesn't, there isn't enough information. –  Oct 04 '17 at 20:53
  • Thank you Jeeped & u8it for all your comments. I wil try it out and will let you know the result. Once again Thank you and really appreciate for your detailed explanation. – Rin Oct 05 '17 at 13:42
  • I replaced my code with "ThisWorkbook.Sheets.Add(After:=Sheets(Sheets.Count)).Name = "Table_" & ia ' add new sheet ActiveSheet.Range("a1").PasteSpecial Format:="Text", Link:=False, DisplayAsIcon:=False", but no luck. – Rin Oct 05 '17 at 14:22
  • @Jeeped The thing I noticed is that, when the template tries to paste the data from the clipboard for the first time, the error shoots up, but the data is already copied to the clipboard and when I try to paste directly from the clipboard, it gets pasted in to spreadsheet. however, when it again loops through, the same bug happens and I have to manually paste it from the clipboard because macro fails to paste into the spreadsheet. – Rin Oct 05 '17 at 14:26
0

@Rin and @ it would be better to add wait function to reduce CPU load. Application.Wait(Now + TimeValue("0:00:10")) 'wait for 10 seconds or as desired to make sure loop doesn't overload cpu and we can reduce TimeoutCounter

--revised code sample-
On Error Resume Next
Dim TimeoutLimit
TimeoutLimit = 6 'counter reduced to 6 attempts
Dim TimeoutCounter
TimeoutCounter = 0
Do
    Err.Clear
    DoEvents 'Yield thread execution
    ActiveSheet.PasteSpecial Format:="Text", Link:=False, DisplayAsIcon:=False
        If Err.Number <> 0 Then Application.Wait (Now + TimeValue("00:00:10")) 'reduces CPU load
    TimeoutCounter = TimeoutCounter + 1
Loop Until (Err.Number = 0 Or TimeoutCounter > TimeoutLimit )
On Error GoTo 0
If TimeoutCounter > TimeoutLimit Then
    MsgBox "Paste failed because of operation timeout", vbCritical
    'Is this fatal? Abort by proper exiting...
    'Exit Sub
    'Exit Function
End If
--------------
  • Application.Wait isn't available in all Office apps so I use the Sleep API instead, incrementing the sleep time by 50ms in a 1 to 10 paste retry loop. This forces the app to wait between a variable 50-500ms before timing out and that has been pretty reliable across lots of different specs of machines with lots of running task variables thrown into the mix in addition to the complexity of the data being copied to the clipboard. – Jamie Garroch - MVP Jul 16 '20 at 16:18