I need to finish an application in C#.
Now I want to get a function that control a Excel file to get data.
I used getActiveObject("Excel.Application"), but this returns nothing. I can't use Excel.Application in VS2008, and Microsoft.Office.Interop.Excel.Application is used instead.
So is there another way to do this?
Microsoft.Office.Interop.Excel.Application e =
(Microsoft.Office.Interop.Excel.Application)Marshal.GetActiveObject(
"Excel.Application");
After about a half a day of playing around I finally figured out how to make this work so you can latch onto an open copy of excel. My users have been complaining mightily about having too many instances of Excel open.
Here is a snippet of what I did to get it working:
_Application excelApp;
try
{
excelApp = (_Application)Marshal.GetActiveObject("Excel.Application");
}
catch(Exception)
{
// this is important. If Excel is not running, GetActiveObject will throw
// an exception
excelApp = null;
}
if( excelApp == null )
{
excelApp = new ApplicationClass();
}
I have been chasing this for a while and finally had some time to hunker down and figure it out.
I also have been working on Excel Addins, in namespaces use
using Excel=Microsoft.Interop.Office.Excel;
instead of Microsoft.Interop.Office.Excel use the above line, for more understanding look at my code:
using System.IO;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
using Excel = Microsoft.Office.Interop.Excel;
using Microsoft.Office.Interop.Excel;
using static MMCAPP2010.Profile;
using System.Runtime.InteropServices;
namespace MMCAPP2010
{
public class ExcelTemplateLoad
{
public void DeserializeObject(string filename)
{
Excel.Application instance;
Workbook wb = null;
try
{
//getting the current running instance of an excel application
instance = (Excel.Application)Marshal.GetActiveObject("Excel.Application");
}
catch
{
instance = new Excel.Application();
}
//opening the template
wb = instance.Workbooks.Open(#"C:\Users\U1152927\Downloads\sample.xltx");
In order to be able to refer to the Excel object model as "Excel", then you can create an alias via a using statement at the top of your namespace (or document) as follows:
using Excel = Microsoft.Office.Interop.Excel;
Thereafter, you can refer to Excel.Application instead of the long-winded Microsoft.Office.Interop.Excel.Application.
As for why your call to Marshal.GetActiveObject is failing, I can't say for sure. A couple of thoughts:
(1) Are you certain that there is a running instance of Excel already present? If not, then create a new Excel application:
Excel.Application xlApp = new Excel.Application();
(2) If there definitely is an Excel application already running, then it is possible that the Excel instance has not yet been added to the Running Objects Table (ROT) if the Excel Application has never lost focus. See: Visual C# .NET Error Attaching to Running Instance of Office Application for more on this. I believe that the Marshal.GetActiveObject method should throw an exception in this case -- not quietly return null -- but this still seems potentially relevant.
I hope this helps...
Mike
Please see the MS KB as given in the link below...
http://support.microsoft.com/kb/238610
Related
I am getting a very stupid error. I am new to VSTO and I need to get the location of the Excel file in some variable in my Addin.
string name = ActiveWorkbook.FullName;
I am getting a red line below ActiveWorkbook with error:
The name ActiveWorkBook does not exist in the current context.
I have added reference of Microsoft.Office.Interop.Excel in the code but its showing this error. I am new to this.. am I missing something?
In Excel VSTO, you need to use Globals.ThisAddIn.Application to get access to the Excel Application Model, see below :
var wb = Globals.ThisAddIn.Application.ActiveWorkbook;
string name = wb.FullName;
see also Programming VSTO Add-ins
If your code is inside the ThisAddIn class you can directly call: this.Application.ActiveWorkbook
ActiveWorkbook is not a class. It's the property of the Application interface. You can not call it in the class-name manner.
Then, you need to change your code to this.Application.ActiveWorkbook.FullName;
I am making a C# application (using Microsoft Visual Studio 2015, using WinForms) that needs to open a password protected .docx file in C#. I know you can do:
Process.Start("WINWORD.EXE", "filename.docx");
To start word (I'm using Microsoft Word 2016) with that file loaded in. But I also want to submit the password to Word so it opens without me doing anything. I know it's not this simple, but this is what imagining right now:
public void submitPassword(string password)
{
Process.Start("WINWORD.EXE", "filename.docx" //something like this: System.Password.Sumbit("WINWORD.EXE", password));
}
There is a COM type library called Microsoft Word Object Library. In Visual Studio, you can add a reference to it from the solution explorer. Right click References, click Add Reference, go to the COM tab, and search for Word.
With the package Microsoft.Office.Interop.Word, you can create an Application instance and tell it to open a document using a password.
using Microsoft.Office.Interop.Word;
...
public void SubmitPassword(string password)
{
Application app = new Application();
app.Documents.Open(FileName: #"filepath", PasswordDocument: password);
}
The answer provided to me by Andrew Piliser got me close, but not there. This is what works:
using Word = Microsoft.Office.Interop.Word;
...
public void submitPassword()
{
var wordApp = new Word.Application();
wordApp.Visible = true;
wordApp.Documents.Open(FileName: #"filepath", PasswordDocument: "filepassword");
}
I have an app using Excel COM interop. It copies a template XLS to an existing doc, replacing same-named tabs by Delete old then Copy new from template.
I am getting 800A03ec exception when my app tries to delete a worksheet.
This problem is in code that has been working for years but now fails after an upgrade to Office 2016.
I find that if I set app.Visible = true, the operation completes properly! But I do not want Excel visible.
If app.Visible = false, I do not get an error from the first worksheet Delete(), but exception occurs on the second.
The first delete of the last tab seems to go OK, but 'Sheets' array of the worksheets object doesn't decrease as I would expect. However the corresponding Sheet item in the array becomes a "null" worksheet.
The second delete, of the tab before it, throws an exception on delete.
I have thoroughly ensured:
No COM reference leaks
All COM references are discarded before each delete, except for the worksheet to be deleted and its parent app, etc. objects
DisplayAlerts is false
Worksheets are simple and ordinary, nothing hidden etc.
Why would it work when app is visible, but not work when app is hidden??
UPDATE: code fragment
int _DeleteTargetTab(string tabName)
{
List<object> comRefs = new List<object>();
int prevTabIndex;
int tabToDelete = _FindTargetTemplateIndex(tabName, out prevTabIndex);
if (tabToDelete > 0)
{
try
{
Excel.Sheets targetSheets = _workbook.Sheets;
comRefs.Add(targetSheets);
Excel.Worksheet targetWorksheet = targetSheets[tabToDelete];
comRefs.Add(targetWorksheet);
targetWorksheet.Delete();
}
finally
{
ExcelUtility.ReleaseAll(comRefs);
}
}
return prevTabIndex;
}
and ExcelUtility.ReleaseAll() calls Marshal.ReleaseComObject(), then GC.Collect() and GC.WaitForPendingFinalizers().
Target worksheet has 8 tabs. Last tab is deleted, then copied, without exception. Then on tab 7 delete throws an exception, only when app is hidden. Works fine on older Office.
Copy code is
void _CopyTemplateTabToTarget(Excel.Worksheet templateWorksheet, int prevTargetTabIndex)
{
List<object> comRefs = new List<object>();
try
{
Excel.Sheets targetSheets = _workbook.Sheets;
comRefs.Add(targetSheets);
Excel.Worksheet prevSheet = targetSheets[prevTargetTabIndex];
comRefs.Add(prevSheet);
templateWorksheet.Copy(Type.Missing, prevSheet);
}
finally
{
ExcelUtility.ReleaseAll(comRefs);
}
}
Init code is
_app = new Excel.Application();
_app.DisplayAlerts = false;
_app.Visible = false;
I got further by adding this:
//
// Super important to activate a tab other than what needs to be deleted.
// Cast is required because an event and method have the same name "Activate".
//
((Excel._Worksheet)_weeklyDataSheet).Activate();
Then that got me to some code around my apps' Save function that used to work but was throwing exception:
_weeklyDataSheet.Select(Type.Missing);
I changed that to Activate() as well and made more progress. But yet Excel still threw exception on Delete() this time on the third workbook.
I was forced to run app Visible for the week's report. But even that ran through about 80 workbooks and then Excel locked up, mouse cursor rapidly flashing between arrow and wait timer animation, and my app got RPC error eventually.
Conclusion: ABANDON COM APIS for Office 2016. Microsoft seems not to support them properly anymore.
I have a simple excel file that has the following code:
Private Sub Workbook_Open()
MsgBox "Hello World!"
End Sub
I tried to run a sql job to open this excel file but it failed, became unresponsive. On googling, I found the reason 'why' SQL job wouldn't open excel file
Job On Sql Server Agent does not complete, but it does in BIDS?
So I thought of creating a simple console application in C# which would simply open the excel file and run my macro. Here's my code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Excel = Microsoft.Office.Interop.Excel;
using System.Threading;
namespace T_OpenExcel
{
class Program
{
static void Main(string[] args)
{
Excel.Application xlApp;
Excel.Workbook xlWorkBook;
//Excel.Worksheet xlWorkSheet;
object misValue = System.Reflection.Missing.Value;
xlApp = new Excel.Application();
xlWorkBook = xlApp.Workbooks.Open(#"E:\data_extracts\TestHelloWorld.xlsm", 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
RunMacro(xlWorkBook, new Object[] { "TestHello" });
Thread.Sleep(5000);
xlWorkBook.Close(true, misValue, misValue);
xlApp.Quit();
}
private static void RunMacro(Excel.Workbook xlWorkBook, object[] p)
{
//throw new NotImplementedException();
}
}
}
I could successfully run this code in IDE. Now I want to run this from SQL job in SSMS, sql server 2008.
I grabbed T_OpenExcel.exe file from my C# project( T_OpenExcel-->bin-->Debug-->T_OpenExcel.exe). I created a SQL Job.Here are some of my details:
Step name: OpenHelloWorldExcel
Type:Operating system(CmdExec)
Run as: SQL Server Agent Service Account
Command: C:\Users\shress2\Documents\visual studio 2010\projects\T_OpenExcel\T_OpenExcel\bin\Debug\T_OpenExcel.exe
On running this job, I get the following status
Start Job 'TestHelloWorld' Status Success
Execute job 'TestHelloWorld' Status Error
On viewing history, it shows:
Message
Executed as user: GSOPS4\SYSTEM. Unhandled Exception: System.Runtime.InteropServices.COMException: Microsoft Excel cannot access the file 'E:\data_extracts\TestHelloWorld.xlsm'.
There are several possible reasons:
The file name or path does not exist.
The file is being used by another program.
The workbook you are trying to save has the same name as a currently open workbook. at Microsoft.Office.Interop.Excel.Workbooks.Open(String Filename, Object UpdateLinks, Object ReadOnly, Object Format, Object Password, Object WriteResPassword, Object IgnoreReadOnlyRecommended, Object Origin, Object Delimiter, Object Editable, Object Notify, Object Converter, Object AddToMru, Object Local, Object CorruptLoad) at T_OpenExcel.Program.Main(String[] args) in C:\Users\shress2\documents\visual studio 2010\projects\T_OpenExcel\T_OpenExcel\Program.cs:line 23. Process Exit Code -532462766. The step failed.
I checked E:\data_extracts\TestHelloWorld.xlsm directory and found it working. I made sure my xlsm file is not being used by anyone. I couldn't figure out why its failing to run it. Any help is greatly appreciated. Thanks in adv!
The problem appears to be that you're attempting to show a messagebox while opening the Excel file, but you're automating it, so there's no human to click the button on the messagebox. The rest of the code can't execute because the file is sitting there waiting for someone to click the button.
On subsequent runs, the server can't access it because it's still sitting there, invisible to normal users, waiting for the button to be clicked. hence the errors.
Short version: Don't proompt for user input on an app that will be run unattended.
Just after a little help with late binding.
I am trying to late bind excel and i don't have any issues doing that. It is only when I have more than one instance of excel open where I run into some problems.
I would like to be able to determine what instance of excel to bind to (and the link events etc.). Main reason being I have an application that opens a excel document from a third party tool and the events aren't handled. I want to be able to tap into the particular excel instance that I know is open to catch the events. Only problem is if excel is already open by the user (doesn't matter how).
If excel is opened after the bind, obviously, I don't get a problem. It is only when excel is already open.
It seems that the binding is done to the first instace that is open.
Here is the actual code:
Type excelType = Type.GetTypeFromProgID("Excel.Application");
// Throw exception if the type wasn't found
if (excelType == null)
throw new Exception(error);
//Get the Excel.Application Type by creating a new type instance
excelApplication = Marshal.GetActiveObject("Excel.Application");
//Throw exception if the object couldn't be created
if (excelApplication == null)
throw new Exception(error);
this.withEvents = withEvents;
//Create link between the Word.Applications events and the ApplicationEvents2_WordEvents class
if (this.withEvents)
{
excelEvents = new ExcelApplicationEvents();
//holds the connection point references of the Word.Application object
IConnectionPointContainer connectionPointContainer = excelApplication as IConnectionPointContainer;
//Find the connection point of the GUID found from Red Gate's .Net Reflector
Guid guid = new Guid("00024413-0000-0000-C000-000000000046");
connectionPointContainer.FindConnectionPoint(ref guid, out connectionPoint);
//Advise the Word.Application to send events to the event handler
connectionPoint.Advise(excelEvents, out sinkCookie);
excelEvents.WorkbookBeforeSaveEvent += new EventHandler<WorkbookEventArgs>(ExcelEventsWorkbookBeforeSaveEvent);
}
Any Ideas?
Cheers,
Dale.
Getting a particular instance of Excel requires that you make use of the AccessibleObjectFromWindow API.
This is explained well in the article Getting the Application Object in a Shimmed Automation Add-in by Andrew Whitechapel.
What you want, however, is to execute it using late binding. This is described in detail by divo here: How to use use late binding to get excel instance.