I get the “Command Failed” exception when using the following code to insert content from one word 2007 document into another using bookmarks in c# :
string filePath = #“C:\temp\one.doc”;
object trueObj = true;
object falseObj = false;
wordApp.Selection.InsertFile( filePath, ref missing, ref falseObj, ref trueObj, ref falseObj );
"one.doc" is another word document containing table content.
Error code: -2146824090. This error generally comes when the target object is disposed or unavailable. Not sure why I am getting it here.
Also when I remove table content from the target document and I just add formatted text, the operation succeeds. When the same operation is performed through word GUI, the operation works fine. Have scoured the internet for pointers on this issue, but none were helpful in resolving this.
Thanks in advance,
Bharath K.
We have solved this problem by defining a macro that performs the above actions and invoking the macro using c# from my program. That worked!
I encountered this problem and I noticed that range where to insert the file contains some locked content controls. Before using InsertFile command make sure that range (Selection here) does not contain locked content controls.
Regards.
Related
With c# and Interop Word I am trying to merge all the files in a specified directory into a new document. My code loops through a list of file names and uses "InsertFile" to add each one to a Selection.
There are a lot of files, and the process is failing after a while. The error message reads:
The disk is full. Free some space on this drive, or save the document on another disk.
Try one or more of the following:
Close any unneeded documents, programs, and windows.
Save the document on another disk."
At the point where the InsertFile fails the selection is using about 7MB. My disk has 300GB of free space and the machine has 32GB of RAM.
The files I am saving do not have any graphics or Math in them.
What am I doing wrong? The exception is getting thrown on the InsertFile line inside the foreach loop (below).
The code follows in a second. Please note that I start from a winword instance, which is a member of a class called PrintObj. Also, please note that this code works fine if I use it with a small number of files, like 100-200).
Document mergeDoc = PrintObj.WinWord.Documents.Add(ref missing, ref missing,
ref missing, ref missing);
mergeDoc.PageSetup.LeftMargin = globalPrintObj.WinWord.InchesToPoints(0.5f);
mergeDoc.PageSetup.RightMargin =
PrintObj.WinWord.InchesToPoints(0.5f);
string[] filePaths = Directory.GetFiles(directoryPath, "*.docx");
string[] documentsToMerge = filePaths;
Array.Sort(documentsToMerge);
// Make a Word selection object.
Selection selection = PrintObj.WinWord.Selection;
//A counter that signals that we shoudn't insert a page break at the end of
document.
int breakStop = 0;
//Count the number of documents to insert;
int documentCount = documentsToMerge.Length;
// Loop thru each of the Word documents
foreach (string file in documentsToMerge)
{
breakStop++;
// Insert the files to our template
selection.InsertFile(file, ref missing, ref missing, ref missing,
ref missing);
if (breakStop != documentCount)
{
selection.InsertBreak(ref pageBreak);
}
}
Directory.CreateDirectory(directoryPath);
//Save the document
obj wordFileName = fileNameWithExtension;
mergeDoc.SaveAs2(ref wordFileName);
Could you check the path with the name of the files?
sometime ago i remember got that error of Disk full for the 255 characters problem, maybe one of those files has a too longe path
https://support.code42.com/CrashPlan/4/Troubleshooting/Windows_file_paths_longer_than_255_characters
try save the files in a different directory, like C:\YourFolder
Eventually I gave up on the MS assemblies and used the Xceed.Words.NET to solve this, which works. When using the Selection object the process always hung after about 840 insert docs (it was always the same exact number of insert docs), which was a problem because I needed to insert more than 1200 files. The final doc currently has just over 15,706,000 characters and over 11,700 pages, and is just over 4MB.
It seems to me that this is a flaw in the Interop Word Document Class because appending documents is a pretty common use case.
((Note added a couple of weeks later - had a chance to go back and try adding a counter every time an insert is done and then
if (Counter % 100 == 0)
{
mergeDoc.UndoClear();
}
This worked. Thank you to CM who suggested it.
I'm working in an application that interact with Word. We import documents in our database then allow the user to modify those documents using Microsoft Word via NetOffice.WordApi.
We are having some issues when the document is marked as read-only, everytime is opened, we get a 'Save As' word dialog that gives you the option of creating a temporal copy of the document to allow you to make changes in the document.
My question is as follow: How can remove the read-only word mark of the document and re-save the document without the mark? I can remove the option manually from word following the instructions in the link below, but I want to automatise that process by code.
The author would like you to open this as read-only
** PLEASE BEFORE ANSWER NOTICE THE FOLLOWING: It's not the read-only property in the file at windows level, it's not an issue with the windows permision attibute. The file attributes are NOT Read-only the property is inside the word document. So changing the windows file permision attributes by code doesn't work, actually the files are not read-only when you check the file properties in windows, it's a word attribute which means the author marked the document as read-only when he saved it and will stop you to modify the file using word (file can be changed using a different software, it just read-only for word). Please don't sent me links about how to change permissions in windows at that's not the case, check the link for more info. **
Many thanks for your time.
The option you want to set is the Document.ReadOnlyRecommended property of the Word document.
When you open a Word document, you can actually set the ReadOnly property with the third argument in the Document.Open method. That argument won't, however, override the read-only recommended setting on a saved document. So if your document is saved with read-only recommended option, it will be opened as read-only when calling Document.Open.
So I think that you have two option:
Option 1
Set the Document.ReadOnlyRecommended to false before the Document is saved for the first time, similar to this
objDoc.ReadOnlyRecommended = false;
Option 2
If the document already is set to read-only recommended, you need to save the document as a new file with the Document.ReadOnlyRecommended property set to false using the Document.SaveAs2 method.
Your code might look like this:
object missing = System.Reflection.Missing.Value;
object readOnly = false;
object fileName = #"C:\User\MyFile.docx";
object newFileName = #"C:\User\MyNewFile.docx";
var objApp = new Application();
var objDoc = objApp.Documents.Open(ref fileName, ref missing, ref readOnly);
if (objDoc.ReadOnlyRecommended)
{
objDoc.SaveAs2(ref newFileName, ref missing, ref missing, ref missing, ref missing, ref missing, ref readOnly);
}
objDoc.Close();
objApp.Quit();
I am using the C# Excel interop and I want to create a copy of a chart from one sheet but I want this copy on another sheet. I have tried the following:
Excel.ChartObject chartTemplate = (Excel.ChartObject)sheetSource.ChartObjects("chart 1");
object o = chartTemplate.Duplicate();
Excel.ChartObject chart = (Excel.ChartObject)sheetSource.ChartObjects("chart 2");
chart.Name = "Skew" + expiry.ToString("MMMyy");
range = sheetDestination.Range["T" + chartRowCoutner.ToString()];
chart.Chart.Location(Excel.XlChartLocation.xlLocationAsObject, range);
But when I try this, the last line throws an error:
An unhandled exception of type 'System.Exception' occurred in projectname.exe
Additional information: Error reading Excel file C:\ ...the file path...\template.xlsx: Value does not fall within the
expected range.
I have also tried passing a sheet in instead of a range:
chart.Chart.Location(Excel.XlChartLocation.xlLocationAsObject, sheetDestination);
but this gives the same error. I can't understand the reason for the error or how to fix it / bypass it.
I am trying to avoid bringing the clipboard into this, but even if I try copying and pasting, I can still only paste it as an image, which is really not ideal:
Excel.ChartArea chartArea = chart.ChartArea;
chartArea.Copy();
range = sheetDestination.Range["T" + chartRowCoutner.ToString()]; // Note that chart is not on the sheet sheetDestination
range.PasteSpecial(Excel.XlPasteType.xlPasteAll);
The only other solution I can think of now is to do this in VBA and then execute the macro via the interop. But surely it can be done in a clean way just using the interop without the clipboard.
You've already got the solution but instead of giving you a fish for a day I'll give you a proper answer that will help you with any C# Excel coding task.
The C# Interop Model for Excel is almost identical to the VBA Excel Model.
This means it's trivial to convert VBA recorded macros to C#. Let's try this with an exercise like moving a chart to a different sheet.
In the Developer Tab in Excel click Record Macro > right click Chart > select Move Chart > choose Object in: Sheet2 > click OK > click Stop Macro Recording.
To see the recorded Macro press Alt + F11 to bring up the VB Editor:
See in the above screenshot how VBA shows you the second parameter for Location() is Name and it's actually a string argument...
Let's convert this VBA Macro to C#:
EDIT by #Ama
The advice below is outdated, there's actually no need to worry about releasing COM objects, this is done automatically at RELEASE mode (DEBUG mode does not). See Hans Passant's answer to "Clean up Excel Interop Objects with IDisposable".
The trick here is: never use 2 dots with com objects.
Notice how I could have written:
var sheetSource = workbookWrapper.ComObject.Sheets["Sheet1"];
but that has two dots, so instead I write this:
var workbookComObject = workbookWrapper.ComObject;
var sheetSource = workbookComObject.Sheets["Sheet1"];
Ref: How do I properly clean up Excel interop objects?
You will see the AutoReleaseComObject code in the above QA that projects like VSTOContrib use.
Here is the complete code:
using Microsoft.Office.Interop.Excel;
...
var missing = Type.Missing;
using (AutoReleaseComObject<Microsoft.Office.Interop.Excel.Application> excelApplicationWrapper = new AutoReleaseComObject<Microsoft.Office.Interop.Excel.Application>(new Microsoft.Office.Interop.Excel.Application()))
{
var excelApplicationWrapperComObject = excelApplicationWrapper.ComObject;
excelApplicationWrapperComObject.Visible = true;
var excelApplicationWrapperComObjectWkBooks = excelApplicationWrapperComObject.Workbooks;
try
{
using (AutoReleaseComObject<Workbook> workbookWrapper = new AutoReleaseComObject<Workbook>(excelApplicationWrapperComObjectWkBooks.Open(#"C:\Temp\ExcelMoveChart.xlsx", false, false, missing, missing, missing, true, missing, missing, true, missing, missing, missing, missing, missing)))
{
var workbookComObject = workbookWrapper.ComObject;
Worksheet sheetSource = workbookComObject.Sheets["Sheet1"];
ChartObject chartObj = (ChartObject)sheetSource.ChartObjects("Chart 3");
Chart chart = chartObj.Chart;
chart.Location(XlChartLocation.xlLocationAsObject, "Sheet2");
ReleaseObject(chart);
ReleaseObject(chartObj);
ReleaseObject(sheetSource);
workbookComObject.Close(false);
}
}
finally
{
excelApplicationWrapperComObjectWkBooks.Close();
ReleaseObject(excelApplicationWrapperComObjectWkBooks);
excelApplicationWrapper.ComObject.Application.Quit();
excelApplicationWrapper.ComObject.Quit();
ReleaseObject(excelApplicationWrapper.ComObject.Application);
ReleaseObject(excelApplicationWrapper.ComObject);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}
private static void ReleaseObject(object obj)
{
try
{
while (System.Runtime.InteropServices.Marshal.ReleaseComObject(obj) > 0);
obj = null;
}
catch (Exception ex)
{
obj = null;
Console.WriteLine("Unable to release the Object " + ex.ToString());
}
}
I know Releasing all the Objects, using GC.Collect and not using two dots when assigning seems over the top but at least when I quit the instance of Excel the process is freed, I don't have to programmatically kill the Excel process!
Ref: Microsoft KB: Office application does not quit after automation from .NET client
From the MSDN documentation here:
https://msdn.microsoft.com/en-us/library/microsoft.office.tools.excel.chart.location.aspx
it states that for the Name parameter of type object:
Name
Type: System.Object
The name of the sheet where the chart is embedded if Where is xlLocationAsObject or the name of the new sheet if Where is xlLocationAsNewSheet.
This is somewhat misleading from the example at the bottom of the same linked page. It would appear from the example given, that you should actually pass a string of the sheet name. The pertinent line from the example is copied below (the example is for copying to a new sheet):
chart1.Location(Excel.XlChartLocation.xlLocationAsNewSheet,
"Sales");
So, for moving to an existing sheet, I would do:
chart1.Location(Excel.XlChartLocation.xlLocationAsObject,
"ExistingSheetName");
Do NOT pass a range, workbook or worksheet object. Try a string of the sheet name.
Now, from the same MSDN document page linked above, if you want to reposition the chart within the page once you have moved it to another sheet, there are additional instructions, repeated here for convenience:
If you want to move a chart to another position on a sheet, use the P:Microsoft.Office.Interop.Excel.ChartArea.Top property and P:Microsoft.Office.Interop.Excel.ChartArea.Left property of the ChartArea. You can get the ChartArea object of the Chart by using the ChartArea property.
If you're moving a chart to an existing sheet, be careful not to overlap your chart over existing data. If so, you will have to code around that separately.
This isn't the answer to the question you asked, but might be fruitful
if you're making a copy and editing it for different variations THIS IS NOT A SOLUTION
if you're truly just copying a chart then I recommend using Excel's "Camera" function instead. It basically creates a window into another sheet - you can do this programmatically and it's well documented, but a little known feature of excel I thought I'd be remiss if I didn't point out.
-E
If you are looking to make edits & the question is still open let me know that in a comment - I've done this before I just need to look back in my workbook and see exactly how I did it.
'Camera option is nice because it doesn't 'recalculate' the data - so I imagine it operates faster; a concern in large workbooks.
I want to Accept all the track changes from word document. I have written following codes to do so.(I am using PowerTools from codeplex.)
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(filePath, true))
{
OpenXmlPowerTools.RevisionAccepter.AcceptRevisions(wordDoc);
}
But above code is not working in some of the document. It shows System.Exception: Internal error 20, found element exception in some of the document.
So is there any issue with my word document? If yes then what should I look into document? In short I want to know that what is wrong with my document so that I can correct my document to run above code.
Another thing is that I am able to accept tracking changes in Word 2013/2010/2007 itself!!
Any help would be highly appreciated,
I have asked same question on https://powertools.codeplex.com/discussions and they have updated RevisionAccepter.cs file to get this resolved.
Thread from Power Tools Discussion
UPDATED:
I have now added the following code based on the answers below:
foreach (Word.XMLSchemaReference reference in Globals.ThisDocument.Application.ActiveDocument.XMLSchemaReferences)
{
if (reference.NamespaceURI.Contains("ActionsPane"))
{
reference.Delete();
}
}
This gives me no errors at design, time, but still gives the user the message described in the original question about choosing an xml expansion pack. So the original problem hasn't been solved.
ORIGINAL QUESTION:
Using Visual Studio 2013, I have created a Word Document level project which has an action pane. Everything works well. The only problem is what when someone uses this documents action pane to insert text into the document and then save it. The next time that saved document is opened, the user gets the following message
One or more XML expansion packs are available for this file.
Choose one from the list below.
No XML expansion pack
Microsoft Actions Pane 3
How do I stop this from happening when saved documents are opened?
You need to check the XMLSchemaReferences of the word document to see if any of the Xml schema's has a namespace referring to the action pane and if so, delete it.
This needs to be done before saving.
The message you get when opening the document is because it contains a schema reference to the action pane namespace.
Something like this :
foreach (XMLSchemaReference reference in wordDocument.XMLSchemaReferences)
{
if (reference.NamespaceURI.Contains("ActionsPane"))
{
reference.Delete();
}
}
where wordDocument is the actual word document you create.
If you don't have a reference to the word document and you just want to use the current document that has the focus, you can use Globals.ThisAddIn.Application.ActiveDocument instead of wordDocument in the code.
I've solved my problem using Huron answer. Thanks Huron.
Remove your xmlreference on your active document.
for my case, i remove my xmlreference on the after mail merge event
void ThisApplication_MailMergeAfterMerge(Word.Document Doc, Word.Document DocResult)
{
DocResult.Fields.Update();
// remove customization
Office.DocumentProperties properties = (Office.DocumentProperties) DocResult.CustomDocumentProperties;
properties["_AssemblyName"].Delete();
properties["_AssemblyLocation"].Delete();
DocResult.RemoveDocumentInformation(Word.WdRemoveDocInfoType.wdRDIDocumentProperties);
foreach (XMLSchemaReference reference in DocResult.XMLSchemaReferences)
{
if (reference.NamespaceURI.Contains("ActionsPane"))
{
reference.Delete();
}
}
ThisApplication.Visible = true;
ThisApplication.NormalTemplate.Saved = true;
Doc.MailMerge.DataSource.Close();
}