Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2005年01月30日] [更新 2018年10月26日] Delphi サンプルプログラム集
390_OLE コンテナにエクセルを表示
動作確認等 Windows 7 U64(SP1) + Delphi XE5(UP2) Pro VCL-32 + Excel 2010
390_Excel_OleContainer.zip [5,057 KB] 2018年10月26日版 (EXE 同梱)







TOleContainer ( OLE コンテナ )


TOleContainer コントロール (OLE コントロール) をフォームに配置すると,その TOleContainer に各種の OLE オブジェクト (COM オブジェクト) を表示して操作できます.
それらのオブジェクトをコードで操作するには,それらの COM オブジェクトのインスタンスを取得して,それらの COM オブジェクトのプロパティやメソッドを設定または実行します.

このページでは,その例として TOleContainer にエクセルのブックを実行時に読み込んで表示し,エクセルのブックを操作します.
TOleContainer には,下図のように設計時に COM オブジェクトを挿入できます.TOleContainer のコンテキストメニューから [オブジェクトの挿入] で表示されるダイアログから選択します.このダイアログは TOleContainer をダブルクリックしても表示することができます.



図1
設計時のオブジェクトの挿入
  • 使用可能なオブジェクトはシステムの環境によって異なる

上の図の [オブジェクトの種類] のオブジェクトに関連付けされたファイル以外は TOleContainer で表示することはできません.[ファイルから作成] ではどんな種類のファイルも選択できますが,[オブジェクトの種類] のオブジェクトに関連付けされていないファイルを選択すると,設計時も実行時にもアイコン表示になります.
[ファイルの種類] のオブジェクトのアプリと拡張子が関連付けされていても,TOleContainer で表示できるとは限りません.

そのようなファイルの場合,実行時のコンテキストメニューから [コンテンツのアクティブ化] を選択すると,そのファイルの拡張子に関連付けされたアプリで表示することができます.



図2
対象外のファイルの場合
  • アイコン表示となる




01_OLE コンテナにエクセルのブックを表示する


添付の OleText.xls というエクセルのブックを表示して TExcelApplication コンポーネントで操作する例です.

本サンプルでは,Microsoft Office 2000 オートメーションサーバーラッパーコンポーネントを使用しています.使用する Office つまりエクセルのラッパークラスのバージョンによってイベントの引数や型が異なることがあります.必要に応じてイベントを再設定してください.
これは,元の Microsof Office のバージョンによって COM の実装が異なるためです.


図3
設計時画面
  • OleContainerを配置
  • Align を alClient とした
  • 以下のコンポーネントを配置
    TExcelApplication
    TExcelWorkBook
    TExcelWorkSheet
  • フォームのスクロールバーは非表示
図4
実行画面
  • Excel 2003 までは OleContainer の仕様でメニューは非表示
  • Excel 2007 以降ではメニューはなくなったので,メニューに相当する [ファイル] タブが非表示となる
図5
シート切換えイベントの動作確認
  • ダイアログを表示する

リスト1
OLE コンテナにエクセルを表示して動作確認
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Excel2000, Vcl.OleServer,
  Vcl.OleCtnrs, Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    ExcelApplication1: TExcelApplication;
    ExcelWorkbook1: TExcelWorkbook;
    ExcelWorksheet1: TExcelWorksheet;
    OleContainer1: TOleContainer;
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormDestroy(Sender: TObject);
    procedure ExcelApplication1SheetChange(ASender: TObject;
      const Sh: IDispatch; const Target: ExcelRange);
    procedure ExcelApplication1SheetActivate(ASender: TObject;
      const Sh: IDispatch);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
    FCellChangeFlag : Boolean;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  // 既に起動中のエクセルと操作を分離するため
  // このプログラムで生成したオブジェクトを対象とする
  ExcelApplication1.ConnectKind := ckRunningInstance;
end;

//=============================================================================
//  フォーム表示開始時の処理
//
//  (1)表示するブックは起動フォルダの上のOleTest.xlsとする
//  (2)OleContainerのオブジェクトをファイルから読出してCreateする
//     CreateObjectFromFileの引数に目的のブックを指定する
//  (3)念のためOleContainer.Visible:=Tureとして表示(DoVervの引数をovShowに)
//  (4)表示したブックをTExcelApplicationオブジェクトと関連づける
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
var
  LFilePath : string;
