Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2005年02月01日] [更新 2020年09月07日] Delphi サンプルプログラム集
   
410_TExcelApplication によるエクセル操作
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro + Excel 2010
410_Excel_TExcelApp.zip [1,925 KB] 2020年09月07日版(EXE同梱)



  • 2008年02月17日
  • 起動したエクセル側でエクセルを終了した時に,残るエクセルのプロセスを強制的に終了させるコードを追加
  • 2010年06月13日
  • CreateComObject 関係の記事追加と動作確認環境の変更,コード修正
  • 2014年01月11日
  • 表示するモニタをプライマリモニタにした
  • Open メソッドを OleVarinat 型で扱うようにした
  • 2014年06月19日
  • 開く時と閉じる時のメソッドを廃止し,各ボタンイベント内にコードを移動
  • 終了時のコードを修正




01_TExcelApplication によるエクセル操作


コンポーネントパレットの [Servers] タブにある TExcelApplication コンポーネント類を使用したエクセル操作のサンプルです.
拙作のエクセル操作フォームEx は,この TExcelApplication を使用しています.その動作の基本と言えるのがここで紹介するサンプルです.TExcelApplication ではコード補完機能が使用できます.
CreateOLEObject でエクセルのオブジェクトを生成した場合は,VBA のコードがほぼそのまま使用できますが,TExcelApplication では,引数を省略できないとか,少し修正が必要となります.

このサンプルのプロジェクトソースは下のリストのようになっています.フォームが最前面に表示されない場合は,MainFormOnTaskbar の値を False にしてみてください.筆者の Windows XP 環境でコンパイルした場合,True のままだと fsStayOnTop が無効になってしまいました.

単にエクセルというアプリケーションを起動するだけであれば,本ページのようなコードは必要ありません.[485_アプリの起動とファイルを開く関数] の記事あるいは,その記事中のリンクを参考にしてください.



[備考 1]
TExcelApplication コンポーネント類も,実際にはエクセルという COM オブジェクトの操作を行うものです.TExcelApplication 関係のコンポーネントを使用すると,そのことを意識しないで,エクセルを操作することができます.


[備考 2]
ネット上に,タイプライブラリをインポート (タイプライブラリの取り込み) して作成したユニットを使用するサンプルがありますが,Delphi の professional エディション以上であれば,その操作は必要ありません.Excel2000, ExcelXP, Excel2010 等のユニットには,タイプライブラリの取り込みで作成したユニットと同じ Excel 操作用のコードが含まれています.どのようなユニットがあるかは以下の記事で確認してください.
それらのユニットが実装されていない Delphi ではタイプライブラリの取り込みでユニットファイルを作成する必要があります.Delphi 6 等の Personal エディションには,それらのユニットが実装されていません.


リスト1
プロジェクトのソースコード
program Project1;

uses
  Forms,
  Unit1 in 'Uinit1.pas' {Form1};

{$R *.RES}

begin
  Application.Initialize;
  //Window XPの場合TrueにするとfsStayOnTopが無効になってしまう
  //Windows 7でコンパイルした場合はTrueでOK
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.


図1
設計時画面
  • [Servers] パレットの以下のコンポーネントを配置
    TExcelApplication
    TExcelWorkBook
    TExcelWorkSheet
  • ブックを開くための TOpenDialog
  • ListBox (下の白い部分) はシート名一覧表示用
図2
実行開始画面
  • このフォームは画面右上隅.エクセルはその左側に表示
図3
本フォームは最前面
  • エクセルにフォーカス移動しても最前面
  • すぐに次のボタンクリックが可能

リスト2
TExcelApplication によるエクセル操作の例
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Excel2000, OleServer;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    Button6: TButton;
    Button7: TButton;
    Button8: TButton;
    Button9: TButton;
    Button10: TButton;
    ExcelApp1: TExcelApplication;
    Worksheet1: TExcelWorksheet;
    Workbook1: TExcelWorkbook;
    OpenDialog1: TOpenDialog;
    ListBox1: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
    procedure Button7Click(Sender: TObject);
    procedure Button8Click(Sender: TObject);
    procedure Button9Click(Sender: TObject);
    procedure Button10Click(Sender: TObject);
    procedure Workbook1BeforeClose(ASender: TObject; var Cancel: WordBool);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

