Microsoft KB Archive/238610

= GetObject or GetActiveObject cannot find a running Office application =

Article ID: 238610

Article Last Modified on 5/11/2007

-

APPLIES TO


 * Microsoft Office Excel 2007
 * Microsoft Office Excel 2003
 * Microsoft Excel 2002 Standard Edition
 * Microsoft Excel 2000 Standard Edition
 * Microsoft Excel 97 Standard Edition
 * Microsoft Visual Basic for Applications 5.0
 * Microsoft Visual Basic for Applications 6.0
 * Microsoft Visual C++ 5.0 Professional Edition
 * Microsoft Visual C++ 6.0 Professional Edition
 * Microsoft Office Access 2007
 * Microsoft Office Access 2003
 * Microsoft Access 2002 Standard Edition
 * Microsoft Access 2000 Standard Edition
 * Microsoft Access 97 Standard Edition
 * Microsoft Office PowerPoint 2007
 * Microsoft Office PowerPoint 2003
 * Microsoft PowerPoint 2002 Standard Edition
 * Microsoft PowerPoint 2000 Standard Edition
 * Microsoft PowerPoint 97 Standard Edition
 * Microsoft Office Word 2007
 * Microsoft Office Word 2003
 * Microsoft Word 2002 Standard Edition
 * Microsoft Word 2000 Standard Edition
 * Microsoft Word 97 Standard Edition

-



This article was previously published under Q238610



SYMPTOMS
When you try to use GetObject (Microsoft Visual Basic) or GetActiveObject (Microsoft Visual C++) to automate a Microsoft Office application, you get one of the following error messages, even though the Office application is running:

Error message 1

Run-time error '429':

ActiveX component can't create object

Error message 2

Error: 0x800401e3 "Operation unavailable"



CAUSE
Although the Office application is running, it might not be registered in the Running Object Table (ROT). A running instance of an Office application must be registered in the ROT before it can be attached to using GetObject (Visual Basic) or GetActiveObject (Visual C++).

When an Office application starts, it does not immediately register its running objects. This optimizes the application's startup process. Instead of registering at startup, an Office application registers its running objects in the ROT once it loses focus. Therefore, if you attempt to use GetObject or GetActiveObject to attach to a running instance of an Office application before the application has lost focus, you might receive one of the errors above.



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 launching the Office application's exe file, you might need to wait for the Office application to finish loading before attempting 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 need to use CreateObject (Visual Basic) or CoCreateInstance (Visual C++) to launch a new instance of the Office application.

However, there are cases where you might prefer to automate an Office application that is already running: for example, if the user previously started the Office application. Or, if you launched the Office application's executable using code so that you could specify command-line switches for the application. In order to automate the running Office application, you must use GetObject or GetActiveObject.

Steps to reproduce the behavior
 Start Microsoft Visual Basic and create a new Standard EXE project. Form1 is created by default. Add a CommandButton control to Form1.  Add the following code to the form's code module. Private Sub Command1_Click Dim oExcel As Object ' Launch a new instance of Microsoft Excel: Shell "C:\Program Files\Microsoft Office\Office\Excel.EXE", _ vbMinimizedNoFocus ' An error 429 occurs on the following line: Set oExcel = GetObject(, "Excel.Application") MsgBox oExcel.Name Set oExcel = Nothing End Sub  Make sure the location of the Excel.exe is correct in the code sample. Quit Microsoft Excel if it is already running.</li> Press F5 to run the project, and click Command1.</li></ol>

Workaround
To work around the problem, you can:
 * Give focus to the Office application by changing the second argument of the Shell function to either vbMinimizedFocus, vbMaximizedFocus, or vbNormalFocus.
 * Give your Visual Basic form the focus.
 * Attempt GetObject while accounting for the Office application's load time.

The following revised code illustrates this workaround. Private Declare Sub Sleep Lib "kernel32" _ (ByVal dwMilliseconds As Long)

Private Sub Command1_Click Dim intSection As Integer Dim intTries As Integer Dim oExcel As Object ' Enable error handler for this procedure: On Error GoTo ErrorHandler ' Launch Microsoft Excel, giving it focus: Shell "C:\Program Files\Microsoft Office\Office\Excel.EXE", _ vbMinimizedFocus 'other options for starting with 'focus: vbMaximizedFocus and vbNormalFocus ' Move focus back to this form. (This ensures the Office   '  application registers itself in the ROT, allowing    '  GetObject to find it.) Me.SetFocus ' Attempt to use GetObject to reference the running ' Office application: intSection = 1 'attempting GetObject... Set oExcel = GetObject(, "Excel.Application") intSection = 0 'resume normal error handling ' Now you can automate Microsoft Excel: MsgBox oExcel.Name & ": able to GetObject after " & _ intTries + 1 & " tries.", vbMsgBoxSetForeground ' Finished with automation so release your reference: Set oExcel = Nothing ' Exit procedure: Exit Sub ErrorHandler: If intSection = 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 '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. intTries = intTries + 1 If intTries < 20 Then Sleep 500 ' wait 1/2 seconds Resume 'resume code at the GetObject line Else MsgBox "GetObject still failing. Process ended.", _ vbMsgBoxSetForeground End If   Else 'intSection = 0 so use normal error handling: MsgBox Error$ End If End Sub

Workaround for C++
If you are programming in C++, the following code sample demonstrates a similar workaround to that shown in the above Visual Basic sample. Notice that SetForegroundWindow is used to move focus away from Excel, allowing it to register its running objects. //Store the handle of the currently active window... HWND hwndCurrent = ::GetForegroundWindow;

//Launch Excel and wait until it is waiting for //user input... STARTUPINFO Start; PROCESS_INFORMATION ProcInfo; ZeroMemory(&Start,sizeof(STARTUPINFO)); Start.cb=sizeof(Start); Start.dwFlags = STARTF_USESHOWWINDOW; Start.wShowWindow = SW_SHOWMINIMIZED;

//Change the path to Excel as needed... LPSTR pszExcelPath = "c:\\program files\\microsoft office\\office\\excel.exe";


 * CreateProcess(NULL, pszExcelPath, 0, 0, 1,

NORMAL_PRIORITY_CLASS, 0, NULL, &Start, &ProcInfo);

if((::WaitForInputIdle(ProcInfo.hProcess, 10000))==WAIT_TIMEOUT) {   ::MessageBox(NULL, "Timed out waiting for Excel.", NULL,                   MB_OK); }

//Restore the active window to the foreground... // NOTE: If you comment out this line, the code will fail!
 * SetForegroundWindow(hwndCurrent);

//Initialize COM library...
 * CoInitialize(NULL);

//Attach to the running instance... CLSID clsid; CLSIDFromProgID(L"Excel.Application", &clsid); IUnknown *pUnk = NULL; IDispatch *pDisp = NULL;

for(int i=1;i<=5;i++) //try attaching for up to 5 attempts {  HRESULT hr = GetActiveObject(clsid, NULL, (IUnknown**)&pUnk); if(SUCCEEDED(hr)) {      hr = pUnk->QueryInterface(IID_IDispatch, (void **)&pDisp); break; }  ::Sleep(1000); } if (!pDisp) { ::MessageBox(NULL, "Failed to find instance!!", "Error",                 MB_ICONHAND); } else { ::MessageBox(NULL, "Got instance of Excel!", "Success", MB_OK); }

//Release the no-longer-needed IUnknown... if (pUnk) pUnk->Release;

//... Add your automation code for Excel here ...

//Release pDisp when no longer needed... if (pDisp) pDisp->Release;

//Cleanup COM... CoUninitialize;

<div class="references_section">