begin
  //セルの編集状態のフラグ
  FCellChangeFlag := False;
  //開くブック名
  LFilePath := ExpandFileName('..\OleTest.xls');
  if not FileExists(LFilePath) then exit;

  //ExcelのファイルをOLEコンテナのオブジェクトとする
  OleContainer1.CreateObjectFromFile(LFilePath,False);

  //OLEオブジェクトを表示可能状態に
  OleContainer1.Visible := True;
  //OLEオブジェクトをアクティブにする
  OleContainer1.DoVerb(ovPrimary);
  OleContainer1.SetFocus;

  ExcelApplication1.Connect;
  //開いているブックをExcleWorkbook1に接続
  ExcelWorkbook1.ConnectTo(ExcelApplication1.ActiveWorkbook as _workbook);
  //シートを ExcelWorksheetに接続
  ExcelWorksheet1.ConnectTo(ExcelWorkbook1.ActiveSheet as _Worksheet);

  //'A1'セルを選択
  // この操作がない場合は値が入力済みの左上のセルが左上端に表示される
  ExcelWorkSheet1.Range['A1', 'A1'].Select;
end;

//=============================================================================
//  フォームを閉じる時の処理
//
//  シート内容の変更はOleContainerのModifiedプロパティでも検出可能
//  Modifiedはセルの実際の変更ではなく操作があった事を検出する
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  LFileName      : OleVariant;
  LobjDialogItem : OleVariant;
begin
  if OleContainer1.State = osEmpty then exit;

  //どこかのセルに変更があったら保存ダイアログを表示
  try
    if FCellChangeFlag then begin
      //保存ファイルのフルパス名
      LFileName := ExpandFileName('..\OleTest_01.xls');

      try
        //TExcelApplication.Dialogsメソッドは引数が多いので
        //省略するためにOleVariant型で扱うことにする
        LobjDialogItem := ExcelApplication1.Dialogs.Item[xlDialogSaveAs];
        LobjDialogItem.Show(Arg1 := LFileName);
      except
        //保存ダイアログを[キャンセル]で閉じると何故か時々エラー
      end;
    end;

    Sleep(100);
    Application.ProcessMessages;

  finally
    //順番にDisconnect
    //TExcelApplicationはQuitは必要
    ExcelWorkSheet1.Disconnect;
    ExcelWorkBook1.Close;
    ExcelWorkBook1.Disconnect;
    ExcelApplication1.Disconnect;
  end;
end;

//=============================================================================
//  フォーム破棄時の処理
//
//  OleContainerのオブジェクトを破棄
//  ここにTExcelApplicationのDisconnect等の処理を行うとメモリリーク発生
//  この時点でOleContainerのオブジェクトは既にない
//  このコードは異常終了に備えたもの
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  OleContainer1.DestroyObject;
end;

//=============================================================================
//  TExcelApplication1のOnSheetActiveイベント処理
//
//  アクティブなシートが変わったらダイアログ表示
//=============================================================================
procedure TForm1.ExcelApplication1SheetActivate(ASender: TObject;
  const Sh: IDispatch);
begin
  MessageBox(Handle, 'シートが変わったよ', '情報', MB_ICONINFORMATION);
end;

//=============================================================================
//  TExcelApplication1のOnSheetChangeイベント処理
//
//  シート(セル)に変更があったらChangeFalをTrueにしておく
//  このイベントは実際の変更がなくても編集状態が終了すると発生する
//  TExcelWorkSheetのOnChangeイベントも引数が違うだけで動作は同じ
//  セルの編集はOleContainerのModifiedプロパティでも検出可能
//=============================================================================
procedure TForm1.ExcelApplication1SheetChange(ASender: TObject;
  const Sh: IDispatch; const Target: ExcelRange);
begin
  FCellChangeFlag := True;
end;

end.




02_ダイアログの Show メソッドの引数


上のコードでは,保存の際に表示するダイアログの Show メソッドで,デフォルトの保存ファイル名を指定しています.この引数については Excel のヘルプで確認してください.
ただし,ヘルプにも MSDN オンラインの記事にも,引数の詳しい説明はありません.引数の名前から判断する以外にないようです.


図6
Excel 2010 VBA のヘルプ
  • xlDialogSaveAs で検索した結果




03_コマンドの送信エラー