const
  LCID: DWORD = LOCALE_SYSTEM_DEFAULT;

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//  エクセルに隠れないようにこのフォームを常に最前面の右上に表示するようにする
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  FormStyle := fsStayOnTop;
  Self.Left := Screen.PrimaryMonitor.Width - Self.Width;
  Self.Top  := Screen.PrimaryMonitor.WorkareaRect.Top;
end;

//=============================================================================
//  フォームを閉じる時はエクセルの終了処理も実行
//  もし複数のワークブック,ワークシートtオブジェクトを生成している場合は
//  それらの Close や Disconnect 処理も必要
//  エクセルを閉じないでこのアプリを終了する場合でも Disconnect の実行は必要
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Worksheet1.Disconnect;
  Workbook1.Disconnect;
  ExcelApp1.Disconnect;
end;

//=============================================================================
//  エクセルを新規起動する
//  起動したら最初のシートをアクティブにする
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LxlsHandle : HWND;
  LSheetName : OleVariant;
  LIndex     : Integer;
begin
  // 表示更新停止
  ExcelApp1.ScreenUpdating[LCID] := False;
  if ExcelApp1.Visible[LCID] then begin
    // 現在のシートとブックの Disconnect 処理
    // 現在起動しているブックは閉じる
    Worksheet1.Disconnect;
    Workbook1.Close(False, EmptyParam, False, LCID);
    Workbook1.Disconnect;
  end;

  // 接続の種類はデフォルトのままでよい
  ExcelApp1.ConnectKind := ckRunningOrNew;

  // ブックオブジェクトを追加
  ExcelApp1.Workbooks.Add(EmptyParam, LCID);
  Workbook1.ConnectTo(ExcelApp1.ActiveWorkbook as _workbook);
  // 最初のシートオブジェクトを取得してアクティブにする
  // シート番号ではなくシート名 (文字列) でも指定可能
  Worksheet1.ConnectTo(Workbook1.Worksheets[1] as _WorkSheet);
  Worksheet1.Activate;

  // 終了時に保存確認ダイアログを表示しない
  ExcelApp1.DisplayAlerts[LCID] := False;

  // このフォームから操作しているので,選択後はエクセルをアクティブにする
  LxlsHandle := FindWindow('XLMAIN', nil);
  SetWindowPos(LxlsHandle,
               HWND_TOP,
               0,
               0,
               0,
               0,
               SWP_NOSIZE or SWP_NOMOVE or SWP_SHOWWINDOW);


  // 表示更新開始
  ExcelApp1.ScreenUpdating[LCID] := True;
  ExcelApp1.Visible[LCID]        := True;

  // リストボックスにシート名一覧を作成
  ListBox1.Clear;
  for LIndex := 1 to Workbook1.Sheets.Count do begin
    LSheetName := Workbook1.Sheets[LIndex];
    ListBox1.Items.Add(LSheetName.Name);
  end;

  // 取敢えず左上のセルを選択しておく (検索テストの都合もあり)
  Worksheet1.Range['A1', 'A1'].Select;
end;

//=============================================================================
//  ブックを開く
//  ファイルオープンダイアログを使用して選択
//  開いたら最初のシートをアクティブにする
//
//  Excel2007 以上がインストールされていない環境では xlsx のブックは開けない
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LBookName    : string;
  LExcelVer    : string;
  LobjExcelApp : OleVariant;
  LxlsHandle   : HWND;
  LSheetName   : OleVariant;
  LIndex       : Integer;
