locked
RPC_E_WRONG_THREAD error on Cut and Paste from Excel to Word RRS feed

  • Question

  • Hello,

    I'm hoping someone out there can shed some light on a problem we're experiencing with a custom in-house Excel Add-in. One of our users is getting multiple errors, such as this:

    Error on Data Table FS.Apprs.2.Info. Exception: Unable to cast COM object of type 'Microsoft.Office.Interop.Word.DocumentClass' to interface type 'Microsoft.Office.Interop.Word._Document'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{0002096B-0000-0000-C000-000000000046}' failed due to the following error: The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD)).

    What the add-in does essentially is this:  interate through many named ranges in Excel ('FS.Apprs.2.Info' listed above is an example), perform a 'cut' operation on each range and do a 'paste special' to a matching Word content control in a Word doc.  The paste operation is done initially as linked, then unlinked.  We suspect the issue occurs somewhere in the method that does the cutting and pasting.  Here's a snippet:

    /// <summary> /// Copies the value (as an image) in the excel range to the specified content control in the associated document. /// </summary> /// <param name="cc">Content control in the associated document to receive the value.</param> /// <param name="DataPoint">The data table range in Excel</param>

    internal static void CopyTable(Word.ContentControl cc, Excel.Range DataPoint, List<string> errors) { int innerRepeat = 20; int i = 0; System.Drawing.Image img = null; while (img == null && i < innerRepeat

    { i++; try { DataPoint.Cut(); } catch (System.Runtime.InteropServices.COMException ex) { // Retry with Copy if the Cut command fails on multiple selectins, otherwise throw the exception if (ex.ErrorCode == -2146827284) DataPoint.Copy(); else throw (ex); } img = Clipboard.GetImage(); }

    if (img == null) { errors.Add(string.Format("Clipboard null error on {0}", DataPoint.Name)); } else { cc.Range.Delete(); cc.Range.PasteSpecial(Link: true, DisplayAsIcon: false, Placement: Word.WdOLEPlacement.wdInLine, DataType: Word.WdPasteDataType.wdPasteOLEObject); cc.Range.Fields.Unlink(); CCTag tag = new CCTag(cc); tag.SetDocument(Globals.ThisAddIn.Application.ActiveWorkbook.Name);

    }

    }

    What is interesting is this:  when the user is logged on to our domain through the network at his office, the add-in runs just fine with no errors.  It is only when he's working from home or outside the office in general does he get the errors. I don't have an exact number, but I would say most of our users do not have this issue.  This particular user is using Windows 7 (32bit), Office 2010. 

    Any help would be greatly appreciated - please let me know if you need more info.

    Regards,

    James

    Friday, May 25, 2012 4:41 PM

Answers

  • Hi James

    "Real-life" has caught up to me and a lot of stuff has come up in the last 24 hours; the weekend is going to be burning the candle at both ends, I fear. Possibly, I could get back to this next week, but it's looking "iffy"...

    Could you give this a "bump" on Monday or so, to get it back on my radar?


    Cindy Meister, VSTO/Word MVP

    Thursday, May 31, 2012 5:06 PM

All replies

  • Hi James

    Could you show us the declarations and instantiation of the objects for Word application, the document and the content control being passed to the method, please?

    And the errors are happening only in this method?


    Cindy Meister, VSTO/Word MVP


    Saturday, May 26, 2012 6:41 AM
  • using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using DataSyncCommon;
    using Excel = Microsoft.Office.Interop.Excel;
    using Word = Microsoft.Office.Interop.Word;
    using Office = Microsoft.Office.Core;
    using Microsoft.Office.Core;
    using System.Windows.Forms;
    using System.IO;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    namespace DataSync_For_Excel
    {
    	/// <summary>
    	/// Represents the associated word document if one exists, and exposes methods and properties of the associated document.
    	/// Most functionality requires the associated document to be open.
    	/// Specifics of the mechanics of association are handled in DataSyncCommon\AssociatedOfficeFiles
    	/// </summary>
    	class AssociatedDocument: IDisposable
    	{
    		//	Private members
    		private Word.Application application;
    		private Word.Document document;
    		
    		//	Public members
    		/// <summary>
    		/// <summary>
    		/// Gets a reference to the associated word document object and returns it
    		/// </summary>
    		public Word.Document Document
    		{
    			get 
    			{
    				if (document == null && Application != null)
    				{
    					var documents = Application.Documents;
    					document = documents.Cast<Word.Document>().Where(doc => doc.FullName == AssociatedFilePath).SingleOrDefault();
    					
    					//	Never open a document, keeping for reference/testing
    					//document = Application.Documents.Open(AssociatedFilePath);
    				}
    				return document;
    			}
    		}
    		/// <summary>
    		/// Gets a reference to the running application object of the associated document
    		/// </summary>
    		public Word.Application Application
    		{
    			get
    			{
    				try
    				{
    					if (application == null)
    					{
    						Process[] processes = Process.GetProcessesByName("WINWORD");
    						if (processes.Length != 0)
    						{
    							application = (Word.Application)Marshal.GetActiveObject("Word.Application");
    						}
    						else
    						{
    							// WMS: if we get to here then word is most likely not open
    							//	MC: commenting this out, since we never want to open a file we don't need to get a new app object if nothing is open.
    							//		Also we are having issues with phantom word processes, and i think these are the cause
    							//application = new Word.Application();
    						}
    					}
    				}
    				catch
    				{
    					//	WMS: if we get to here then word is most likely not open
    					//	MC: commenting this out, since we never want to open a file we don't need to get a new app object if nothing is open.
    					//		Also we are having issues with phantom word processes, and i think these are the cause
    					//	MC: Update, we do in one scenario want to open a new word file, but we do that at that spot in the code so we don't get side effects here.
    					//application = new Word.Application();
    				}
    				return application;
    			}
    		}
    		//	Constructor
    		/// <summary>
    		/// Constructor, initializes with the current associated document if one exists
    		/// </summary>
    		public AssociatedDocument()
    		{
    			string thisDocPath = Globals.ThisAddIn.Application.ActiveWorkbook.Path + "\\";
    			AssociatedOfficeFiles assocFiles = new AssociatedOfficeFiles(Globals.ThisAddIn.Application.ActiveWorkbook.CustomXMLParts);
    			//	The program should stop us from adding more than one associated file because associate isn't available if this property exists.
    			AssociatedFilename = assocFiles.GetFileList().Select(file => file.FileName).SingleOrDefault();
    			AssociatedFilePath = string.IsNullOrEmpty(AssociatedFilename) ? string.Empty : Path.Combine(thisDocPath, AssociatedFilename);
    		}
    	}
    	public static class SyncHelper
    	{
            static BackgroundWorker _bw;
            static AssociatedDocument _doc;
            static List<string> _errors;
            static long _elapsedTime;
    	/// <summary>
    	/// Main Sync Function, syncs all data points and data tables on the tabs specified.
    	/// </summary>
    	/// <param name="DataPointSheets">The sheets to sync all data points from.</param>
    	/// <param name="DataTableSheets">The sheets to sync all data tables from.</param>
    	/// <param name="SyncType">Type of the sync, used for display purposes e.g. sync all, sync current tab, etc.</param>
    	internal static void PerformSync(List<Excel.Worksheet> DataPointSheets, List<Excel.Worksheet> DataTableSheets, string SyncType)
    	{
    	    List<string> errors = new List<string>();
                List<Excel.Name> syncablePoints = SyncHelper.GetDataItemsToSync(DataPointSheets, DataType.DataPoint, errors, Globals.ThisAddIn.Application.ActiveWorkbook);
                List<Excel.Name> syncableTables = SyncHelper.GetDataItemsToSync(DataTableSheets, DataType.DataTable, errors, Globals.ThisAddIn.Application.ActiveWorkbook);
                AssociatedDocument doc = new AssociatedDocument();
                try
    	    {
    	            //Syncing tables first to avoid resource use errors.
    		    foreach (Excel.Name name in syncableTables)
    		    {
    			try		
    			{
    				SyncHelper.copyDataTableRange(name, doc, errors);
    				prog.IncrimentProgress();
    				Clipboard.Clear();
    			}
    			catch (Exception ex)
    			{
    				errors.Add(string.Format("Error on Data Table {0}. Exception: {1}", name.Name, ex.Message));
    				Logging.Log(ex.Message + " | " + ex.StackTrace, LogLevel.Error);
    			}
    		    }
                   
    	            if (errors.Count == 0)
    		    {
    			prog.Close();
    		    }
    		    else
    		    {
    			//Show errors on status box
    			prog.Errors = errors;
    			prog.ShowErrors();
    		    }
    	    }
                    
    	    catch (Exception ex)
    	    {
    		errors.Add(ex.Message);
    		prog.Errors = errors;
    		prog.ShowErrors();
    		Logging.Log(ex.Message + " | " + ex.StackTrace, LogLevel.Error);
    	    }
    			
    	}
    	/// <summary>
    	/// Copies a data table to a collection of controls in the associated document.
    	/// Copies values to content controls in word with the same name as the data table, there can be more than one content control with the same name.
    	/// </summary>
    	/// <param name="name">Excel named range to be copied.</param>
    	/// <param name="doc">Associated document to copy values to.</param>
            internal static void copyDataTableRange(Excel.Name name, AssociatedDocument doc, List<string> errors)
    	{
    	    Excel.Range DataTable = name.RefersToRange;
                
                Word.ContentControls ctrls = doc.Document.SelectContentControlsByTitle(name.Name);
    			
      	    foreach (Word.ContentControl cc in ctrls)
    	    {
    		DataSyncCommon.RetryUtility.RetryAction(() => CopyTable(cc, DataTable, errors), 1, 300);   // JT changed retry times from 10 to 0
                }
    	}
    }

    Cindy,

    Thank you for taking the time to look at this.  I have included the relevant classes and methods (hopefully without bombarding you with code).  If you have any further questions, please don't hesitate.

    Best regards,

    James

    Wednesday, May 30, 2012 11:06 PM
  • To anwer your questin, yes, the errors appear to occur in the original method I posted.  In other words, if I were to comment out the 'CopyTable' method, then no errors occur.

    James

    Wednesday, May 30, 2012 11:09 PM
  • Hi James

    "Real-life" has caught up to me and a lot of stuff has come up in the last 24 hours; the weekend is going to be burning the candle at both ends, I fear. Possibly, I could get back to this next week, but it's looking "iffy"...

    Could you give this a "bump" on Monday or so, to get it back on my radar?


    Cindy Meister, VSTO/Word MVP

    Thursday, May 31, 2012 5:06 PM
  • Hi Cindy,

    I understand, no worries.  Your request on how the application is instantiated got me thinking that I need to revisit the code there, so that does give me a possible lead.  I appreciate your help on this!

    Regards,

    James

    Tuesday, June 5, 2012 5:08 AM