本ページのように TOleContainer を使用したプログラムが正常に終了しなかった場合,エクセルのブックを開こうとすると,下図のような「プログラムにコマンドを送信している時に,エラーが発生しまた」というエラーが発生することがあります.
これは DDE 通信関係のエラーです.リンクの記事を参考にして設定を変更してください.


図7
DDE 関係のエラー




04_操作用のボタンを配置する TPanel の設定


本ページの最初のサンプルではフォームのクライアント領域全体に TOleContainer を表示していますが,実際のアプリでは下図のように,操作用のボタン類をフォームに配置するのが一般的です.

本サンプルでは Panel1 (TPanel) をフォームに配置して,Align プロパティの値を alTop に,Locked プロパティの値を True にしています.そこにボタン類を配置しています.その下に Panel2 (TPanel) を配置して Align プロパティの値を alClient にしています.この TPanel の上に TOleContainer を配置してその Align プロパティの値を alClient にしています.

エクセルのブックを読み込んで表示した後,TExcelApplication.OnWorkbookOpen イベント内でリボンコントロールを非表示にしています.
本サンプルでは,読み込むエクセルのブックのファイル名は添付の OleTest.xls としています.保存ファイル名は OleTest_01.xls の固定としています.



図8
設計時の画面
  • Panel1 (TPanel) の設定
    TPanel.Align := alTop
    TPanel.Locked := True
  • Panel2 (TPanle) の設定
    TPanel.Align := alClient
  • TOleContainer を Panel2 に配置
    Align は alClient
  • 以下のコンポーネントを配置
    TExcelApplication
    TExcelWorkBook
    TExcelWorkSheet
図9
実行して [Button1] クリック
  • エクセルのブックを表示
  • リボンコントロールはイベント処理で非表示にしている
図10
[Button2] クリック
  • エクセルのブックを保存

リスト2
OLE コンテナにエクセルを表示.操作用のボタン類をフォームに配置
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Excel2000, Vcl.OleServer,
  Vcl.OleCtnrs, Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    ExcelApplication1: TExcelApplication;
    ExcelWorkbook1: TExcelWorkbook;
    ExcelWorksheet1: TExcelWorksheet;
    Panel1: TPanel;
    Panel2: TPanel;
    OleContainer1: TOleContainer;
    Button1: TButton;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure ExcelApplication1WorkbookOpen(ASender: TObject;
      const Wb: _Workbook);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//
//  操作用ボタン類を配置するためのTPanelを配置
//  そのTPanelのLockedプロパティをTrueにするのがポイント
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Panel1.Caption := '';
  Panel2.Caption := '';
  Panel1.Locked  := True;

  Button2.Enabled := False;

  // 既に起動中のエクセルと操作を分離するため
  // このプログラムで生成したオブジェクトを対象とする
  ExcelApplication1.ConnectKind := ckNewInstance;

  // TOleContainer.AutoActivate := aaGetFocus にしておくと
  // TOleContainer.SetFocus でOLEオブジェクトをアクティブになる
  OleContainer1.AutoActivate := aaGetFocus;
end;

//=============================================================================
//  フォームを閉じる時の処理
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  LExcelWnd : HWND;
begin
  if OleContainer1.State = osEmpty then begin
    exit;
  end;

  // エクセルのウィンドウに[ESC]キーを送信
  // セルへの入力中に終了するとエラーになるのでその対策
  LExcelWnd := FindWindow('XLMAIN', nil);
  PostMessage(LExcelWnd, WM_KEYDOWN, VK_ESCAPE,  $00010001);
  PostMessage(LExcelWnd, WM_KEYUP,   VK_ESCAPE,  $C0010001);

  // リボンコントロールは表示にする
  //  本来は元の状態を退避しておいて元の値にするべき
  ExcelApplication1.ExecuteExcel4Macro('SHOW.TOOLBAR("Ribbon", True)');

  //順番にDisconnect
  //TExcelApplicationはQuitは不要
  ExcelWorkSheet1.Disconnect;
  ExcelWorkBook1.Close(False);
  ExcelWorkBook1.Disconnect;
  ExcelApplication1.Disconnect;
end;

//=============================================================================
//  フォーム破棄時の処理
//
//  OleContainerのオブジェクトを破棄
//  ここにTExcelApplicationのDisconnect等の処理を行うとメモリリーク発生
//  この時点でOleContainerのオブジェクトは既にない
//  このコードは異常終了に備えたもの
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  OleContainer1.DestroyObject;
end;