begin
  with OpenDialog1 do begin
    //初期フォルダ
    InitialDir := ExtractFileDir(ExtractFileDir(Application.ExeName));

    LExcelVer := ExcelApp1.Version[LCID];
    if StrToFloatDef(LExcelVer, 0.0) > 11.9 then begin
      Filter := 'エクセルブック (*.xls;*.xlsx)|*.xls;*.xlsx';
    end else begin
      Filter := 'エクセルブック (*.xls)|*.xls';
    end;
  end;

  if OpenDialog1.Execute then begin
    LBookName := OpenDialog1.FileName;

    // 表示更新停止
    ExcelApp1.ScreenUpdating[LCID] := False;
    if ExcelApp1.Visible[LCID] then begin
      // 現在のシートとブックの Disconnect 処理
      // 現在起動しているブックは閉じる
      Worksheet1.Disconnect;
      Workbook1.Close(False, EmptyParam, False, LCID);
      Workbook1.Disconnect;
    end;

    // 接続の種類はデフォルトとする
    ExcelApp1.ConnectKind := ckRunningOrNew;
    // ブックを開いてワークブック.ワークシートに接続
    // Open メソッドの引数は多いので,省略するために OleVariant 型で扱ってしまう
    LobjExcelApp := ExcelApp1.Application;
    LobjExcelApp.Workbooks.Open(FileName := LBookName);

    // 最初のシートオブジェクトを取得してアクティブにする
    // シート番号ではなくシート名 (文字列) でも指定可能
    Workbook1.ConnectTo(ExcelApp1.ActiveWorkbook as _workbook);
    Worksheet1.ConnectTo(Workbook1.Worksheets[1] as _WorkSheet);
    Worksheet1.Activate;

    // 終了時に保存確認ダイアログを表示しない
    ExcelApp1.DisplayAlerts[LCID] := False;

    // このフォームから操作しているので,選択後はエクセルをアクティブにする
    LxlsHandle := FindWindow('XLMAIN', nil);
    SetWindowPos(LxlsHandle,
                 HWND_TOP,
                 0,
                 0,
                 0,
                 0,
                 SWP_NOSIZE or SWP_NOMOVE or SWP_SHOWWINDOW);


    // 表示更新開始
    ExcelApp1.ScreenUpdating[LCID] := True;
    ExcelApp1.Visible[LCID]        := True;

    // リストボックスにシート名一覧を作成
    ListBox1.Clear;
    for LIndex := 1 to Workbook1.Sheets.Count do begin
      LSheetName := Workbook1.Sheets[LIndex];
      ListBox1.Items.Add(LSheetName.Name);
    end;

    // 取敢えず左上のセルを選択しておく(検索テストの都合もあり)
    Worksheet1.Range['A1', 'A1'].Select;
  end;
end;

//=============================================================================
//  エクセルを開いたまま終了
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
  Close;
end;

//=============================================================================
//  エクセルも一緒に閉じて終了
//
//  もし複数のワークブック,ワークシートオブジェクトを生成している場合は
//  それらの処理も必要
//=============================================================================
procedure TForm1.Button4Click(Sender: TObject);
var
  LBookNo : Integer;
begin
  if Assigned(ExcelApp1) then begin

    for LBookNo := ExcelApp1.Workbooks.Count downto 1 do begin
      ExcelApp1.Workbooks[LBookNo].Close(False, EmptyParam, False, LCID);
      Application.ProcessMessages;
    end;

    if Assigned(Workbook1) then begin
      Workbook1.Disconnect;
    end;

    if Assigned(Worksheet1) then begin
      Worksheet1.Disconnect;
    end;

    ExcelApp1.Disconnect;
    ExcelApp1.Quit;
  end;

  Close;
end;

//=============================================================================
//  クリックしたシートをワークシートオブジェクトにしてアクティブに
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
var
  LSheetName : string;
begin
  LSheetName := ListBox1.Items.Strings[ListBox1.ItemIndex];

  // ワークシートオブジェクトはシート名が LSheetName のシートとなる
  Worksheet1.ConnectTo(Workbook1.Worksheets[LSheetName] as _WorkSheet);
  Worksheet1.Activate;

  SetForegroundWindow(FindWindow('XLMAIN', nil));
end;

//=============================================================================
//  行の選択
//  Range オブジェクトを取得してから選択
//  Range オブジェクトは,この行に対していろいろな操作をする場合に使用する
//  各種操作は Range オブジェクトに対しても可能なので選択しなくても可能
//
//  選択するだけでよいなら以下のコードでも可.
//  Worksheet1.Range['C5','C5'].EntireRow.Select;
//=============================================================================
procedure TForm1.Button5Click(Sender: TObject);
var
  LSelectRange : ExcelRange;
begin
  if not ExcelApp1.Visible[LCID] then exit;

  LSelectRange := Worksheet1.Range['C5', 'C5'].EntireRow;
  LSelectRange.Select;

  SetForegroundWindow(FindWindow('XLMAIN', nil));
end;

