Příznaky
Při pokusu o automatizaci aplikace systému Microsoft Office pomocí GetObject (Microsoft Visual Basic) nebo GetActiveObject (Microsoft Visual C++) se zobrazí jedna z následujících chybových zpráv, i když běží aplikace Office:
Chybová zpráva 1
Chyba za běhu "429": komponenta ActiveX nemůže vytvořit objekt
Chybová zpráva 2
Chyba: 0x800401e3 "operace není dostupná."
Příčina
Přestože aplikace Office běží, nemusí být registrovaná v aplikační tabulce objektů (HNILOBu). Běžící instance aplikace Office musí být registrována v HNILOBě před tím, než ji lze připojit pomocí funkce GetObject (Visual Basic) nebo GetActiveObject (Visual C++). Když se aplikace Office spustí, neregistruje okamžitě svoje běžící objekty. Tím se optimalizuje proces spuštění aplikace. Aplikace Office se místo registrace při spuštění zaregistruje ve všech použitých objektech ve HNILOBě, jakmile ztratí fokus. Když se tedy pokusíte použít funkci GetObject nebo GetActiveObject k připojení ke spuštìné instanci aplikace Office před tím, než se aplikace ztratí fokus, může se zobrazit jedna z výše uvedených chyb.
Řešení
Pomocí kódu můžete z aplikace Office změnit zaměření na vlastní aplikaci (nebo na jinou aplikaci), abyste se mohli přihlásit do HNILOBy. Pokud navíc váš kód spouští soubor exe aplikace Office, možná budete muset počkat, než se pokusíte připojit ke spuštění aplikace Office. Ukázka kódu je k dispozici v části Další informace.
Stav
Toto chování je záměrné.
Další informace
Ve většině případů musí vývojáři, kteří chtějí automatizovat aplikaci Office, použít funkci CreateObject (Visual Basic) nebo CoCreateInstance (Visual C++) ke spuštění nové instance aplikace Office. Existují však případy, kdy je vhodnější automatizovat aplikaci Office, která je již spuštěna: například když uživatel dříve spustil aplikaci Office. Nebo pokud jste spustitelný soubor aplikace Office spustili pomocí kódu, abyste mohli pro aplikaci zadat přepínače příkazového řádku. Pro automatizaci běžící aplikace Office musíte použít GetObject nebo GetActiveObject.
Kroky pro reprodukci chování
-
Spusťte Microsoft Visual Basic a vytvořte nový standardní projekt EXE. Ve výchozím nastavení se vytvoří formulář Form1.
-
Přidejte ovládací prvek CommandButton do Form1.
-
Do modulu Code Form přidejte následující kód.
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 = NothingEnd Sub
-
Ujistěte se, že je v ukázce kódu správné umístění souboru Excel. exe.
-
Ukončete Microsoft Excel, pokud už běží.
-
Stisknutím klávesy F5 spusťte projekt a klikněte na Command1.
Alternativní řešení
Problém vyřešíte takto:
-
Přidělte aplikaci Office tak, že změníte druhý argument funkce Shell na vbMinimizedFocus, vbMaximizedFocusnebo vbNormalFocus.
-
Přidělte vybranému jazyku Visual Basic.
-
Pokus o použití metody GetObject při účtování doby načtení aplikace Office.
Tento alternativní postup ilustruje následující revidovaný kód.
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 IfEnd Sub
Alternativní řešení pro C++
Pokud programujete v jazyce C++, následující ukázka kódu ukazuje podobné řešení, které je uvedené v ukázkové ukázce jazyka Visual Basic. Povšimněte si, že SetForegroundWindow se používá k přesunutí fokusu mimo aplikaci Excel a umožňuje mu registrovat jeho běžící objekty.
//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();
Odkazy
Další informace naleznete v následujících článcích znalostní báze Microsoft Knowledge Base:
192919 Jak automatizovat zabezpečenou databázi Accessu pomocí jazyka Visual Basic
237338 Chybová zpráva s odkazem WordMail: "Tato metoda nebo vlastnost není k dispozici"
240794 Jak zjistit cestu pro aplikaci Office