//=============================================================================
//  エクセルのブックを読み込んで表示
//
//  2回以上実行すると終了時にメモリリークが発生する
//  メモリリークの値は実行回数に関係なく一定
//  対処方法については不明
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LFilePath : string;
begin
  if ExcelApplication1.Visible[0] then begin
    //現在のシートとブックのDisconnect処理
    //現在接続しているブック切断する
    ExcelWorkSheet1.Disconnect;
    ExcelWorkBook1.Close(False);
    ExcelWorkBook1.Disconnect;
    ExcelApplication1.Disconnect;
  end;

  //開くブック名
  LFilePath := ExpandFileName('..\OleTest.xls');
  if not FileExists(LFilePath) then exit;

  //OLEオブジェクトを生成
  OleContainer1.CreateObjectFromFile(LFilePath, True);
  //OLEオブジェクトをアクティブにする
  OleContainer1.DoVerb(ovShow);

  // TExcelApplicationに接続
  ExcelApplication1.Connect;
  //表示しているブックをExcleWorkbook1に接続
  ExcelWorkbook1.ConnectTo(ExcelApplication1.ActiveWorkbook as _workbook);
  //シートを ExcelWorksheetに接続
  ExcelWorksheet1.ConnectTo(ExcelWorkbook1.ActiveSheet as _Worksheet);

  // 左上の位置に表示されるセルを指定する
  ExcelApplication1.ActiveWindow.ScrollColumn := 1;
  ExcelApplication1.ActiveWindow.ScrollRow    := 1;

  //'A1'セルを選択
  ExcelWorkSheet1.Range['C4', 'C4'].Select;

  Button2.Enabled := True;
end;

//=============================================================================
//  現在表示中のブックを保存
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LSaveFilePath  : OleVariant;
  LFileFormat    : OleVariant;
  LPassword      : OleVariant;
  LWritePassword : OleVariant;
  LReadOnly      : OleVariant;
  LCreateBackup  : OleVariant;
  LAccessMode    : OleVariant;
begin
  LSaveFilePath := ExpandFileName('..\OleTest_01.xls');

  //保存形式の指定
  LFileFormat    := EmptyParam;
  LPassword      := '';
  LWritePassword := '';
  LReadOnly      := False;
  LCreateBackup  := False;
  LAccessMode    := xlNoChange;

  try
    //OLEオブジェクトをアクティブにする
    //これがないとダイアログを表示した時にエクセルが見えなくなる
    //リボンコントロールを非表示にしても表示されてしまう
    OleContainer1.DoVerb(ovPrimary);

    // 左上の位置に表示されるセルを指定する
    ExcelApplication1.ActiveWindow.ScrollColumn := 1;
    ExcelApplication1.ActiveWindow.ScrollRow    := 1;

    // SaveAsは別名保存用のメソッド
    // 引数は省略不可
    ExcelWorkbook1.SaveAs( LSaveFilePath,   //保存ファイル名
                         LFileFormat,   //保存形式.現在のエクセルのバージョン形式
                         LPassword,     //パスワード
                         LWritePassword,//文書読出しパスワード指定
                         LReadOnly,     //読取り専用推奨表示の有無
                         LCreateBackup, //バックアップファイル作成
                         LAccessMode,   //アクセスモード
                         EmptyParam,    //ブックが共有の場合のコンフリクトの解決方法
                         EmptyParam,    //最近使用したファイルの一覧をブックに追加
                         EmptyParam,    //TextCodePage
                         EmptyParam,    //TextVisualLayout
                         0);
   except
     //[キャンセル]を選択すると例外が発生
   end;
end;

//=============================================================================
//  TExcelApplicationのOnWorkbookOpenイベント
//  ブックを表示した後に発生するイベント1
//=============================================================================
procedure TForm1.ExcelApplication1WorkbookOpen(ASender: TObject;
  const Wb: _Workbook);
begin
  // リボンコントロールを非表示にする
  ExcelApplication1.ExecuteExcel4Macro('SHOW.TOOLBAR("Ribbon", False)');
end;

end.


本項のサンプルも次項のサンプルも,Excel 2007 以降のブック形式ファイルである .xlsx も表示して操作できまずが,TOleContainer のオブジェクトのアプリケーションとしては,Microsof Excel 2003 となっています.下図はこのことを確認した結果です.


