Don't use ApplicationClass (unless you have to)

Don't use ApplicationClass (unless you have to)

  • Comments 15

A comment on Mike Howard's blog exhibits a common problem that I see time and time again: developers are creating instances of Word.ApplicationClass or Excel.ApplicationClass in their projects.

Even though it's the wrong thing to do, I don't blame them for doing that. I blame IntelliSense.

First things first: What's the right way to do it? Well, just use Word.Application or Excel.Application (or any other type that follows the same Thinggy / ThinggyClass pattern).

But IntelliSense doesn't show me that as a valid option!

That's because IntelliSense is not as intelligent as its name might suggest. IntelliSense has a simple rule that says "after the user types the new keyword, show the user a list of new-able things." (Note I may be simplifying things here as I don't work on IntelliSense... but it explains how the system works. Feel free to correct me if you work on that team ;-) ). IntelliSense believes that the only things you can new are concrete, visible classes with one or more visible constructors. And it's almost right.

In general, you cannot new an interface because interfaces have no implementation. But the default interfaces on COM CoClasses are a little different; the CLR knows something that IntelliSense's parents forgot to teach it. Crack open the Excel PIA using ILDASM and have a look at the Application interface. Among the gobbledy-gook, you will see the following:

.custom instance void [mscorlib]System.Runtime.InteropServices.CoClassAttribute::.ctor(class [mscorlib]System.Type) = ( 01 00 2F 4D 69 63 72 6F 73 6F 66 74 2E 4F 66 66

69 63 65 2E 49 6E 74 65 72 6F 70 2E 45 78 63 65

6C 2E 41 70 70 6C 69 63 61 74 69 6F 6E 43 6C 61

73 73 00 00 ) // ../Microsoft.Office.Interop.Excel.ApplicationClass

This tells the CLR that when someone wants to create an instance of type Application, it should really go ahead and create an instance of ApplicationClass.

Another tip: In Microsoft Word, you may want to handle the Quit event to do something when the user closes down the application. But the Word.Application interface defines Quit as a method, not an event. What to do?

Well, you cast it to a Word.ApplicationEvents4_Event (you could find this out by using the Object Browser):

  Word.Application app;

  app = new Word.Application();

  app.Visible = true;

 

  Word.ApplicationEvents4_Event appEvents = app;

  appEvents.Quit += new Word.ApplicationEvents4_QuitEventHandler(QuitHandler);

Both of these are due to the way COM works (again I am going to gloss over the details here, so feel free to correct me or point readers to a more accurate, in-depth blog if you have one). COM doesn't really have the notion of a 'class' as a first-class (ha ha) object; everything to COM is an interface. Now of course in order to get a real live implementation of an interface in your hot little hand, you need to be able to instantiate a concrete implementation of the interface through a function such as CreateObject or CoCreateInstance. But these things simply use the ProgID or ClassID to look up the implementation provider in the registry, and from then on you're pretty much dealing with interfaces. The COM coclass Application gets turned into the managed class ApplicationClass and a new interface Application is created to represent it and given the custom attribute you see above. You should always bind to this interface. I believe this is for versioning reasons, but I can't really remember now (this topic discusses the versioning problem when exposing .NET objects to COM, but we're doing the reverse here).

The reason you have to have the funky cast to get the Quit event is that COM has no notion of overloads and, indeed, no native notion of events: interfaces can only have methods on them. Instead of having events, a class can expose one or more "source" interfaces which say to clients of the interface "if you implement this interface and then register it with me then I will call the methods on it in an event-like fashion." Wherever possible, the CLR will "collapse" the events from the class' source interfaces into the main interface (eg, the Application.WorkbookOpen event in Excel actually comes from the AppEvents_Event interface), but when they have the same name as a method it can't do that so you need to explicitly cast to the original interface.

Like I said, I missed a lot of detail there (and maybe even told some fibs) so if someone else wants to give a more detailed (and correct!) explanation, please do so.

