Microsoft KB Archive/308409

From BetaArchive Wiki

Article ID: 308409

Article Last Modified on 1/17/2007



APPLIES TO

  • Microsoft Visual Basic .NET 2003 Standard Edition
  • Microsoft Visual Basic .NET 2002 Standard Edition
  • Microsoft Office Access 2003
  • Microsoft Access 2002 Standard Edition
  • Microsoft Office Excel 2003
  • Microsoft Excel 2002 Standard Edition
  • Microsoft Office PowerPoint 2003
  • Microsoft PowerPoint 2002 Standard Edition
  • Microsoft Office Word 2003
  • Microsoft Word 2002 Standard Edition



This article was previously published under Q308409

For a Microsoft Visual Basic 6.0 version of this article, see 238610.
For a Microsoft Visual C# .NET version of this article, see 316125.


SYMPTOMS

When you try to use GetObject or System.Runtime.InteropServices.Marshal.GetActiveObject from Microsoft Visual Basic .NET to automate a running Microsoft Office application, you may receive one of the following error messages:

With GetObject:

Cannot create ActiveX component.

With System.Runtime.InteropServices.Marshal.GetActiveObject:

An unhandled exception of type 'System.Runtime.InteropServices.COMException' occurred in mscorlib.dll

Additional information: Operation unavailable

CAUSE

Although the Office application is running, it may not be registered in the Running Object Table (ROT). A running instance of an Office application must be registered in the ROT before before it can be automated.

When an Office application starts, it does not immediately register its running objects. This optimizes the application startup process. Instead of registering at startup, an Office application registers its running objects in the ROT after it loses focus. Therefore, if you attempt to attach to a running instance of an Office application before the application has lost focus, you may receive an error message.

RESOLUTION

Using code, you can change focus from the Office application to your own application (or to some other application) to allow it to register itself in the ROT. Additionally, if your code is starting the executable (.exe) file for the Office application, you may need to wait for the Office application to finish loading before you attempt to attach to the running instance. A code sample is provided as a workaround in the "More Information" section.

STATUS

This behavior is by design.

MORE INFORMATION

In most situations, developers who want to automate an Office application should use CreateObject (or New) to start a new instance of the Office application. In some cases, however, you may prefer to automate an Office application that is already running, such as if the user previously started the Office application or if you start the .exe file for the Office application by using code so that you can specify command-line switches for the application. In order to automate the running Office application, you must use GetObject.

Steps to Reproduce Behavior

  1. Start Microsoft Visual Studio .NET.
  2. On the File menu, click New, and then click Project. Under Project types, click Visual Basic Projects, and then click Windows Application under Templates. Form1 is created by default.
  3. On the View menu, click Toolbox to display the toolbox, and then add a button to Form1.
  4. Double-click Button1. The code window for the form appears.
  5. In the code window, replace the following code:

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    
        End Sub
                        

    with:

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim oExcel As Object
            ' Start a new instance of Microsoft Excel.
            Shell("C:\Program Files\Microsoft Office\Office10\Excel.exe", _
                AppWinStyle.MinimizedNoFocus)
            ' "Cannot create ActiveX component." occurs on the following line:
            oExcel = GetObject(, "Excel.Application")
            MsgBox(oExcel.Name, MsgBoxStyle.MsgBoxSetForeground)
            oExcel = Nothing
        End Sub
                        
  6. Make sure that the location of the Excel.exe file is correct in the code sample.
  7. Quit Microsoft Excel if it is already running.
  8. On the Debug menu, click Start.


WORKAROUND

To work around the problem, follow these steps:

  1. Give focus to the Office application by changing the second argument of the Shell function to AppWinStyle.MinimizedFocus, AppWinStyle.MaximizedFocus, or AppWinStyle.NormalFocus.
  2. Give your Visual Basic form the focus.
  3. Try to use GetObject while accounting for the load time of the Office application.

The following revised code illustrates this workaround:

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim iSection As Integer
        Dim iTries As Integer
        Dim oExcel As Object

        ' Enable an error handler for this procedure.
        On Error GoTo ErrorHandler

        ' Start Excel, giving it focus.
        Shell("C:\Program Files\Microsoft Office\Office10\Excel.exe", _
            AppWinStyle.MinimizedFocus)

        ' Move focus back to this form. (This ensures the Office
        '  application registers itself in the ROT, allowing
        '  GetObject to find it.)
        AppActivate(Title:=Me.Text)

        ' Attempt to use GetObject to reference the running
        '  Office application.
        iSection = 1 'attempting GetObject...
        oExcel = GetObject(, "Excel.Application")
        iSection = 0 'resume normal error handling

        ' Automate Excel.
        oExcel.ActiveCell.Value = "Hi"
        MsgBox(oExcel.Name & ": able to GetObject after " & _
            iTries + 1 & " tries.", MsgBoxStyle.MsgBoxSetForeground)
        
        ' You are finished with automation, so release your reference.
        oExcel = Nothing

        ' Exit procedure.
        Exit Sub

ErrorHandler:
        If iSection = 1 Then 'GetObject may have failed because the
            'Shell function is asynchronous; enough time has not elapsed
            'for GetObject to find the running Office application. Wait
            '1/2 seconds and retry the GetObject. If you try 20 times
            'and GetObject still fails, assume some other reason
            'for GetObject failing and exit the procedure.
            iTries = iTries + 1
            If iTries < 20 Then
                System.Threading.Thread.Sleep(500) ' wait 1/2 seconds
                AppActivate(Title:=Me.Text)
                Resume 'resume code at the GetObject line
            Else
                MsgBox("GetObject still failing. Process ended.", _
                    MsgBoxStyle.MsgBoxSetForeground)
            End If
        Else 'iSection = 0 so use normal error handling:
            MsgBox(Err.Description, _
                MsgBoxStyle.MsgBoxSetForeground)
        End If
    End Sub
                

REFERENCES

For more information, visit the following Microsoft Developer Network (MSDN) Web site:

Microsoft Office Development with Visual Studio
http://msdn2.microsoft.com/en-us/library/aa188489(office.10).aspx


Keywords: kbautomation kbprb KB308409