図11
アプリケーション名
  • Microsof Excel 2003

上の図の ProgID の値の最後の数値はエクセルのバージョン番号です.8 というのは Excel 97 相当のバージョンということになります.


バージョン    製品
3.1    Excel 3.1
4.0    Excel 4.0
5.0    Excel 5.0
7.0    Excel 95
8.0    Excel 97
9.0    Excel 2000
10.0    Excel 2002
11.0    Excel 2003
12.0    Excel 2007
14.0    Excel 2010
15.0    Excel 2013
16.0    Excel 2016


リスト3
TOleContainer のオブジェクトのアプリケーション名を調べる
//-----------------------------------------------------------------------------
//  IOleObjectからアプリ名を取得
//  usesにActiveX,ComObjが必要
//-----------------------------------------------------------------------------
function GetOleObjectAppName(const OleObject: IOleObject): string;
var
  LAppName: PWideChar;
begin
  OleCheck(OleObject.GetUserType(USERCLASSTYPE_APPNAME, LAppName));
  try
    Result := LAppName;
  finally
    CoTaskMemFree(LAppName);
  end;
end;

//=============================================================================
//  TOleContainerに表示中のCOMアプリの名前とProgIDの値を取得して表示
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LOleObject : IOleObject;
  LClassID   : TGUID;
  LProgID    : string;
  LInfoText  : string;
begin
  OleContainer1.DoVerb(ovPrimary);

  // IOleObjectを取得
  LOleObject := OleContainer1.OleObjectInterface;

  // アプリ名を取得
  LInfoText := GetOleObjectAppName(LOleObject);

  // ProgIDの値を取得
  LOleObject.GetUserClassID(LClassID);
  LProgID := ClassIDid-dategID(LClassID);

  LInfoText := LInfoText + sLineBreak + LProgID;
  MessageBox(Handle, PChar(LInfoText), 'アプリ名', MB_ICONINFORMATION);
end;




05_OleVariant 型でエクセルのオブジェクトを扱う例


前項と動作仕様はほとんど同じてすが,エクセル関係のオブジェクトを OleVariant 型で扱う例です.
フォームに配置しているコントロール類の設定は前項のサンプルと同じです.
OleVariant 型で扱う場合はイベントは利用できません.そこでリボンコントロールの非表示はボタンクリックで行うようにしています.
本サンプルでも,読み込むエクセルのブックのファイル名は添付の OleTest.xls としています.保存ファイル名は OleTest_01.xls の固定としていすま.


図12
実行して [Button1] クリック
  • エクセルのブックを表示
  • セルの値を一部変更
図13
[Button2] クリックで保存
  • ブックの表示位置が変わる
図14
[Button3] クリック
  • リボンコントロールを非表示

リスト4
OLE コンテナにエクセルを表示.操作用のボタン類をフォームに配置
エクセル関係のオブジェクトを OleVariant 型で扱う例
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.OleServer, Vcl.OleCtnrs,
  Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    OleContainer1: TOleContainer;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
    FobjExcel     : OleVariant;
    FobjWorkBook  : OleVariant;
    FobjWorkSheet : OleVariant;
  end;

var
  Form1: TForm1;

implementation

uses
  ComObj;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//
//  操作用ボタン類を配置するためのTPanelを配置
//  そのTPanelのLockedプロパティをTrueにするのがポイント
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Panel1.Caption := '';
  Panel2.Caption := '';
  Panel1.Locked  := True;

  Button2.Enabled := False;
  Button3.Enabled := False;
end;

//=============================================================================
//  フォームを閉じる時の処理
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  LExcelWnd : HWND;
begin
  if VarIsEmpty(FobjExcel) then begin
    exit;
  end;

  // エクセルのウィンドウに[ESC]キーを送信
  // セルへの入力中に終了するとエラーになるのでその対策
  LExcelWnd := FindWindow('XLMAIN', nil);
  PostMessage(LExcelWnd, WM_KEYDOWN, VK_ESCAPE,  $00010001);
  PostMessage(LExcelWnd, WM_KEYUP,   VK_ESCAPE,  $C0010001);

  // リボンコントロールは表示にする
  // 本来は元の状態を退避しておいて元の値にするべき
  FobjExcel.Application.ExecuteExcel4Macro('SHOW.TOOLBAR("Ribbon", True)');

  FobjWorkSheet := unAssigned;
  FobjWorkBook  := unAssigned;
  FobjExcel     := unAssigned;