//=============================================================================
//  最終行を選択状態に
//  EntireRow の代わりに Row とすれば行番号が取得できる
//=============================================================================
procedure TForm1.Button6Click(Sender: TObject);
var
  LRange : ExcelRange;
begin
  if not ExcelApp1.Visible[LCID] then exit;

  LRange := Worksheet1.Cells.SpecialCells(xlLastCell, EmptyParam).EntireRow;
  LRange.Select;

  SetForegroundWindow(FindWindow('XLMAIN', nil));
end;

//=============================================================================
//  セルの書式設定
//  ここでは単にセルの文字列の色を clRed にするだけ
//=============================================================================
procedure TForm1.Button7Click(Sender: TObject);
var
  LSelectRange: ExcelRange;
begin
  if not ExcelApp1.Visible[LCID] then exit;

  LSelectRange := Worksheet1.Range['A1', 'F15'];
  LSelectRange.Font.Color := clRed;

  SetForegroundWindow(FindWindow('XLMAIN', nil));
end;

//=============================================================================
//  セルに関数を定義
//=============================================================================
procedure TForm1.Button8Click(Sender: TObject);
begin
  if not ExcelApp1.Visible[LCID] then exit;

  Worksheet1.Range['A8', 'F8'].Formula := '=SUM(A1:A7)';
end;

//=============================================================================
//  アクティブシートのセル値の部分一致検索
//  検索範囲は [A1:F100].
//  ボタンをクリックする度に次の検索を行う (巡回方式で)
//
//  以下の 2 行
//  LRange := Worksheet1.Range[Range1, Range2].Find(...
//  を次の様に変更するとシート全体の検索となる.
//  LRange := Worksheet1.Cells.Find(...
//=============================================================================
procedure TForm1.Button9Click(Sender: TObject);
var
  LRange        : ExcelRange;
  LSearchValue  : OleVariant;
  LRange1       : OleVariant;
  LRange2       : OleVariant;
  LpAfter       : OleVariant;
  LpSearchOrder : XlSearchOrder;
  LpDirection   : XlSearchDirection;
begin
  if not ExcelApp1.Visible[LCID] then exit;

  // シート内のセルをクリアしてテスト用の文字列をセット
  Worksheet1.Cells.Clear;
  Worksheet1.Range['A4',  'A4'].Value   := 'ABCD';
  Worksheet1.Range['C8',  'C8'].Value   := 'ABCF';
  Worksheet1.Range['B15', 'B15'].Value  := 'FABC';

  LRange1      := 'A1';
  LRange2      := 'F100';
  LSearchValue := 'ABC';

  // 現在アクティブなセルの次から検索
  LpAfter       :=  ExcelApp1.ActiveCell;
  LpSearchOrder := xlByRows;
  LpDirection   := xlNext;
  LRange := Worksheet1.Range[LRange1, LRange2].Find(
                             LSearchValue,  // 検索データ
                             LpAfter,       // 検索開始セル
                             EmptyParam,   // 検索データ種類
                             EmptyParam,   // 部分か全一致
                             LpSearchOrder, // 行列の方向
                             LpDirection,   // 検索方向
                             EmptyParam,   // 大文字小文字の区別
                             EmptyParam ); // 半角全角の区別

  // 見つかったら該当セルをアクティブに
  if not VarIsEmpty(LRange) then begin
    LRange.Select;

    SetForegroundWindow(FindWindow('XLMAIN', nil));
  end;
end;

//=============================================================================
//  シートの追加
//=============================================================================
procedure TForm1.Button10Click(Sender: TObject);
var
  LpAfter : OleVariant;
  LpCount : OleVariant;
begin
  if not ExcelApp1.Visible[LCID] then exit;

  // Sheet2 のシートオブジェクトを取得
  // Sheet2 の後ろに追加するので After 変数を使用
  // Sheet2 というシートが存在しない場合はエラー
  LpAfter := Workbook1.Worksheets['Sheet2'];
  LpCount := 1;

  Workbook1.Worksheets.Add(EmptyParam, LpAfter, LpCount, xlWorksheet, 0);

  // 追加したシートをワークシートオブジェクトにする
  Worksheet1.ConnectTo(Workbook1.ActiveSheet as _WorkSheet);
end;

//=============================================================================
//  ブックを閉じる時のイベント
//=============================================================================
procedure TForm1.Workbook1BeforeClose(ASender: TObject;
  var Cancel: WordBool);
