How to get CustomDocumentProperties using Excel Interop? - c#

The below code is used to get the custom document properties for Excel workbook.
var xlApp = Globals.ThisAddIn.Application; // This works in VSTO Excel Add-in
var xlApp = new global::Microsoft.Office.Interop.Excel.Application(); // This doesn't work anywhere
xlApp.Visible = true;
global::Microsoft.Office.Interop.Excel.Workbook workbook = xlApp.Workbooks.Open(file, false, true, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, false, Type.Missing, Type.Missing);
global::Microsoft.Office.Core.DocumentProperties properties = workbook.CustomDocumentProperties; // Exception occurs here
global::Microsoft.Office.Core.DocumentProperty property = properties["propertyname"];
The first 2 lines are references to the Excel Application. One obtain the reference from VSTO add-in internals, the other is a regular new Application().
When using the Application from VSTO internals, the code run fines without any problems. But when using new Application(), the workbook.CustomDocumentProperties line throws InvalidCastException:
Unable to cast COM object of type 'System.__ComObject' to interface
type 'Microsoft.Office.Core.DocumentProperties'. This operation failed
because the QueryInterface call on the COM component for the interface
with IID '{2DF8D04D-5BFA-101B-BDE5-00AA0044DE52}' failed due to the
following error: No such interface supported (Exception from HRESULT:
0x80004002 (E_NOINTERFACE)).
I am trying to make it to work on a C# winforms project without VSTO. A lot of examples and tutorials use new Application() for Excel interop, but I noticed that Microsoft.Office.Interop.Excel.Application is an interface, so using new on interface is actually strange to me. How can I create a proper Application that can get the CustomDocumentProperties?
Reference Assemblies I am using:
Microsoft.Office.Interop.Excel v2.0.50727 Version 14.0.0.0
Microsoft.CSharp v4.0.30319 Version 4.0.0.0

I noticed that Microsoft.Office.Interop.Excel.Application is an interface, so using new on interface is actually strange to me.
That is strange indeed, but by design. The Excel.Application interface is decorated with the CoClass attribute telling the actual class to instantiate on 'instantiating' the interface. More about it here.
But when using new Application(), the workbook.CustomDocumentProperties line throws InvalidCastException:
Strange indeed again. I have experienced some issues myself using document properties. It seems that the actual class returned differs from the spec, so I moved to use dynamic in order to prevent type casting issues.
So instead of this:
Microsoft.Office.Core.DocumentProperties properties = workbook.CustomDocumentProperties;
Use:
dynamic properties = workbook.CustomDocumentProperties;

How can I create a proper Application that can get the CustomDocumentProperties?
There is no need to create a new Excel Application instance if you develop an add-in. You should use the Application property provided by the VSTO runtime:
var xlApp = Globals.ThisAddIn.Application; // This works in VSTO Excel Add-in
But if you develop a standalone application which automates Excel, in that case you need to create a new Application instance by using the new operator:
var xlApp = new global::Microsoft.Office.Interop.Excel.Application();
Use the late binding technology (Type.InvokeMember) for getting or setting document property as the How To Use Automation to Get and to Set Office Document Properties with Visual C# .NET article suggests.

I've had a same problem. Today it has been resolved. There is a different approach to derive the results. The question and its' answer is at
How can I read excel custom document property using c# excel interop
Here is my implementation.
public string CheckDocProp(string propName, object props)
{
Excel.Workbook workBk = Globals.ThisAddIn.Application.ActiveWorkbook;
object customProperties = workBk.CustomDocumentProperties;
Type docPropsType = customProperties.GetType();
object nrProps;
object itemProp = null;
object oPropName;
object oPropVal = null;
nrProps = docPropsType.InvokeMember("Count",
BindingFlags.GetProperty | BindingFlags.Default,
null, props, new object[] { });
int iProps = (int)nrProps;
for (int counter = 1; counter <= ((int)nrProps); counter++)
{
itemProp = docPropsType.InvokeMember("Item",
BindingFlags.GetProperty | BindingFlags.Default,
null, props, new object[] { counter });
oPropName = docPropsType.InvokeMember("Name",
BindingFlags.GetProperty | BindingFlags.Default,
null, itemProp, new object[] { });
if (propName == oPropName.ToString())
{
oPropVal = docPropsType.InvokeMember("Value",
BindingFlags.GetProperty | BindingFlags.Default,
null, itemProp, new object[] { });
return oPropVal.ToString();
break;
}
else
{
return "Not Found.";
}
}
return "Not Found.";
}
Usage:
object docProps = wb.CustomDocumentProperties;
string prop1 = ExistsDocProp("<CustomProperty>", docProps);

Related

Why am I getting a superfluous sheet even though I'm explicitly setting sheet count to 1?

