通常 Excel を起動して OLE オートメーションとして使用したい場合には、CreateOleObject を使用します。
ただ、この方法ですと個人マクロブック(PERSONAL.XLS)が使えなかったり、アドインが読み込まれていなかったりするなどいろいろ不都合が多いのも事実です。
今回はそんなときに使える 「Excel をふつうに起動させる」方法です。
この方法は、ExcelをCreateProcessで非OLEアプリと同じように起動させて、その後GetActiveOleObjectでOLEを取得します。
Excel を起動するのは、CreateExcelProcess 関数です。ここでは「Excel.Application」という識別子から CLSID を取得し、さらにその CLSID から起動情報を取得した結果を利用してプロセスを作成しています。 また、起動後すぐには OLE のインスタンスを捕まえることができないので、最大 0.5 秒を 10 回計 5 秒を待つ設定にしています。 途中で SetForegroundWindow しているのは XP 上での Excel のバグ対策です。
procedure TForm1.Button1Click(Sender: TObject); // Excelプロセス生成 function CreateExcelProcess(out hProcess: THandle): Bool; function GetExcelRegInfo: String; const KeyFormat = 'CLSID%sLocalServer32'; var Key: String; begin Key := Format(KeyFormat, [GUIDToString(ProgIDToClassID('Excel.Application'))]); Result := GetRegStringValue(Key, ''); end; var RegInfo: String; PROCESSINFO: TProcessInformation; STARTUPINFO: TStartupInfo; begin Result := false; RegInfo := GetExcelRegInfo; // Excel が導入されていない場合は、RegInfoが空文字列 if (RegInfo = '') then exit; // 起動情報構造体 with STARTUPINFO do begin cb := SizeOf(STARTUPINFO); // 構造体のサイズをセット lpReserved := nil; // 上記以外は初期化 lpDesktop := nil; lpTitle := nil; dwFlags := 0; cbReserved2 := 0; lpReserved2 := nil; dwysize := 0; end; // '/e' を付加すると初期のワークブックが抑制できる Result := CreateProcess(nil, PChar(RegInfo + ' /e'), nil, nil, false, 0, nil, nil, STARTUPINFO, PROCESSINFO); hProcess := PROCESSINFO.hProcess; end; const RetryCount = 10; SErrorRunExcel = 'Excelの起動に失敗しました。'; var Excel: OleVariant; hProcess: THandle; // Excel Process Handle i: Integer; begin CreateExcelProcess(hProcess); try Excel := GetActiveOleObject('Excel.Application'); except on E: EOleSysError do begin // Excelがまだ起動していない場合には、起動をかける if (E.ErrorCode = $FFFFFFFF800401E3) then if not CreateExcelProcess(hProcess) then raise Exception.Create(SErrorRunExcel); // Excel再取得 for i := 0 to RetryCount do begin sleep(500); SetForegroundWindow(Handle); try Excel := GetActiveOleObject('Excel.Application') except end; if not VarIsEmpty(Excel) then break; // ループ最終回で取得できていなかった場合、 if (RetryCount = i) then raise Exception.Create(SErrorRunExcel); end; end; on E: Exception do exit; end; // Excel起動ループ Excel := unassigned; end;