But the gist of it all is this: Just as Luke Skywalker puts away the computer targeting system and is told to "use the force" in Star Wars Episode IV, so too should you put away IntelliSense and never use ApplicationClass when programming against Office.

  • using MSWord = Microsoft.Office.Interop.Word;

    MSWord.Application wapp = new MSWord.Application();
    wapp.Visible = true;

    I am also using the Offce/Word Primary Interop Assembly, and Strong Name *is* set to true...

    I don't get it...

    Help me me Obi Wan Kenobi, you're my only hope,
  • I guess I'm ignorant, but if it creates ApplicationClass when you ask for Application, then what's wrong with asking for ApplicationClass in the first place?
  • JaD: Is this on Office XP or 2003?

    Do you have any AddIns loaded, or any kind of extensions to Office? Have you tried a "repair" operation on the Office product?
  • Jim: Like I said, I'm not an expert but I beleive that the reason is that ApplicationClass could change its layout in v2 in which case you would be out of luck. For example, if it adds another interface that changes the vTable layout, you would end up binding to the wrong class method.
  • Its Office 2003...I guess I could try to repair/reinstall Office. :-(
  • Repair / Reinstall failed .. O well...maybe someone at MSFT will able to duplicate the problem
  • People are looking into it here... did you also install the recent Office Update? Have you re-built your solution, or are you just trying to run an already-built solution? There were some problems with the Office core DLL being registered incorrectly with a recent Office patch.
  • Doing things the hard way with RSS Bandit leads to some interesting statistics; Stuff for my Boss and co-workers; SOA and Joe Developer -- Phillip gets it right (again); Bits on Reporting Services; Wake up and smell RSS.NET; htmlArea (drool); InfoPath duh; McD's
  • If you open several docs, but you only want to work with only one, you can have a bool variable that determine if document is opened:


    using MWD =Microsoft.Office.Interop.Word;

    ----------....
    object nada=Missing.Value;
    object falso =false;

    /// bool variable that determine if document is opened
    bool bWDAbierto=false;

    public void openOtherDocument(object FILENAME){
    try
    {
    if(bWDAbierto)wd.Documents.Close(ref falso, ref nada, ref nada);
    else wd= new MWD.Application();
    MWD._Document doc = wd.Documents.Open(ref FILE_NAME, ref nada,ref nada, ref nada, ref nada, ref nada,ref nada,ref nada,ref nada,ref nada,ref nada,ref nada,ref nada,ref nada,ref nada);
    wd.DocumentBeforeSave+=new MWD.ApplicationEvents3_DocumentBeforeSaveEventHandler(wd_DocumentBeforeSave);
    wd.DocumentBeforeClose+=new Microsoft.Office.Interop.Word.ApplicationEvents3_DocumentBeforeCloseEventHandler(wd_DocumentBeforeClose);
    wd.Visible=true;
    bWDAbierto=true;
    statusBar1.Text="";
    }
    catch(Exception e2)
    {
    statusBar1.Text = "Error Opening Microsoft Word : " + e2.Message;
    wd=null;
    bWDAbierto=false;
    }
    }

    private void wd_DocumentBeforeClose(Microsoft.Office.Interop.Word.Document Doc, ref bool Cancel)
    {
    bWDAbierto=false;
    }
  • I am having major issues trying to interop w/ word 9.

    I am using Managed C++ (VC++ 2003) and I can import the word com library but when I try to new Interop::Word::Application it tells me I can't create an instance of an interface... I can create an instance of Interop::Word::ApplicationClass but the data members all are access violations.. I haven't the slightest clue what I am doing wrong and the only info I can find is for C#. Can any one help me???
  • Unfortunately only Office XP and 2003 are supported with managed code.

    You should be able to talk to Word through normal COM interop though.
  • while i am trying to create the object of Excel.Application class it shows the following error:

    Access is denied.
    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

    Exception Details: System.UnauthorizedAccessException: Access is denied.

    ASP.NET is not authorized to access the requested resource. Consider granting access rights to the resource to the ASP.NET request identity. ASP.NET has a base process identity (typically {MACHINE}\ASPNET on IIS 5 or Network Service on IIS 6) that is used if the application is not impersonating. If the application is impersonating via <identity impersonate="true"/>, the identity will be the anonymous user (typically IUSR_MACHINENAME) or the authenticated request user.

    To grant ASP.NET write access to a file, right-click the file in Explorer, choose "Properties" and select the Security tab. Click "Add" to add the appropriate user or group. Highlight the ASP.NET account, and check the boxes for the desired access.

    Source Error:


    Line 64:
    Line 65:
    Line 66: Microsoft.Office.Interop.Excel.Application ThisApplication = new Microsoft.Office.Interop.Excel.Application();
    Line 67:
    Line 68: string strFilename=Server.MapPath(@"\ExcelToXml")+ @"\myExcell.xlt";


    Source File: c:\inetpub\wwwroot\exceltoxml\toexceltemplate.aspx.cs Line: 66

    Stack Trace:


    [UnauthorizedAccessException: Access is denied.]
    ExcelToXML.ToExcelTemplate.btnToExcel_Click(Object sender, EventArgs e) in c:\inetpub\wwwroot\exceltoxml\toexceltemplate.aspx.cs:66
    System.Web.UI.WebControls.Button.OnClick(EventArgs e)
    System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument)
    System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument)
    System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData)
    System.Web.UI.Page.ProcessRequestMain()


    Please sombody help me on this issue

    regards,
    binoy
  • Guess I have some input.

    First of all i discovered that i had not installed Office 2003 properly, I was missing some components like ".Net-programming support" and other stuff.
    This might aply to an older input above.

    And for biony, try put the <identity impersonate="true"/> in your web.config as told by the trace and give the access rights to ASPNET user.
  • Is there a way to handle Quit event for Excel? I don't know why, but Excel.Application class has no ApplicationEvents4 and no Quit handler :(
Page 1 of 1 (15 items)