end;

//=============================================================================
//  フォーム破棄時の処理
//
//  OleContainerのオブジェクトを破棄
//  ここにTExcelApplicationのDisconnect等の処理を行うとリソース漏れ
//  この時点でOleContainerのオブジェクトは既にない
//  このコードは異常終了に備えたもの
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  OleContainer1.DestroyObject;
end;

//=============================================================================
//  エクセルのブックを読み込んで表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LFilePath : string;
begin
  //フルパスで指定
  LFilePath := ExpandFileName('..\OleTest.xls');

  //OLEオブジェクトを生成
  OleContainer1.CreateObjectFromFile(LFilePath, True);
  //OLEオブジェクトをアクティブにする
  OleContainer1.DoVerb(ovPrimary);

  FobjExcel     := OleContainer1.OleObject;
  FobjWorkBook  := FobjExcel.Application.ActiveWorkbook;
  FobjWorkSheet := FobjWorkBook.ActiveSheet;

  FobjWorkSheet.cells[5, 2] := '日本代表選手';
  FobjWorkSheet.cells[4, 3] := 123455;

  Button2.Enabled := True;
  Button3.Enabled := True;
end;

//=============================================================================
//  現在表示中のブックを保存
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
const
  xlWorkbookDefault = 51;
var
  LSaveFilePath : string;
begin
  LSaveFilePath := ExpandFileName('..\OleTest_01.xls');
  try
    //これがないとダイアログを表示した時にエクセルが見えなくなる
    //リボンコントロールを非表示にしても表示されてしまう
    OleContainer1.DoVerb(ovPrimary);
    FobjWorkBook.saveAs(LSaveFilePath);
  except
  end;
end;

//=============================================================================
//  リボンコントロールを非表示にする
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
  FobjExcel.Application.ExecuteExcel4Macro('SHOW.TOOLBAR("Ribbon", False)');
  //OLEオブジェクトをアクティブにする
  OleContainer1.DoVerb(ovPrimary);
end;

end.




06_OleVariant 型でエクセルのオブジェクトを扱う際の挙動例


エクセルのオブジェクトを OleVariant 型で扱う場合は,そのインスタンスはシステムで 1 つしか有効になりません.このことを認識しておく必要があります.
下図は,エクセルを起動した後,前項のサンプルの EXE を起動した画面です.


図15
エクセルを起動した後に前項のサンプルの EXE を起動
  • 背後がエクセル

起動した前項のサンプルで [Button1] をクリックして,OLE コンテナにエクセルを表示します.すると下図のようになります.
この状態では,既に起動したエクセルにフォーカスが移動できなくなります.既に起動しているエクセルの列と行番号が消えて,リボンコントロールも使用不可の状態になっています.

TExcelApplication コンポーネントを使用するサンプルではこの現象は発生しません.
また,エクセルを起動しないで,埋め込みオブジェクトとして操作しているだけであれば,このような現象は発生しません.



図16
前項のサンプルの [Button1] をクリック
  • エクセルのブックが表示される
  • 背後のエクセルにはフォーカス移動できなくなる




07_SaveAsDocument メソッドによるオブジェクトの保存


本サンプルではエクセルのブックの保存ですが,オブジェクトの保存には TOleContainer.SaveAsDocument メソッドも使用できます.以下はその例です.


[備考]
SaveAsDocument メソッドは,Excel 2007 以降の拡張子が .xlsx のブックは正常に保存できません.保存できても,CreateObjectFromFile メソッドで読み込んで TOleContainer に表示できません.また,エクセルでも正常に開くことはできません.
TOleContainer で直接扱えるエクセルの形式は Excel 97 - 2003 で扱える形式 (OLE2 形式) です.このメソッドで保存したファイルは,MS OLE 構造化ストレージという形式,複合化された構造化ストレージとも言える形式で保存されています.


リスト5
SaveAsDocument メソッドによるオブジェクトの保存
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.OleServer, Vcl.OleCtnrs,
  Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    OleContainer1: TOleContainer;
    Button1: TButton;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//
//  操作用ボタン類を配置するためのTPanelを配置
//  そのTPanelのLockedプロパティをTrueにするのがポイント
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Panel1.Caption := '';
  Panel2.Caption := '';
  Panel1.Locked  := True;

  Button2.Enabled := False;
end;

//=============================================================================
//  フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  // このコードは異常終了に備えたもの
  OleContainer1.DestroyObject;
end;

//=============================================================================
//  エクセルのブックを読み込んで表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LFilePath : string;
begin
  LFilePath := ExpandFileName('..\OleTest.xls');

  OleContainer1.CreateObjectFromFile(LFilePath, True);
  //OLEオブジェクトをアクティブにする
  OleContainer1.DoVerb(ovPrimary);

  Button2.Enabled := True;
end;

//=============================================================================
//  TOleContainerに表示中のオブジェクトをSaveAsDocumentメソッドで保存
//
//  Excel2007以降のブック形式 xlsx は正常に保存できない
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LFilePath : string;
begin
  LFilePath := ExpandFileName('..\OleCtrl_Doc.xls');

  OleContainer1.SaveAsDocument(LFilePath);
  //OLEオブジェクトをアクティブにする
  OleContainer1.DoVerb(ovPrimary);
end;

end.




08_SaveToFile メソッドと LoadFromFile メソッド


TOleContainer に SaveToFile というメソッドがあります.
このメソッドは表示中のオブジェクトを TOleContainer 専用の形式でファイルに保存します.このメソッドで保存したファイルは LoadFromFile メソッドでないと正常に読み込めません.
本サンプルでは,添付のエクセルのブックを読み込んで,SaveToFile メソッドで保存します.

これらのメソッドは,TOleContainer コントロールだけでエクセルやワードあるいは他のオブジェクトを扱うのであれば便利なメソッドと言えます.



図17
実行して [Button1] クリック
  • 添付のエクセルのブックを読み込んで表示
図18
[Button2] クリックで保存
  • SaveToFile メソッドで保存
  • リボンコントロールが非表示になる
図19
[Button2] クリックで保存
  • SaveToFile メソッドで保存したファイルを LoadFromFile メソッドで読み込んで表示

リスト6
SaveToFile メソッドによる保存と LoadFromFile メソッドによる読み出し
SaveToFile メソッドによるオブジェクトの保存は TOleContainer 専用の形式での保存となる
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.OleServer, Vcl.OleCtnrs,
  Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    OleContainer1: TOleContainer;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//
//  操作用ボタン類を配置するためのTPanelを配置
//  そのTPanelのLockedプロパティをTrueにするのがポイント
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Panel1.Caption := '';
  Panel2.Caption := '';
  Panel1.Locked  := True;

  Button2.Enabled := False;
  Button3.Enabled := False;
end;

//=============================================================================
//  フォーム破棄時の処理
//
//  OleContainerのオブジェクトを破棄
//  このコードは異常終了に備えたもの
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  OleContainer1.DestroyObject;
end;

//=============================================================================
//  エクセルのブックを読み込んで表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LFilePath : string;
begin
  LFilePath := ExpandFileName('..\OleTest.xls');

  OleContainer1.CreateObjectFromFile(LFilePath, True);
  //OLEオブジェクトをアクティブにする
  OleContainer1.DoVerb(ovPrimary);

  Button2.Enabled := True;
  Button3.Enabled := True;
end;

//=============================================================================
//  TOleContainerに表示中のオブジェクトを保存
//  TOleContainerで扱える形式のオブジェクトとして保存
//  拡張子を.xlsにして保存してもエクセルでは表示できない
//  保存時の拡張子は任意
//
//  ここで保存したファイルはCreateObjectFromFileメソッドでは読み込めない
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LFilePath : string;
begin
  LFilePath := ExpandFileName('..\OleCtrl.ole');

  OleContainer1.SaveToFile(LFilePath);
end;

//=============================================================================
//  TOleContainer専用のオブジェクト形式のファイルを読み込む
//  LoadFromFileメソッドはエクセルのブック等は読み込めない
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LFilePath : string;
begin
  //フルパスでないと読み込めない
  LFilePath := ExpandFileName('..\OleCtrl.ole');

  OleContainer1.LoadFromFile(LFilePath);
  //OLEオブジェクトをアクティブにする
  OleContainer1.DoVerb(ovPrimary);
end;

end.