begin
//
end;

end.




02_TExcelApplication を使用して起動中のエクセルに接続


既に起動中,あるいは表示中のエクセルに接続するサンプルです.複数のブックが起動している場合は,Z オーダーがトップのブックに接続するようです.
Windows 7 では,このサンプルを管理者権限で実行すると,WorkSheet に接続するところで例外が発生します これは,エクセルのオブジェクトが取得できていないために発生します.



図4
設計時画面

リスト3
起動中のエクセルに接続.Windows XP 以前用
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Excel2000, OleServer, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    ExcelApp1: TExcelApplication;
    Workbook1: TExcelWorkbook;
    Worksheet1: TExcelWorksheet;
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

const
  LCID: DWORD = LOCALE_SYSTEM_DEFAULT;

{$R *.DFM}

//=============================================================================
//  フォームを閉じる時はエクセルの終了処理も実行
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  LBookNo : Integer;
begin
  if Assigned(ExcelApp1) then begin

    for LBookNo := ExcelApp1.Workbooks.Count downto 1 do begin
      ExcelApp1.Workbooks[LBookNo].Close(False, EmptyParam, False, LCID);
      Application.ProcessMessages;
    end;

    if Assigned(Workbook1) then begin
      Workbook1.Disconnect;
    end;
    if Assigned(Worksheet1) then begin
      Worksheet1.Disconnect;
    end;
    ExcelApp1.Disconnect;
    ExcelApp1.Quit;
  end;
end;

//=============================================================================
//  起動中のエクセルに接続
//  Windows 7 ではこのサンプルを管理者権限で起動しているとエクセルに接続不可
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LxlsHandle : HWND;
begin
  // エクセルが起動中かをハンドルの有無で検査
  LxlsHandle := FindWindow('XLMAIN',nil);
  if LxlsHandle =0 then begin
    ShowMessage('エクセルは起動していません');
  end else begin
    ExcelApp1.Connect;

    Workbook1.ConnectTo(ExcelApp1.ActiveWorkbook as _workbook);

    Worksheet1.ConnectTo(Workbook1.Worksheets[1] as _WorkSheet);
    Worksheet1.Activate;

    // 終了時に保存確認ダイアログを表示しない
    ExcelApp1.DisplayAlerts[LCID] := False;

    // 取敢えず左上のセルを選択しておく
    if Worksheet1.Range['A2','A2'].Value = 'ABC' then begin
    Worksheet1.Range['A2','A2'].Value := '日本語 ABC 文字列';
    end else begin
      Worksheet1.Range['A2','A2'].Value := 'ABC';
    end;
  end;
end;

end.




03_CreateComObject でエクセルのオブジェクトを生成


CreateComObject でエクセルのオブジェクトを生成してエクセルを操作する例です.エクセルのオブジェクトは実行時に生成しています.TExcelApplication と違い ConnectTo はありません.こちらの方がある意味では使いやすいかも知れません.コード補完機能も使用できます.
テスト内容は前のサンプルと全く同じです.fsStayOnTop プロパティの挙動については上のサンプルの説明を参考にしてください.

CreateOLEObject または CreateComObject で生成して操作可能な対象オブジェクトのインスタンスは 1 つだけに限られます.
一方,TExcelApplication のような Delphi のクラスファクトリ (コンポーネント) では,Delphi のコンポーネント同様,複数のインスタンスを生成して,個別に操作することができます.


図5
設計時画面
  • ブックを開くための TOpenDialog を配置
  • ListBox (下の白い部分) はシート名一覧表示用

