Symptomen
Wanneer u GetObject (Microsoft Visual Basic) of GetActiveObject (Microsoft Visual C++) probeert te gebruiken om een Microsoft Office-toepassing te automatiseren, krijgt u een van de volgende foutberichten, ook al wordt de Office-toepassing uitgevoerd:
Foutbericht 1
Runtimefout '429':
ActiveX-onderdeel kan geen object maken
Foutbericht 2
Fout: 0x800401e3 'Bewerking niet beschikbaar'
Oorzaak
Hoewel de Office-toepassing wordt uitgevoerd, is deze mogelijk niet geregistreerd in de Running Object Table (ROT). Een actief exemplaar van een Office-toepassing moet zijn geregistreerd in de ROT voordat deze kan worden gekoppeld met behulp van GetObject (Visual Basic) of GetActiveObject (Visual C++).
Wanneer een Office-toepassing wordt gestart, worden de actieve objecten niet onmiddellijk geregistreerd. Dit optimaliseert het opstartproces van de toepassing. In plaats van zich bij het opstarten te registreren, registreert een Office-toepassing de actieve objecten in de ROT zodra deze de focus verliest. Als u GetObject of GetActiveObject probeert te gebruiken om te koppelen aan een actief exemplaar van een Office-toepassing voordat de focus van de toepassing is verloren, kan een van de bovenstaande fouten worden weergegeven.
Oplossing
Met behulp van code kunt u de focus van de Office-toepassing wijzigen in uw eigen toepassing (of naar een andere toepassing) zodat deze zichzelf kan registreren in de ROT. Als uw code het exe-bestand van de Office-toepassing start, moet u mogelijk wachten totdat de Office-toepassing is geladen voordat u een verbinding probeert te maken met het actieve exemplaar. In de sectie 'Meer informatie' vindt u een codevoorbeeld als tijdelijke oplossing.
Status
Dit gedrag is inherent aan het ontwerp van het product.
Meer informatie
In de meeste gevallen moeten ontwikkelaars die een Office-toepassing willen automatiseren CreateObject (Visual Basic) of CoCreateInstance (Visual C++) gebruiken om een nieuw exemplaar van de Office-toepassing te starten.
Er zijn echter gevallen waarin u liever een Office-toepassing wilt automatiseren die al wordt uitgevoerd, bijvoorbeeld als de gebruiker de Office-toepassing eerder heeft gestart. Of als u het uitvoerbare bestand van de Office-toepassing hebt gestart met behulp van code, zodat u opdrachtregelopties voor de toepassing kunt opgeven. Als u de actieve Office-toepassing wilt automatiseren, moet u GetObject of GetActiveObject gebruiken.
Stappen om het gedrag te reproduceren
-
Start Microsoft Visual Basic en maak een nieuw Standard EXE-project. Form1 wordt standaard gemaakt.
-
Voeg een besturingselement CommandButton toe aan Form1.
-
Voeg de volgende code toe aan de codemodule van het formulier.
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
-
Zorg ervoor dat de locatie van de Excel.exe juist is in het codevoorbeeld.
-
Sluit Microsoft Excel af als deze al wordt uitgevoerd.
-
Druk op F5 om het project uit te voeren en klik op Command1.
Tijdelijke oplossing
Als u het probleem wilt omzeilen, kunt u het volgende doen:
-
Geef de focus op de Office-toepassing door het tweede argument van de shell-functie te wijzigen in vbMinimizedFocus, vbMaximizedFocus of vbNormalFocus.
-
Geef uw Visual Basic-formulier de focus.
-
Probeer GetObject uit te voeren terwijl u de laadtijd van de Office-toepassing controleert.
De volgende herziene code illustreert deze tijdelijke oplossing.
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
Tijdelijke oplossing voor C++
Als u in C++ programmeert, laat het volgende codevoorbeeld een vergelijkbare tijdelijke oplossing zien als in het bovenstaande Visual Basic-voorbeeld. U ziet dat SetForegroundWindow wordt gebruikt om de focus van Excel te verplaatsen, zodat de actieve objecten kunnen worden geregistreerd.
//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();
Verwijzingen
Klik voor meer informatie op de volgende artikelnummers om het artikel weer te geven: