現象
GetObject (Microsoft Visual Basic) または GetActiveObject (Microsoft Visual C++) を使用して Microsoft Office アプリケーションのオートメーションを実行すると、Office アプリケーションが実行中でも、以下のいずれかのエラー メッセージが表示されます。
実行時エラー '429':
ActiveX コンポーネントはオブジェクトを作成できません。
または
エラー : 0x800401e3 "操作を利用できません"
原因
Office アプリケーションが実行されていても、それが実行中のオブジェクト テーブル (ROT) に登録されていないことがあります。GetObject (Visual Basic) または GetActiveObject (Visual C++) を使用してアタッチする前に、Office アプリケーションを実行中のインスタンスが ROT に登録されている必要があります。
Office アプリケーションは、起動直後には実行中のオブジェクトを登録しません。これは、アプリケーションの起動処理を最適化するためです。Office アプリケーションを実行中のオブジェクトが ROT に登録されるのは、起動時ではなく、フォーカスが移動したときです。このため、Office アプリケーションからフォーカスが移動する前に GetObject または GetActiveObject を使用して Office アプリケーションの実行中のインスタンスにアタッチすると、上記のいずれかのエラー メッセージが表示されることがあります。
解決方法
コードを使用して、Office アプリケーションからユーザー独自のアプリケーション (またはその他のアプリケーション) にフォーカスを移動することで、Office アプリケーションが ROT に登録されるようにすることができます。また、使用するコードで Office アプリケーションの exe ファイルを起動する場合は、Office アプリケーションの読み込みが完了するまで待ってから、実行中のインスタンスにアタッチする必要があります。回避策として、この資料の「詳細」にコードのサンプルを掲載しています。
状況
この動作は仕様です。
詳細
ほとんどの場合、Office アプリケーションをオートメーションで実行するには CreateObject (Visual Basic) または CoCreateInstance (Visual C++) を使用して Office アプリケーションの新規インスタンスを起動する必要があります。
ただし、既に実行中の Office アプリケーションをオートメーションで実行することが必要な場合もあります。たとえば、ユーザーが Office アプリケーションを既に起動している場合です。また、コマンド ライン スイッチを指定できるように、コードを使用して Office アプリケーションの実行可能ファイルを起動している場合もあります。実行中の Office アプリケーションをオートメーションで実行するには、GetObject または GetActiveObject を使用する必要があります。
問題の再現手順
-
Microsoft Visual Basic を起動し、新しい標準 EXE プロジェクトを作成します。デフォルトで Form1 が作成されます。
-
[CommandButton] コントロールを Form1 に追加します。
-
次のコードをフォームのコード モジュールに追加します。
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 -
サンプル コードで指定されている Excel.exe の場所が正しいことを確認します。
-
Microsoft Excel を既に実行している場合は終了します。
-
F5 キーを押してプロジェクトを実行し、[Command1] をクリックします。
回避策
この問題を回避するには、次のいずれかの操作を行います。
-
Shell 関数の 2 番目の引数を vbMinimizedFocus、vbMaximizedFocus、vbNormalFocus のいずれかに変更して、Office アプリケーションにフォーカスを移動します。
-
Visual Basic フォームにフォーカスを移動します。
-
最後に、Office アプリケーションの読み込み時間を考慮して GetObject を実行します。
次の修正コードはこの回避策の具体例です。
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
C++ での回避策
C++ を使用してプログラミングしている場合は、次のサンプル コードが、上記の Visual Basic のサンプルと同様の回避策の具体例です。SetForegroundWindow() を使用して Excel からフォーカスを移動し、実行中のオブジェクトが登録されるようにしています。
//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();
関連情報
関連情報を参照するには、以下の「サポート技術情報」 (Microsoft Knowledge Base) をクリックしてください。
192919 Visual Basic を使用してセキュリティで保護された Access データベースを自動化する方法
237338 [WD2000] WordMail を使用すると "メソッドまたはプロパティが使用できません" というエラー メッセージが表示される
240794
Office アプリケーションのパスを調べる方法
関連情報
この資料は米国 Microsoft Corporation から提供されている Knowledge Base の Article ID 238610 (最終更新日 2004-07-16) を基に作成したものです。
この資料に含まれているサンプル コード/プログラムは英語版を前提に書かれたものをありのままに記述しており、日本語環境での動作は確認されておりません。