リスト4
CreateComObject でエクセルのオブジェクトを生成
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Excel2000;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    Button6: TButton;
    Button7: TButton;
    Button8: TButton;
    OpenDialog1: TOpenDialog;
    ListBox1: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
    procedure Button7Click(Sender: TObject);
    procedure Button8Click(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
  private
    FExcelApp  : Excel2000.ExcelApplication;
    FWorkBook  : Excel2000.ExcelWorkbook;
    FWorkSheet : Excel2000.ExcelWorksheet;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses ComObj;

const
  LCID: DWORD = LOCALE_SYSTEM_DEFAULT;

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//  エクセルに隠れないようにこのフォームを常に最前面の右上に表示するようにする
//  表示するのはプライマリモニタ
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  FormStyle := fsStayOnTop;
  Self.Left := Screen.PrimaryMonitor.Width - Self.Width;
  Self.Top  := Screen.PrimaryMonitor.WorkareaRect.Top;

  // Createするのはエクセルアプリケーションのオブジェクトだけ
  FExcelApp := CreateComObject(CLASS_ExcelApplication) as ExcelApplication;
end;

//=============================================================================
//  フォームを閉じる時はエクセルの終了処理も実行
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  LBookNo : Integer;
begin
  if Assigned(FExcelApp) then begin
    for LBookNo := FExcelApp.Workbooks.Count downto 1 do begin
      FExcelApp.Workbooks[LBookNo].Close(False, EmptyParam, False, LCID);
      Application.ProcessMessages;
    end;
    if Assigned(FWorkBook) then begin
      FWorkBook := nil;
    end;
    if Assigned(FWorkSheet) then begin
      FWorkSheet := nil;
    end;
    FExcelApp.Quit;
    FExcelApp := nil;
  end;
end;

//=============================================================================
//  ブックを開く
//  ファイルオープンダイアログを使用して選択
//  開いたら最初のシートをアクティブにする
//
//  Excel2007 以降がインストールされていない環境では xlsx 形式のブックは開けない
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LBookName    : string;
  LExcelVer    : string;
  LobjExcelApp : OleVariant;
  LobjSheet    : OleVariant;
  LxlsHandle   : HWND;
  LIndex       : Integer;
begin
  with OpenDialog1 do begin
    // 初期フォルダ
    InitialDir := ExtractFileDir(ExtractFileDir(Application.ExeName));

    LExcelVer := FExcelApp.Version[LCID];
    if StrToFloatDef(LExcelVer, 0.0) > 11.9 then begin
      Filter := 'エクセルブック (*.xls;*.xlsx)|*.xls;*.xlsx';
    end else begin
      Filter := 'エクセルブック (*.xls)|*.xls';
    end;
  end;

  if OpenDialog1.Execute then begin
    LBookName := OpenDialog1.FileName;

    // 表示更新停止
    FExcelApp.ScreenUpdating[LCID] := False;

    // 現在表示しているブックは閉じる
    if FExcelApp.Visible[LCID] then begin
      FWorkBook.Close(False, EmptyParam, False, LCID);
    end;

    // ブックを開いてワークブックオブジェクトを取得
    // Open メソッドの引数は多いので,省略するために OleVariant 型を使用してしまう
    LobjExcelApp := FExcelApp.Application;
    LobjExcelApp.Workbooks.Open(FileName := LBookName);
    FWorkBook := FExcelApp.ActiveWorkbook as ExcelWorkbook;

    // 1 番目のシートのオブジェクトを取得してアクティブにする
    // 番号でなくてシート名でも指定可能
    FWorkSheet := FWorkBook.Worksheets[1] as ExcelWorksheet;
    FWorkSheet.Activate(LCID);

    // 終了時に保存確認ダイアログを表示しない
    FExcelApp.DisplayAlerts[LCID] := False;

      // このフォームから操作しているので,選択後はエクセルをアクティブにする
      LxlsHandle := FindWindow('XLMAIN', nil);
      SetWindowPos(LxlsHandle,
                   HWND_TOP,
                   0,
                   0,
                   0,
                   0,
                   SWP_NOSIZE or SWP_NOMOVE or SWP_SHOWWINDOW);

    // 表示更新開始
    FExcelApp.ScreenUpdating[LCID] := True;
    FExcelApp.Visible[LCID]        := True;

    // リストボックスにシート名一覧を作成
    ListBox1.Clear;
    for LIndex := 1 to FWorkBook.Sheets.Count do begin
      LobjSheet := FWorkBook.Sheets[LIndex];
      ListBox1.Items.Add(LobjSheet.Name);
    end;

    // 取敢えず左上のセルを選択しておく(検索テストの都合もあり)
    FWorkSheet.Range['A1', 'A1'].Select;
  end;
end;

//=============================================================================
//  エクセルを閉じて終了
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  Close;
end;

//=============================================================================
//  クリックしたシートをワークシートオブジェクトにしてアクティブに
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
var
  LSheetName : string;
begin
  LSheetName := ListBox1.Items.Strings[ListBox1.ItemIndex];

  // ワークシートオブジェクトはシート名が FShName のシートとなる
  FWorkSheet := FWorkBook.Worksheets[LSheetName] as ExcelWorksheet;
  FWorkSheet.Activate(LCID);

  // このフォームから操作しているので,選択後はエクセルをアクティブにする
  SetForegroundWindow(FindWindow('XLMAIN', nil));
end;

//=============================================================================
//  行の選択
//  Range オブジェクトを取得してから選択
//  Range オブジェクトは,この行に対していろいろな操作をする場合に使用する
//  各種操作は Range オブジェクトに対しても可能なので選択しなくても可能
//
//  選択するだけでよいなら以下のコードでも可.
//  FWorkSheet.Range['C5','C5'].EntireRow.Select;
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LSelectRange: ExcelRange;
begin
  if not FExcelApp.Visible[LCID] then exit;

  LSelectRange := FWorkSheet.Range['C5', 'C5'].EntireRow;
  LSelectRange.Select;

  SetForegroundWindow(FindWindow('XLMAIN', nil));
end;

//=============================================================================
//  最終行 (データがある) を選択状態に
//  セルをクリアしても書式等が設定されていると空白行が最終行になることもある
//
//  EntireRow のかわりに Row とすれば行番号が取得できる
//=============================================================================
procedure TForm1.Button4Click(Sender: TObject);
var
  LRange : ExcelRange;
begin
  if not FExcelApp.Visible[LCID] then exit;

  LRange := FWorkSheet.Cells.SpecialCells(xlLastCell, EmptyParam).EntireRow;
  LRange.Select;

  SetForegroundWindow(FindWindow('XLMAIN', nil));
end;

//=============================================================================
//  セルの書式設定
//  ここでは単にセルの文字列の色を clRed にするだけ
//=============================================================================
procedure TForm1.Button5Click(Sender: TObject);
var
  LSelectRange : ExcelRange;
begin
  if not FExcelApp.Visible[LCID] then exit;

  LSelectRange := FWorkSheet.Range['A1', 'F15'];
  LSelectRange.Font.Color := clRed;

  SetForegroundWindow(FindWindow('XLMAIN', nil));
end;

//=============================================================================
//  セルに関数を定義
//=============================================================================
procedure TForm1.Button6Click(Sender: TObject);
begin
  if not FExcelApp.Visible[LCID] then exit;

  FWorkSheet.Range['A8','F8'].Formula := '=SUM(A1:A7)';
end;

//=============================================================================
//  アクティブシートのセル値の部分一致検索
//  検索範囲は [A1:F100].
//  ボタンをクリックする度に次の検索を行う (巡回方式で)
//
//  以下の行
//  LRange := FWorkSheet.Range[Range1, Range2].Find(...
//  を次の様に変更するとシート全体の検索となる.
//  LRange := FWorkSheet.Cells.Find(...
//=============================================================================
procedure TForm1.Button7Click(Sender: TObject);
var
  LRange        : ExcelRange;
  LSearchValue  : OleVariant;
  LRange1       : OleVariant;
  LRange2       : OleVariant;
  LpAfter       : OleVariant;
  LpSearchOrder : XlSearchOrder;
  LpDirection   : XlSearchDirection;
begin
  if not FExcelApp.Visible[LCID] then exit;

  // シート内のセルをクリアしてテスト用の文字列をセット
  FWorkSheet.Cells.Clear;
  FWorkSheet.Range['A4',  'A4'].Value  := 'ABCD';
  FWorkSheet.Range['C8',  'C8'].Value  := 'ABCF';
  FWorkSheet.Range['B15', 'B15'].Value := 'FABC';

  LRange1      := 'A1';
  LRange2      := 'F100';
  LSearchValue := 'ABC';

  // 現在アクティブなセルの次から検索
  LpAfter       := FExcelApp.ActiveCell;
  LpSearchOrder := xlByRows;
  LpDirection   := xlNext;

  LRange := FWorkSheet.Range[LRange1, LRange2].Find(
                             LSearchValue,  // 検索データ
                             LpAfter,       // 検索開始セル
                             EmptyParam,   // 検索データ種類
                             EmptyParam,   // 部分か全一致
                             LpSearchOrder, // 行列の方向
                             LpDirection,   // 検索方向
                             EmptyParam,   // 大文字小文字の区別

                             EmptyParam ); // 半角全角の区別

  // 見つかったら該当セルをアクティブに
  if not VarIsEmpty(LRange) then begin
    LRange.Select;

    SetForegroundWindow(FindWindow('XLMAIN', nil));
  end;
end;

//=============================================================================
//  シートの追加
//=============================================================================
procedure TForm1.Button8Click(Sender: TObject);
var
  LpAfter : OleVariant;
  LpCount : OleVariant;
begin
  if not FExcelApp.Visible[LCID] then exit;

  // Sheet2 のシートオブジェクトを取得
  // Sheet2 の後ろに追加するので After 変数を使用
  // Sheet2 というシートが存在しない場合はエラー
  LpAfter := FWorkBook.Worksheets['Sheet2'];
  LpCount := 1;

  FWorkBook.Worksheets.Add(EmptyParam, LpAfter, LpCount, xlWorksheet, 0);

  // 追加したシートをワークシートオブジェクトにする
  // しないとアクティブになっているだけで操作対象にはならない
  // FWorkSheet := FWorkBook.WorkSheets.Add(...) as .. ExcelWorksheet;でもよい
  FWorkSheet := FWorkBook.ActiveSheet as ExcelWorksheet;
end;

end.




04_CoExcelApplication でエクセルのオブジェクト生成


CreateComObject の代わりに,CoExcelApplication.create でもエクセルのオブジェクトを生成して利用できます.


リスト5
CoExcelApplication を使用
  // エクセルのオブジェクトを生成
  FExcelApp := CoExcelApplication.Create;


CoExcelApplication.Create の実現部は Excel2000.pas で次のようになっていて,CreateComObject でエクセルのオブジェクトを生成しています.

class function CoExcelApplication.Create: _Application;
begin
  Result := CreateComObject(CLASS_ExcelApplication) as _Application;
end;




05_大量データの読込み


エクセルのセルに大量のデータをセットする場合,各々のセルに個別に値をセットすると非常に時間がかかります.クリップボード経由や,エクセル操作フォームExの [データベースから読込み] や [CSV データの読込み],[19_エクセルから読み込みとエクセルへの出力] で説明しているバリアント配列を用いると,大幅に処理時間を短縮することが可能です.

ディスク上のエクセルへの読み書は,M & Iさんの XBiff (Excel ファイル出力/入力) を使用すると更に高速処理が可能です.また,アドバンスソフトウェアの ExcelCreator を使用しても,劇的に処理速度が速くなりますが,使用にはエクセル・クリエータの DLL が必要です.





06_[ ツールパレット ] 等に Servers がない場合


Delphi のバージョンによっては,初期状態では,TWordApplication や TExcelApplication 等のコンポーネントが使用できないことがあります.
この場合,下図のような [コンポーネントパレット] あるいは [ツールパレット] に [Servers] タブが存在しません.

  • 図6
  • コンポーネントパレットの [Servers] タブ

図7
ツールパレットの [Servers]
  • この図は Delphi XE4 Pro のもの

このような場合は,[コンポーネント] [パッケージのインストール] で下図のダイアログを表示して, [Microsoft Office XXX オートメーション...] というパッケージをチェックします.もし,該当するパッケージ名がリストにない場合は,[追加] ボタンをクリックして,[bin] フォルダにあるコンパイル済みのパッケージをインストールします.
これらのパッケージは複数ある場合があります.使用可能なのは 1 つだけです.使用しないパッケージは削除します.
Delphi XE2 以降には,Office 2010 用のファイルも用意してありますが,パッケージとしては用意されていません.Delphi XE4 には,Offfice 2010 用のパッケージが用意されています.パッケージのインストールを実行すれば使用可能となります.


図8
インストール済みパッケージ
  • [コンポーネント] [パッケージのインストール] で表示されるダイアログ
  • 使用可能なパッケージは 1 つだけ
  • 使用しないパッケージはリストから削除する

上記の操作をした後,Delphi の IDE を起動した時に,
      次にパッケージを読み込む時にもこのパッケージを読み込みますかと
いう内容のダイアログが表示された場合は,[いいえ] のボタンをクリックして起動します.そして,再度パッケージの登録をします.