I want to avoid generated spreadsheets having empty/superfluous sheets named "Sheet1" and such. I thought I could do that by specifying how many sheets a workbook should have this way:
_xlApp = new Excel.Application { SheetsInNewWorkbook = 1 };
...But I'm still getting an unwanted "Sheet1" in addition to the sheet I create. Here is the pertinent code:
using Excel = Microsoft.Office.Interop.Excel;
. . .
private Excel.Application _xlApp;
private Excel.Workbook _xlBook;
private Excel.Sheets _xlSheets;
private Excel.Worksheet _xlSheet;
. . .
private void InitializeSheet()
{
_xlApp = new Excel.Application { SheetsInNewWorkbook = 1 };
_xlBook = _xlApp.Workbooks.Add(Type.Missing);
_xlBook.Worksheets.Add(Type.Missing, Type.Missing, Type.Missing, Type.Missing);
_xlSheets = _xlBook.Worksheets;
_xlSheet = (Excel.Worksheet)_xlSheets.Item[1];
_xlSheet.Name = String.Format("Price Compliance {0} {1}", _month, _year);
}
So since setting SheetsInNewWorkbook to 1 in the Excel.Application instance doesn't do the trick, what do I need to do to prevent these tramp sheets from showing up?
The answer to your question can be found in the documentation of the Template parameter on the Workbook.Add method.
[...] If this argument is omitted, Microsoft Excel creates a new
workbook with a number of blank sheets (the number of sheets is set by
the SheetsInNewWorkbook property).
Your code is omitting it, therefore it is creating a single Worksheet for you (since you've set SheetsInNewWorkbook to 1.
That property is also constrained to be between 1 and 255, so you aren't able to add a work book without a sheet (unless you use a file template).
Also from the Template parameter documentation:
If this argument is a constant, the new workbook contains a single
sheet of the specified
type. Can be one of the following Microsoft.Office.Interop.Excel.XlWBATemplate
constants: xlWBATChart, xlWBATExcel4IntlMacroSheet, xlWBATExcel4MacroSheet,
or xlWBATWorksheet.
So an alternative way to do this is:
_xlApp = new Excel.Application();
_xlBook = _xlApp.Workbooks.Add(Excel.XlWBATemplate.xlWBATWorksheet);
_xlSheets = _xlBook.Worksheets;
_xlSheet = (Excel.Worksheet)_xlSheets.Item[1];
_xlSheet.Name = String.Format("Price Compliance {0} {1}", _month, _year);
Which simply renames the single created sheet.

Excel interop doesn't accept ranges with commas

According to the documentation of Workbook.Range, you can provide commas in the first argument to provide an union.
However, the following code throws a COMException with HRESULT 0x800A03EC on the line that gets the range:
using Microsoft.Office.Interop.Excel;
public void RangeWithCommas() {
var excel = new Application();
var wb = excel.Workbooks.Add(xlWBATemplate.xlWBATWorksheet);
var ws = (Worksheet)wb.Worksheets[1];
var range = ws.Range["A1,A2"]; // this throws an exception
Console.WriteLine(range.Address[false,false]);
ws.Delete();
wb.Close(false);
excel.Quit();
}
How can I do or fix this?
P.S. I am aware of Application.Union but I would very much prefer not to use it because there is no easy way to provide a variable number of arguments.
As AnalystCave.com pointed out, some Excel COM methods are locale specific. You need to use the regional list seperator when accessing the COM method.
This code should work correctly on all locales:
using Microsoft.Office.Interop.Excel;
public void RangeWithCommas() {
var excel = new Application();
var wb = excel.Workbooks.Add(xlWBATemplate.xlWBATWorksheet);
var ws = (Worksheet)wb.Worksheets[1];
var rangestring = String.Join((string)excel.International[XlApplicationInternational.xlListSeparator], new [] {"A1","A2"});
var range = ws.Range[rangestring];
Console.WriteLine(range.Address[false,false]);
ws.Delete();
wb.Close(false);
excel.Quit();
}

Read the calculated values from Excel using AddIn Formulas and Microsoft Object Library

we are trying to retrieve a calculated value from a cell which has add-In formulas in it.
The sample add-in "myUtilityl.xla" is working properly in excel. It retrieves value for the addin function =ISOWEEKNUM(F9). But we are unable to retrieve the value programatically using C# & Microsoft Object Library. The add-In "myUtilityl.xla" is attached to Excel. Environment is VS2010
I am providing the sample code here.
string path = #"C:\Test.xls";
Workbook theWorkbook;
Worksheet theWorksheet;
Range readRange;
Microsoft.Office.Interop.Excel.Application app = new Microsoft.Office.Interop.Excel.Application();
theWorkbook = app.Workbooks.Open(path);
Sheets theSheets = (Sheets)theWorkbook.Worksheets;
theWorksheet = (Worksheet)theWorkbook.Worksheets.get_Item("Sheet1");
readRange = theWorksheet.get_Range("B1");
MessageBox.Show(Convert.ToString(readRange.Value));
//theWorkbook.Save();
app.Workbooks.Close();
I am new to Microsoft Object library. Any help or clue will be very helpful.
Well Brijesh its working now. The only thing that was missing was that we have to open the xla.
app.Workbooks.Open(xlaFilePath);
Then it started working..
Thank you very much. i am posting the code here anyways
string path = #"C:\Test2.xls";
string xlaPath = #"C:\Test2.xla";
Workbook theWorkbook;
Worksheet theWorksheet, theWorksheet2;
Range readRange;
Microsoft.Office.Interop.Excel.Application app = new Microsoft.Office.Interop.Excel.Application();
app.Workbooks.Open(xlaPath);
theWorkbook = app.Workbooks.Open(path);
theWorksheet2 = (Worksheet)theWorkbook.Worksheets.get_Item("Sheet2");
theWorksheet2.get_Range("A3").Value = 7;
theWorksheet2.get_Range("A4").Value = 7;
theWorkbook.RefreshAll();
theWorksheet = (Worksheet)theWorkbook.Worksheets.get_Item("Sheet1");
readRange = theWorksheet.get_Range("A1");
Console.WriteLine(Convert.ToString(readRange.Value));
Console.ReadLine(); //theWorkbook.Save();
theWorkbook.Close();
app.Workbooks.Close();
Above code inputs two values into cells of sheet2 and the VBA UDF calculated value is retrieved.
you may add following in your code sample
var addins = Application.AddIns.Add(xlaFilePath);
if (!addins.Installed)
{
addins.Installed = true;
}

Exception "Error Decrpyting Data"when opening drawing

I have a program that does batch processing on some drawings. One of the drawings throws an exception "Error Decrypting Data" when I try to open it. This drawing in particular was generated by a third-party tool other than AutoCAD. In addition, this problem only occurs in AutoCAD 2011. In AutoCAD 2010 it prompts the user that the file was generated outside of AutoCAD but they can click and the batch will continue. I've tried opening it using both the managed .NET API and the COM Interop API but both give the same error.
Here is a post from the AutoCAD formus though it didn't provide me with a solution:
http://forums.autodesk.com/t5/NET/Error-Decrypting-Data-Acad-2011/td-p/2661762/highlight/true
Managed API
string drawingFilePath = #"C:\Drawings\MyDrawing.dwg";
Application.DocumentManager.Open(drawingFilePath, false);
COM Interop
string drawingFilePath = #"C:\Drawings\MyDrawing.dwg";
Object comAutoCAD = Application.AcadApplication;
Object comDocuments = comAutoCAD.GetType().InvokeMember("Documents", BindingFlags.GetProperty, null, comAutoCAD, new object[] { });
Object comDocument = comDocuments.GetType().InvokeMember("Open", BindingFlags.InvokeMethod, null, comDocuments,
new object[] { drawingFilePath, false, Type.Missing });
Document.FromAcadDocument(comDocument);
Someone from the AutoCAD forums posted an answer that works for me.
http://forums.autodesk.com/t5/NET/Error-Decrypting-Data-Acad-2011/td-p/2661762/page/2
Here is an example:
const string systemVar_DwgCheck = "DWGCHECK";
Int16 dwgCheckPrevious = (Int16)Application.GetSystemVariable(systemVar_DwgCheck);
Application.SetSystemVariable(systemVar_DwgCheck, 2);
Document document = Application.DocumentManager.Open(#"C:\Drawings\MyDrawing.dwg", false);
// Do stuff...
Application.SetSystemVariable(systemVar_DwgCheck, dwgCheckPrevious);

Excel Automation From .NET - creating a new worksheet

I am attempting what seems like a simple task: using C# to create a new Excel document containing new worksheets.
For some reason, I am getting a strange COM error (0x800A03EC)
Has anyone managed to get this to work? Does anyone have suggestions as to how to troubleshoot this?
I've isolated this into the minimum amount of code:
using Microsoft.Office.Interop.Excel;
using System.Diagnostics;
namespace ExcelAutomation
{
public static class ExcelTests
{
public static void CreateWorksheet()
{
try
{
var app = new Microsoft.Office.Interop.Excel.Application();
app.Visible = true;
var workBooks = app.Workbooks;
var newWorkbook = app.Workbooks.Add(XlWBATemplate.xlWBATWorksheet);
Worksheet existingWorksheet = (Worksheet)newWorkbook.Sheets[1];
Worksheet workSheet = (Worksheet)newWorkbook.Sheets.Add
(
null, // before
existingWorksheet,
null, // 1,
null //XlSheetType.xlWorksheet
);
}
catch (System.Runtime.InteropServices.COMException ex)
{
Trace.WriteLine(string.Format("Caught COMException. Message: \"{0}\"", ex.Message));
}
}
}
}
The output window now says:
Caught COMException. Message: "Exception from HRESULT: 0x800A03EC"
The mistake I'm making is to use null for optional values that I don't want to set.
Instead I should use System.Reflection.Missing.Value
Worksheet workSheet = (Worksheet)newWorkbook.Sheets.Add
(
existingWorksheet, // before
System.Reflection.Missing.Value,
System.Reflection.Missing.Value, // 1,
System.Reflection.Missing.Value //XlSheetType.xlWorksheet
);
I Think this will help you
Visit http://www.aspose.com/docs/display/cellsnet/Adding+New+Worksheets+to+Workbook+and+Activating+a+Sheet

Categories