Delphi Programming / Object Pascal
[掲載 2005年02月01日] [更新 2020年07月28日] Delphi サンプルプログラム集
400_CreateOleObject によるエクセル操作の基本例
動作確認等
Windows 7 U64(SP1) + Delphi XE Pro + Excel 2010
400_Excel_CreateObject.zip [2,246 KB] 2020年07月28日版 (EXE 同梱)
2010年06月13日
CreateOLEObject 関係の記事追加と動作確認環境の変更、コード修正
2014年01月12日
プライマリモニタに表示するようにした
2014年01月22日
エクセル操作のテスト用基本コードのサンプルを追加
履歴の一部を削除
2014年06月13日
終了時の処理コードを変更 (エクセル側でブックを閉じてしまった時への対応策の追加)
他のアプリの操作に関する注意の記事を追加
CreaateOleObject で生成したエクセルのオブジェクトの操作
CreateOleObject で生成したエクセルのオブジェクトを、OleVariant 型で操作する例です。
OleVariant 型でオブジェクトを扱う場合、コード補完機能は使用できません。
単にエクセルというアプリケーションを起動するだけであれば、本ページのようなコードは必要ありません。[485_アプリの起動とファイルを開く関数] の記事あるいは、その記事中のリンクを参考にしてください。
[備考 1]
エクセルに限らず、CreatOleObject で生成した COM オブジェクトのインスタンスは、 1 つしか存在できません。複数のエクセルのスンスタンスが必要な場合は、クラスのインスタンスを生成して利用します。エクセルの場合は、TExcelApplication を使用します。TButton 等と同じように、クラスのインスタンスは複数生成することができます。
複数のブックを扱う場合、複数のエクセルのインスタンスを生成しなくても、1 つのエクセルのインスタンスで、複数のブックのオブジェクトが扱えます。
[備考 2]
ネット上に、タイプライブラリをインポート (タイプライブラリの取り込み)
して作成したユニットを使用するサンプルがありますが、Delphi の professional エディション以上であれば、その操作は必要ありません。Excel2000, ExcelXP, Excel2010 等のユニットには、タイプライブラリの取り込みで作成したユニットと同じ Excel 操作用のコードが含まれています。どのようなユニットがあるかは以下の記事で確認してください。
それらのユニットが実装されていない Delphi ではタイプライブラリの取り込みでユニットファイルを作成する必要があります。Delphi 6 等の Personal エディションには、それらのユニットが実装されていません。
Delphi の IDE で本サンプルプログラムをコンパイルして実行すると、GetActiveOleObject 関数は例外を発生して失敗します。作成した EXE を直接実行してください。
Delphi の IDE でコンパイルして実行した場合、通常は、管理者権限での実行となります。本サンプルを管理者権限で実行すると、GetActiveOleObject が例外を発生します。
[備考 1]
GetActiveOleObject 関数が例外を発生するのは、Windows Vista 以降のユーザーインタフェイス特権の分離 (UIPI) 機能によるものです。本サンプルを管理者権限で実行した場合、エクセルも管理者権限で実行すれば、エクセルからのメッセージを受信できるようになり、GetActiveOleObject 関数は例外を発生しません。起動中のエクセルの存在を検出できます。
[備考 2]
エクセルを管理者権限で実行するには、エクセルの [プロパティ] ダイアログで、[互換性] タブの [特権レベル 管理者としてこのプログラムを実行する] をチェックします。
ただし、テストだけにしておいた方が無難です。管理者権限での実行のままにしておくと、エクセルの機能の一部が正常に動作しなる場合があります。
[備考 3]
エクセルが起動しているか、エクセルのブックが開いているかは、以下の記事のような方法でも確認することができます。
このサンプルのプロジェクトソースは次のようになっています。フォームが最前面に表示されない場合は、MainFormOnTaskbar の値を False にしてみてください。筆者の Windows XP の環境でコンパイルした場合、True のままだと fsStayOnTop が無効になってしまいました。
図1
設計時の画面
copy code
unit Unit1;
interface
uses
// テストで定数定義変数を使用する場合は、Office2000を追加する
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Excel2000, OleServer;
type
TForm1 = class (TForm)
GroupBox1: TGroupBox;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Button5: TButton;
Button6: TButton;
Button7: TButton;
Button8: TButton;
Button9: TButton;
Button10: TButton;
Button11: TButton;
OpenDialog1: TOpenDialog;
Button12: TButton;
Button13: TButton;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button1Click(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 Button9Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button10Click(Sender: TObject);
procedure Button11Click(Sender: TObject);
procedure Button12Click(Sender: TObject);
procedure Button13Click(Sender: TObject);
private
{ Private 宣言 }
FExcelObj : OleVariant;
FWorkBook : OleVariant;
FWorkSheet : OleVariant;
procedure ClearActiveSheetCells;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses ComObj;
{$R *.dfm}
// =============================================================================
// フォーム生成時の処理
// エクセルに隠れないようにこのFormを常に最前面の右上に表示するようにする
// =============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
FormStyle := fsStayOnTop;
Self.Left := Screen.PrimaryMonitor.Width - Self.Width;
Self.Top := Screen.PrimaryMonitor.WorkareaRect.Top;
end ;
// =============================================================================
// フォームを閉じる時に念のため unAssignedしておく
// =============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if not VarIsEmpty(FExcelObj) then begin
FWorkBook := unAssigned;
FWorkSheet := unAssigned;
FExcelObj := unAssigned;
end ;
end ;
// =============================================================================
// 新規にブックを作成し表示する
// 表示した Exel のセルに値をセット
// もしこのプログラムで制御しているエクセルがあれば何もしない
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
if VarIsEmpty(FExcelObj) then begin
// Excel のオブジェクトを作成(特定の警告やメッセージを非表示)
FExcelObj := CreateOleObject('Excel.Application' );
FExcelObj.DisplayAlerts := False;
// ブックを作成
FWorkBook := FExcelObj.Workbooks.Add;
// シートオブジェクトを定義
FWorkSheet := FWorkBook.WorkSheets[1 ];
// 表示
FExcelObj.Visible := True;
// 最大化で表示
// 後でエクセルを単独起動した時最大化表示となってしまうので注意
FExcelObj.WindowState := xlMaximized;
end ;
end ;
// =============================================================================
// [ファイルを開く]ダイアログを表示して既存のブックを開く
// もしこのプログラムで操作しているブックがあれば閉じる
// また DisplayAlerts =T rue にしている時は、保存の確認ダイアログが表示されるので
// SetWindowPos でエクセルを前面に表示してダイアログが隠れない様にする
// 表示したら Excel のセルに値をセット
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LVer : string ;
LCaption : string ;
LWnd : HWND;
begin
if VarIsEmpty(FExcelObj) then begin
FExcelObj := CreateOleObject('Excel.Application' );
end ;
with OpenDialog1 do begin
// 初期フォルダ
InitialDir := ExtractFileDir(Application.ExeName);
LVer := FExcelObj.Version;
if StrToFloatDef(LVer, 0.0 ) > 11.9 then begin
Filter := 'エクセルブック (*.xls;*.xlsx)|*.xls;*.xlsx' ;
end else begin
Filter := 'エクセルブック (*.xls)|*.xls' ;
end ;
end ;
if OpenDialog1.Execute then begin
if not VarIsEmpty(FWorkBook) then begin
LCaption := FExcelObj.Caption;
LWnd := FindWindow(nil , PChar(LCaption));
SetWindowPos(LWnd,
HWND_TOP,
0 ,
0 ,
0 ,
0 ,
SWP_NOMOVE or SWP_NOSIZE);
FWorkBook.Close;
end ;
// ブックを開く
FWorkBook := FExcelObj.Workbooks.Open(OpenDialog1.FileName);
// シートオブジェクトを定義
FWorkSheet := FWorkBook.WorkSheets[1 ];
// 表示
FExcelObj.Visible := True;
FExcelObj.ActiveWindow.WindowState := xlMaximized;
FWorkSheet.Cells[2 ,2 ].Value := 'ABC' ;
end ;
end ;
// =============================================================================
// 既に誰かが表示したエクセルを操作する
// 表示したらこのフォームの左側いっぱいに表示
// WorkRect はタスクバーを除いたモニタのワークエリア
// =============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
LxlsHandle : HWND;
LxlsWidth : Integer;
LxlsHeight : Integer;
LWorkRect : TRect;
begin
// エクセルが起動中かをハンドルの有無で検査
LxlsHandle := FindWindow('XLMAIN' , nil );
if LxlsHandle = 0 then begin
MessageBox(Handle, 'エクセルは起動していません' , '情報' , MB_ICONINFORMATION);
end else begin
// 表示位置を調整
LWorkRect := Screen.PrimaryMonitor.WorkareaRect;
LxlsWidth := LWorkRect.Right - Self.Width;
LxlsHeight := LWorkRect.Bottom - LWorkRect.Top;
SetWindowPos(LxlsHandle,
HWND_TOP,
LWorkRect.Left,
LWorkRect.Top,
LxlsWidth,
LxlsHeight,
SWP_SHOWWINDOW);
// 表示中のExcelのオブジェクトを取得
// 管理者権限で実行すると失敗する
FExcelObj := GetActiveOleObject('Excel.Application' );
// 表示中のブックに接続
FWorkBook := FExcelObj.ActiveWorkbook;
// シートオブジェクトを定義(ここではSheet3)
FWorkSheet := FWorkBook.WorkSheets[3 ];
// そのシートをアクティブにする
FWorkSheet.Activate;
// 表示
FExcelObj.Visible := True;
end ;
end ;
// =============================================================================
// エクセルは開いたまま終了
// =============================================================================
procedure TForm1.Button4Click(Sender: TObject);
begin
Close;
end ;
// =============================================================================
// エクセルも一緒に閉じる
// このプログラム起動前に表示しているエルクセルは閉じない
//
// 初期化の unAssigned は終了時には不要だが、習慣的に実行した方がよい
// =============================================================================
procedure TForm1.Button5Click(Sender: TObject);
var
LBookObj : OleVariant;
LBookNo : Integer;
begin
// エクセルのオブジェクトが生成されているかをチェック
if not VarIsEmpty(FExcelObj) then begin
// テスト用なので警告ダイアログを非表示にする
FExcelObj.Application.DisplayAlerts := False;
// 開いているブックを保存しないで閉じる
for LBookNo := FExcelObj.Workbooks.count downto 1 do begin
LBookObj := FExcelObj.Workbooks[LBookNo];
LBookObj.Saved := True;
LBookObj.Close;
end ;
// FWorkBook オブジェクトが生成されているかをチェック
if not VarIsEmpty(FWorkBook) then begin
FWorkBook := unAssigned;
// FWorkSheet オブジェクトが生成されているかをチェック
if not VarIsEmpty(FWorkSheet) then begin
FWorkSheet := unAssigned;
end ;
end ;
FExcelObj.Quit;
FExcelObj := unAssigned;
end ;
Close;
end ;
// =============================================================================
// テスト用に現在アクティブなシートを全てクリアする関数
// ClearContent を使用すると、書式を残したままセルの値をクリアできる
// =============================================================================
procedure TForm1.ClearActiveSheetCells;
begin
if VarIsEmpty(FExcelObj) then exit;
FWorkSheet.Cells.Clear;
end ;
//
// ------ この上のコードはエクセルの起動から終了の基本コード ------
//
// =============================================================================
// セル A1 に文字列 ABC をセット
//
// [備考]
// TExcelApplication, TExcelSheet等のコンポーネントを使用時は、
// FWorkSheet.Cells.Items[1,1].Value := 'ABC';
// のように Items が必要
// =============================================================================
procedure TForm1.Button6Click(Sender: TObject);
begin
if VarIsEmpty(FExcelObj) then exit;
ClearActiveSheetCells;
FWorkSheet.Cells[1 ,1 ].Value := 'ABC' ;
FWorkSheet.Cells[1 ,1 ].Interior.ColorIndex := 4 ;
end ;
// =============================================================================
// 行の挿入
// どこに挿入したかわかるように文字列をセット
// xl.....で定義されている定数を使用するには Excel2000.pas や Office 000.pas 等が
// uses に必要(あるいは自分で定義する)
// =============================================================================
procedure TForm1.Button7Click(Sender: TObject);
begin
if VarIsEmpty(FExcelObj) then exit;
// シートの内容をクリアしてから
ClearActiveSheetCells;
FWorkSheet.Range['A1:A7' ].Value := '123' ;
FWorkSheet.Range['A7:A7' ].Insert(xlShiftDown);
end ;
// =============================================================================
// シートの追加
// 最後尾に追加
// =============================================================================
procedure TForm1.Button8Click(Sender: TObject);
begin
if VarIsEmpty(FExcelObj) then exit;
FWorkBook.WorkSheets.Add(EmptyParam,
FWorkBook.WorkSheets[FWorkBook.WorkSheets.Count]);
end ;
// =============================================================================
// 文字列の検索
// 現在アクティブなシート内の 'AB' を最初だけ検索
// =============================================================================
procedure TForm1.Button9Click(Sender: TObject);
var
LobjRow : Variant;
LStrText : String ;
begin
if VarIsEmpty(FExcelObj) then exit;
// テスト用の値をセット
FWorkSheet := FWorkBook.WorkSheets[1 ];
FWorkSheet.Activate;
ClearActiveSheetCells;
FWorkSheet.Cells[2 ,3 ].Value := 'ABC' ;
FWorkSheet.Cells[5 ,3 ].Value := 'DAB' ;
FWorkSheet.Cells[7 ,3 ].Value := 'ABC' ;
try
// FWorkSheet.Cells.Find('AB').Row;が通常のコードであるが
// このような書き方も可能という例
LobjRow := FWorkSheet.Cells.Find(what := 'AB' , LookIn := xlValues).Row;
LStrText := VarToStr(LobjRow) + '行目にあります' ;
MessageBox(Handle, PChar(LStrText), '情報' , MB_ICONINFORMATION);
except
MessageBox(Handle, '見つかりません' , '情報' , MB_ICONINFORMATION);
end ;
end ;
// =============================================================================
// 日付型を文字列に変換
// =============================================================================
procedure TForm1.Button10Click(Sender: TObject);
var
LDateTime : TDateTime;
LFormat : String ;
begin
if VarIsEmpty(FExcelObj) then exit;
// テスト用の値をセット
FWorkSheet.Cells[1 ,2 ].NumberFormatLocal := 'ggee年mm月dd日' ;
FWorkSheet.Cells[1 ,2 ].Value := '2005/3/6' ;
// セルの値を TDataTime 型の値として取り出す
LDateTime := VarToDatetime(FWorkSheet.Cells[1 , 2 ]);
// 表示形式を設定して表示
LFormat := 'ggee"年"mm"月"dd"日' ;
Button10.Caption := FormatDateTime (LFormat, LDateTime);
end ;
// =============================================================================
// 整数値を文字列に変換して取得
// =============================================================================
procedure TForm1.Button11Click(Sender: TObject);
var
LVar: Variant;
begin
if VarIsEmpty(FExcelObj) then exit;
LVar := FWorkSheet.Cells[1 , 3 ];
Button11.Caption := VarToStr(LVar);
end ;
// =============================================================================
// 枠線と枠内罫線
// uses に Excel2000 が必要(各定数値使用のため)
// CreateOleObject の場合はwith ... do begin 文は使用不可
// [Servers] タブの TExcel.... の場合はエクセル操作フォームの例参照
// =============================================================================
procedure TForm1.Button12Click(Sender: TObject);
var
LRange: Variant;
begin
if VarIsEmpty(FExcelObj) then exit;
// 対象範囲
LRange := FWorkSheet.Range['B5' , 'G22' ];
// 枠罫線
LRange.BorderAround(xlContinuous,xlMedium, 16 , EmptyParam);
// インサイド罫線.縦
LRange.Borders[xlInsideVertical].LineStyle := xlContinuous;
LRange.Borders[xlInsideVertical].Weight := xlHairline;
LRange.Borders[xlInsideVertical].ColorIndex := 12 ;
// インサイド罫線.横
LRange.Borders[xlInsideHorizontal].LineStyle := xlContinuous;
LRange.Borders[xlInsideHorizontal].Weight := xlHairline;
LRange.Borders[xlInsideHorizontal].ColorIndex := 12 ;
end ;
// =============================================================================
// 上下左右個別罫線
// uses に Excel2000 が必要(各定数値使用のため)
// CreateOleObject の場合はwith ... do begin 文は使用不可
// [Servers] タブの TExcel.... の場合はエクセル操作フォームの例参照
// =============================================================================
procedure TForm1.Button13Click(Sender: TObject);
var
LRange: Variant;
begin
if VarIsEmpty(FExcelObj) then exit;
LRange := FWorkSheet.Range['F24' , 'G25' ];
// 上罫線
LRange.Borders[xlEdgeTop].LineStyle := xlContinuous;
LRange.Borders[xlEdgeTop].Weight := xlThick;
LRange.Borders[xlEdgeTop].ColorIndex := 15 ;
// 下罫線
LRange.Borders[xlEdgeBottom].LineStyle := xlContinuous;
LRange.Borders[xlEdgeBottom].Weight := xlThick;
LRange.Borders[xlEdgeBottom].ColorIndex := 15 ;
// 左罫線
LRange.Borders[xlEdgeLeft].LineStyle := xlContinuous;
LRange.Borders[xlEdgeLeft].Weight := xlThick;
LRange.Borders[xlEdgeLeft].ColorIndex := 15 ;
// 右罫線
LRange.Borders[xlEdgeRight].LineStyle := xlContinuous;
LRange.Borders[xlEdgeRight].Weight := xlThick;
LRange.Borders[xlEdgeRight].ColorIndex := 15 ;
// セルの結合
LRange.MergeCells := True;
LRange.HorizontalAlignment:= xlCenter;
LRange.VerticalAlignment := xlCenter;
LRange.WrapText := True;
// 右下がり斜め罫線
LRange.Borders[xlDiagonalDown].LineStyle := xlDot;
LRange.Borders[xlDiagonalDown].Weight := xlHairline;
LRange.Borders[xlDiagonalDown].Color := clGray;
// 右上がり斜め罫線
LRange.Borders[xlDiagonalUp].LineStyle := xlDot;
LRange.Borders[xlDiagonalUp].Weight := xlHairline;
LRange.Borders[xlDiagonalUp].Color := clGray;
end ;
end .
02_ OleVariant で扱う際の注意 - 文字列としての数字
Delphi は変数の型に厳しい言語です。それが便利でもあり、また面倒と感じることもあるかも知れません。型が明らかな場合には、Delphi は、多くの場合、変数の型変換を自動で行ってくれます。
OleVariant 型の場合も自動で行ってくることもありますが、変数の種類と値によっては、型を明示的にキャスト等しないと、期待通りの結果が得られないことがあります。
以下は、その 1 つの例です。
次のコードは、セルの書式を「文字列」にする例ですが、このコードは '@' という文字のコードである 64 という値が「数値」としてセルに表示されてしまいます。
図2
'@' の文字コードが表示される
エクセルのオブジェクトは、基本的に全てバリアント型である
Delphi の TExcelApplication 等は、扱いやすいように型を定義している
エクセルの場合、書式設定の '@' は、特殊な意味を持つ
リスト2
OleVariant で扱う際の注意
セルの書式を「文字列」にする場合に、失敗するコードの例
copy code
unit Unit1;
interface
uses
// テストで定数定義変数を使用する場合は、Office2000 を追加する
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Excel2000;
type
TForm1 = class (TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
FExcelObj : OleVariant;
FWorkBook : OleVariant;
FWorkSheet : OleVariant;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses
ComObj,
ActiveX;
{$R *.dfm}
// =============================================================================
// フォーム生成時の処理
// =============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
FormStyle := fsStayOnTop;
// エクセルのオブジェクトを作成 (特定の警告やメッセージを非表示)
FExcelObj := CreateOleObject('Excel.Application' );
FExcelObj.DisplayAlerts := False;
// ブックを作成
FWorkBook := FExcelObj.Workbooks.Add;
// シートオブジェクトを定義
FWorkSheet := FWorkBook.WorkSheets[1 ];
// 表示
FExcelObj.Visible := True;
end ;
// =============================================================================
// フォームを閉じる時にエクセルも一緒に閉じる
//
// フォームを閉じる時に念のため unAssigned しておく
// 初期化の unAssigned は終了時には不要だが、習慣的に実行した方がよい
// =============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// エクセルのオブジェクトが生成されているかをチェック
if VarIsEmpty(FExcelObj) then Exit;
// 開いているブックを保存しないで閉じる
FWorkBook.Saved := True;
FWorkBook.Close;
FExcelObj.Quit;
FWorkSheet := unAssigned;
FWorkBook := unAssigned;
FExcelObj := unAssigned;
end ;
// =============================================================================
// 数値を文字列 (数字) にする例
// @ 記号の ASCII コードの数値 64 が表示される
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LRange : OleVariant;
begin
LRange := FWorkSheet.Range['A1' , 'C10' ];
LRange.NumberFormatLocal := '@' ;
// Range ではなく、Cells を使ってみる
LRange.Cells[1 ,1 ].Value := '010' ;
end ;
end .
数字を常に指定桁数で表示したい場合、以下のような書式を使用する方法も考えられます。ただし、この場合、セルの値は文字列ではなく、数値となります。数値なので、デフォルトでの表示は右端寄せとなります。
// =============================================================================
// OLEObjectで変数等を扱う際の注意
// NumberFormatLocal でセルの書式を文字列に設定する例
// NumberFormatLocal に直接、文字列の書式である @ を指定
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LRange : OleVariant;
begin
LRange := FWorkSheet.Range['A1' , 'C10' ];
// 表示する桁数分の 0 を指定する (右寄せ表示となる)
LRange.NumberFormatLocal := '0000' ;
// 左寄せで表示したい場合
// LRange.HorizontalAlignment := xlLeft;
LRange.Cells[1 ,1 ].Value := 10 ;
end ;
セルの書式を「文字列」にするには、例えば以下のコードのように、文字に変換して、書式文字列の文字であることを明示的に認識させるか、OleVariant にキャストしてから代入します。
図3
数字が文字列として扱われる
リスト3
OleVariantで扱う際の注意
セルの書式を「文字列」にする例。変数の型を明示的に指定する
copy code
// =============================================================================
// OLEObjectで変数等を扱う際の注意
// NumberFormatLocal でセルの書式を文字列に設定する例
// NumberFormatLocal に VarToStr で文字列の書式である @ を指定
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LRange : OleVariant;
begin
LRange := FWorkSheet.Range['A1' , 'C10' ];
LRange.NumberFormatLocal := VarToStr('@' );
// Rangeではなく、Cellsを使ってみる
LRange.Cells[1 ,1 ].Value := '010' ;
end ;
// =============================================================================
// OLEObjectで変数等を扱う際の注意
// セルの書式を文字列に設定する例
// NumberFormatLocal に OleVariant で文字列の書式である @ を指定
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LRange : OleVariant;
begin
LRange := FWorkSheet.Range['A1' , 'C10' ];
LRange.NumberFormatLocal := OleVariant('@' );
// Rangeではなく、Cells を使ってみる
LRange.Cells[1 ,1 ].Value := '010' ;
end ;
// =============================================================================
// OLEObjectで変数等を扱う際の注意
// セルの書式を文字列に設定する例
// NumberFormatLocal に OleVariant 型の変数の文字 @ を指定
// =============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
LRange : OleVariant;
LType : OleVariant;
begin
LRange := FWorkSheet.Range['A1' , 'C10' ];
LType := '@' ;
LRange.NumberFormatLocal := LType;
// Rangeではなく、Cells を使ってみる
LRange.Cells[1 ,1 ].Value := '010' ;
end ;
OleVariant 型の変数は、次のように TVariantArg や TVarData で、変数の型を指定して扱うのが正統法と言えます。
ただし、このコードを実行しても、セルに '010' という文字列は表示できません。これはエクセルのセル側の書式の問題だからです。先行する 0 を表示するような書式を指定するには、上のサンプルのように NumberFormatLocal を使用します。
Delphi では、下のコードのようなバリアント型を指定しなくても自動認識してくれますが、特殊な型の変数を扱う場合は、TVariantArg や TVarData 型を使用した処理が必要な場合があります。
COM インターフェイスでは、文字列として WideString や PWideString 型を使用します。
このコードで String 型を使用すると、例外が発生します。
copy code
// =============================================================================
// OleVariant 型の変数に値を設定する際の最も正統的と思われる方法
// TrVaiantArg を使用する例
// TVariantArg 型や TVarData を使用する場合は、WideString 型を使用する
// TVariantArg, VT_XXX 等の定数名の使用には、uses に ActiveX が必要
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LRange : OleVariant;
LValue : OleVariant;
LCellVal : WideString;
begin
LRange := FWorkSheet.Range['A1' , 'C10' ];
LCellVal := '010' ;
TVariantArg(LValue).vt := VT_BSTR;
TVariantArg(LValue).bstrVal := PWideChar(LCellVal);
LRange.Cells[1 ,1 ].Value := LValue;
end ;
// =============================================================================
// OleVariant 型の変数に値を設定する際の最も正統的と思われる方法
// TVarData を使用する例
// TVariantArg 型や TVarData を使用する場合は、WideString 型を使用する
// TVariantArg, VT_XXX 等の定数名の使用には、uses に ActiveX が必要
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LRange : OleVariant;
LValue : OleVariant;
LCellVal : WideString;
begin
LRange := FWorkSheet.Range['A1' , 'C10' ];
LCellVal := '010' ;
TVarData(LValue).VType := varOleStr;
TVarData(LValue).VOleStr := PWideChar(LCellVal);
LRange.Cells[1 ,1 ].Value := LValue;
end ;
03_ OleVariant で扱う際の注意 - 整数型の定数値
上の場合と同じように、型に関する例です。
Excel を操作するコードでは、ExcelXXXX.pas 内で定義している定数名をよく使用します。多くの場合は、これらの定数は 16 進数で定義されていますから、Cardinal 型となります。一方、エクセル等のプロパティの値には負の値を持つものがあります。
例えば、下のコードはセルの書式設定の例ですが、定数 xlHAlignCenter の値は -4108 です。この定数名で実行すると、下図のように、「Range クラスの XXXXXXXX プロパティを設定できません」というエラーが発生する場合があります。本来ならば、プロパティ HorizontalAlignment の設定用関数で符号付きの整数値に変換するか、エクセルの方で解釈するはずですが、そうしてくれない場合があるようです。
そこで、下のコードでは、このプロパティに定数値を代入する際に、明示的に符号付きの整数型にキャストして実行しています。
この現象は、TExcelApplication コンポーネントを使用する際には、少なくても筆者の現在の環境では発生していません。
図4
プロパティが設定できない
リスト4
OleVariant で扱う際の注意
整数型のプロパティの値を、定数名で設定する場合
copy code
unit Unit1;
interface
uses
// テストで定数定義変数を使用する場合は、Office2000を追加する
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComObj, Excel2000;
type
TForm1 = class (TForm)
Button1: TButton;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
FExcelObj : OleVariant;
FWorkBook : OleVariant;
FWorkSheet : OleVariant;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
// =============================================================================
// フォームを閉じる時の処理
// =============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// エクセルのオブジェクトが生成されているかをチェック
if VarIsEmpty(FExcelObj) then Exit;
FWorkBook := unAssigned;
FWorkSheet := unAssigned;
FExcelObj.Quit;
FExcelObj := unAssigned;
end ;
// =============================================================================
// 新規にブックを作成し表示する
// HorizontalAlignment プロパティの設定例
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LRange : OleVariant;
begin
// Excel のオブジェクトを作成 (特定の警告やメッセージを非表示)
FExcelObj := CreateOleObject('Excel.Application' );
FExcelObj.DisplayAlerts := False;
// ブックを作成
FWorkBook := FExcelObj.Workbooks.Add;
// シートオブジェクトを定義
FWorkSheet := FWorkBook.WorkSheets[1 ];
// 表示
FExcelObj.Visible := True;
// 1 行目から 100 行目までの 2 列目のアライメントを設定
// 行と例の番号は 1 ベースの値
LRange := FWorkSheet.Range[FWorkSheet.Cells[1 , 2 ], FWorkSheet.Cells[100 , 2 ]];
LRange.HorizontalAlignment := Integer(xlHAlignCenter);
end ;
end .
あるいは、上の数字の時と同じように OleVariant でキャストし、OleVariant 型であることを明確にしても動作するようです。元々 OleVariant で扱うのですから、こちらの方がいいかも知れません。
LRange := FWorkSheet.Range[FWorkSheet.Cells[1 , 2 ], FWorkSheet.Cells[100 , 2 ]];
LRange.HorizontalAlignment := OleVariant(xlHAlignCenter);
04_ VBA ( マクロ ) のコードを参考にする
このサンプルプログラムは操作例が少ないので、必要であれば、ML や掲示板などで調べて下さい。掲示板などには結構操作例のコードがあります。あるいは、
で説明している Excel VBA のヘルプや VBA のコードを参考にして作成します。VBA のコードは、エクセル上で該当する操作をマクロに記録して、それを Delphi のコードに書換えます。また、記録したマクロをそのまま実行する方法もあります。
マクロの機能を利用するには、[開発] タブを表示するようにしておくと便利です。以下の説明図は、Excel 2010 の場合です。2013年05月19日までは Excel 2007 を使用した説明でしたが、変更しました。設定項目の表示方法が違っていますので注意してください。
図5
[開発] タブを表示
[Excel のオプション] の [リボンのユーザ設定] で [開発] にチェック
図6
マクロの記録開始
図7
マクロの設定ダイアログ
図8
記録終了
図9
記録終了したらマクロを表示
図10
マクロの編集画面
コードを見ながらまたはコードをコピーして Delphi で編集
05_ CreateOLEObject 関数で生成可能なオブジェクト
このページのサンプルでは、エクセルのオブジェクトを CreateOLEObject で生成しています。これはマイクロソフトの他の言語では CreateObject に相当しますが、この引数に 'Excel.Application' を指定しています。
この CreateOLEObject の引数は、Delphi のヘルプでは [ClassName は、クラス識別子 (CLSID) の文字列表現を指定します]となっています。この CLSID の文字列表現が [ProgID] で、プログラム ID と呼ばれている文字列です。本サンプルでは 'Excel.Application' です。他のサンプルやネット上のサンプルコードを見ると分かるように、この ProgID はアプリケーションそのものだけではなく、例えばエクセルのシート等も含まれます。
ProgID に指定可能な値は、OLE/COM Object Viewer で確認することができます。これは [タイプライブラリ取り込みの他の方法 ] で説明している Microsoft Windows SDKs をインストールすると使用できるようになります。
VersionIndependentProgID の値が定義されているオブジェクトが、CreateOLEObect, CreateCOMObject 関数で生成可能なオブジェクトです。
// Windows SDK 7.1 + 32 ビット版 Windows
C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\OleView.exe
//Windows SDK 8.1 + 64 ビット版 Windows (32 ビット版 OleView)
C:\Program Files (x86)\Windows Kits\8.1\bin\x86\oleview.exe
//Windows SDK 8.1 + 64 ビット版 Windows (64 ビット版 OleView)
C:\Program Files (x86)\Windows Kits\8.1\bin\x64\oleview.exe
図11
OLE/COM Object Viewer
Microsoft SDKs 7.1 の場合
Windows SDK 8.1 は [スタート] メニューに該当する項目はない
図12
[Object Classes] [All Ojbects] の [Microsoft Office Excel Application]
図13
[Type Libraries] の Excel の [PictureFormat] 項目
どんなプロパティやメソッド類があるかを確認できる
06_ Visual Basic Editor のオブジェクトブラウザ
上の図のような内容は、以下の図の説明のように、VB の Editor でも見ることができます。ここではエクセル 2010 での説明ですが、ワードでも同じです。
Visual Basic あるいは VBA では、この [参照設定] で、操作したいオブジェクトを追加すると、そのオブジェクトがコード内で使用可能となります。
図14
[開発] タブから [Visual Bascic] をクリックして [Visual Basic Editor] を起動
図15
[表示] から [オブジェクトブラウザ] を起動
図16
右クリックのメニューから [参照設定] を選択
図17
例えば、本サイトのサンプルプログラム集でも使用している ADOX をリストから選択
チェックして [OK] をクリック
ダイアログの下には、このライブラリが使用する DLL の名前と場所が表示されている
図18
上のコンボボックスで[ADOX]を選択
左ペインに ADOX のオブジェクト (クラス) リスト。右ペインにメソッドやプロパティ
Create を選択すると下にそのメソッドの引数と簡単な説明
前項で述べた VBA の [参照設定] は、Delphi で言えばタイプライブラリの取り込みや ActiveX コントロールの取り込みに相当します。
Delphi の [コンポーネントのインポート] で表示されるリストは、前項の VBA では [参照可能なライブラリ ファイル] です、これらのファイルは DLL または TLB (タイプライブラリ) ファイルです。
一般に、他のアプリケーションのプロパティや関数類は、別のアプリからは操作できません。COM はこれらを他のプログラム言語から操作可能にするための仕様です。アプリケーションを外部のアプリケーションから操作可能にする仕組みを一般に COM (コム) と言います。
COM はアプリケーション間通信の手段を提供しているとも言えます。
COM を実装したアプリケーションは、CreateOleObject, CreateComObject やタイプライブラリの取り込みでインポートしたファイルを使用して、そのアプリケーションを操作できるようになります。
COM を実装していても、どのような操作が可能かは、そのアプリケーションがどのような COM オブジェクトを、どのようなプロパティやメソッド類を公開しているかによります。
エクセルのブック、シートは COM オブジェクトであり、エクセル自体も COM オブジェクトということになります。また、エクセルは COM を実装した COM アプリということにもなります。
Delphi で作成するアプリケーションにも COM は実装できます。その際、どのような機能、プロパティやメソッドを公開するかは、そのアプリケーションの開発者によります。
COM を実装していないアプリケーションを操作するには、ウィンドウハンドルを取得して、ある程度は操作することが可能です。また、IAccessible インターフェイスや UI Automation 関係インターフェイスを使用する方法もあります。
文字列 (テキスト) だけは特別で、専用のメッセージ類があります。
[備考]
COM オブジェクト (OLE オートメーション、ActiveX コントロール) をオブジェクトのクラス名あるいは、クラス名を識別する CLSID 文字列から生成するには CreateOleObject を使用します。
一方、オブジェクトの GUID 値から COM オブジェクトを生成する時は CreateComObject 関数を使用します。全ての COM オブジェクトが CreateOleObject と CreateComObject のどちらでも生成できるわけではありません。それは、COM オブジェクトを提供する開発者がどのインターフェイスから生成するようにしているかによります。
CLSID の値と ProgID は ComObj.pas 内の関数類を使用して相互変換できます。そのテスト用プログラムです。CLSID または ProgID の値を取得して表示します。
図19
設計時画面
図20
Button1 クリック
図21
Button2 クリック
これが CLSID の文字列表現で、ProgID と呼ばれる値
末尾の 14 は、エクセルのバージョン
この上の図の結果の末尾の 14 は、エクセルのバージョンです。14 は Excel 2010 です。
CreateOleObject の引数として使用する場合、システムにインストールされていないバージョンの値を指定するとエラーとなり、エクセルは起動できません。通常は、このバージョン番号をしないで使用します。指定しても、指定しなくても、現在システムで有効なバージョンが起動します。
システムに複数のバージョンのエクセルがインストールされていても、CLSID の値は全て同じです。つまり、バージョンを指定して、エクセルのオブジェクトを生成することはできません。
本ページの最初のサンプルのように、起動中のエクセルのバージョンとは関係なく、起動中のエクセルに接続することはできます。
リスト5
CLSID と ProgID の相互変換
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses ComObj;
{$R *.dfm}
// =============================================================================
// プログラムIDからCLSIDを取得
// ProgIDToClassID, GUIDToString の使用には uses にComObj が必要
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LGUID : TGUID;
LStr : string ;
begin
LStr := 'Excel.Application' ;
LGUID := ProgIDToClassID(LStr);
// 登録されていれば CLSID が表示される
ShowMessage(GUIDToString(LGUID));
end ;
// =============================================================================
// CLSID からプログラム ID を取得
// ClassIDid-dategID の使用には usesに ComObj が必要
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LGUID : TGUID;
LStr : string ;
begin
LStr := '{00024500-0000-0000-C000-000000000046}' ;
LGUID := StringToGUID(LStr);
// 登録されていれば ProgID が表示される
ShowMessage(ClassIDid-dategID(LGUID));
end ;
end .
最初のサンプルは、いろいろなテスト用のコードがあり長くなっています。次のコードは、ほとんどエクセルの起動と終了だけにしたサンプルです。
起動時にエクセルのオブジェクトを生成し、ブックのオブジェクトも生成します。各種テスト用プロジェクトとして利用できます。
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
FExcelObj : OleVariant;
FWorkBook : OleVariant;
FWorkSheet : OleVariant;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses
ComObj,
Excel2000,
Office2000;
{$R *.dfm}
// =============================================================================
// フォーム表示開始時の処理
//
// (1) エクセルのオブジェクトを生成
// (2) ブックオブジェクトを作成
// (3) シートオブジェクトを取得
// (4) ブックを表示
//
// FExcelObj, FWorkBook, FWorkSheet は全て OleVariant 型の変数
// =============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
FExcelObj := CreateOleObject('Excel.Application' );
FWorkBook := FExcelObj.Workbooks.Add;
FWorkSheet := FWorkBook.WorkSheets[1 ];
FExcelObj.Visible := True;
end ;
// =============================================================================
// フォームを閉じる時の処理
// エクセルも閉じる。保存はしない
//
// 初期化の unAssigned は終了時には不要だが、習慣的に実行した方がよい
// =============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
LBookObj : OleVariant;
LBookNo : Integer;
begin
// エクセルのオブジェクトが生成されているかをチェック
if not VarIsEmpty(FExcelObj) then begin
// テスト用なので警告ダイアログを非表示にする
FExcelObj.Application.DisplayAlerts := False;
// 開いているブックを保存しないで閉じる
for LBookNo := FExcelObj.Workbooks.count downto 1 do begin
LBookObj := FExcelObj.Workbooks[LBookNo];
LBookObj.Saved := True;
LBookObj.Close;
end ;
// ワークブックオブジェクトが生成されているかをチェック
if not VarIsEmpty(FWorkBook) then begin
FWorkBook := unAssigned;
// ワークシートオブジェクトが生成されているかをチェック
if not VarIsEmpty(FWorkSheet) then begin
FWorkSheet := unAssigned;
end ;
end ;
FExcelObj.Quit;
FExcelObj := unAssigned;
end ;
end ;
// =============================================================================
// エクセルの操作例
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
FWorkSheet.Cells[1 ,1 ].Value := 'ABC' ;
FWorkSheet.Cells[1 ,1 ].Interior.ColorIndex := 4 ;
end ;
end .
10_ エクセル操作のテスト用基本コード ( 起動時に指定ブックを開く )
最初のサンプルは、いろいろなテスト用のコードがあり長くなっています。次のコードは、ほとんどエクセルの起動と終了だけにしたサンプルです。
起動時にエクセルのオブジェクトを生成し、指定したブックを開くようにしています。各種テスト用プロジェクトとして利用できます。
リスト7
エクセル操作のテスト用基本コード (起動時に指定ブックを開く)
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormShow(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
FExcelObj : OleVariant;
FWorkBook : OleVariant;
FWorkSheet : OleVariant;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses
ComObj,
Excel2000,
Office2000;
{$R *.dfm}
// =============================================================================
// フォーム表示開始時の処理
//
// (1) エクセルのオブジェクトを生成
// (2) 指定のブックを開いてブックオブジェクトを取得
// (3) シートオブジェクトを取得
// (4) ブックを表示
//
// FExcelObj, FWorkBook, FWorkSheet は全て OleVariant 型の変数
// =============================================================================
procedure TForm1.FormShow(Sender: TObject);
var
LBookFilePath : String ;
begin
LBookFilePath := ExtractFilePath(Application.ExeName) + 'Test.xls' ;
FExcelObj := CreateOleObject('Excel.Application' );
FWorkBook := FExcelObj.Workbooks.Open(LBookFilePath);
FWorkSheet := FWorkBook.WorkSheets[1 ];
FExcelObj.Visible := True;
end ;
// =============================================================================
// フォームを閉じる時の処理
// エクセルも閉じる。保存はしない
//
// 初期化の unAssigned は終了時には不要だが、習慣的に実行した方がよい
// =============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
LBookObj : OleVariant;
LBookNo : Integer;
begin
// エクセルのオブジェクトが生成されているかをチェック
if not VarIsEmpty(FExcelObj) then begin
// テスト用なので警告ダイアログを非表示にする
FExcelObj.Application.DisplayAlerts := False;
// 開いているブックを保存しないで閉じる
for LBookNo := FExcelObj.Workbooks.count downto 1 do begin
LBookObj := FExcelObj.Workbooks[LBookNo];
LBookObj.Saved := True;
LBookObj.Close;
end ;
// ワークブックオブジェクトが生成されているかをチェック
if not VarIsEmpty(FWorkBook) then begin
FWorkBook := unAssigned;
// ワークシートオブジェクトが生成されているかをチェック
if not VarIsEmpty(FWorkSheet) then begin
FWorkSheet := unAssigned;
end ;
end ;
FExcelObj.Quit;
FExcelObj := unAssigned;
end ;
end ;
// =============================================================================
// エクセルの操作例
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
FWorkSheet.Cells[1 ,1 ].Value := 'ABC' ;
FWorkSheet.Cells[1 ,1 ].Interior.ColorIndex := 4 ;
end ;
end .
一般的に、他のアプリケーションの操作は簡単ではありません。
エクセルの場合、Delphi 側で起動したブックを、エクセル側の操作で閉じてしまったり、操作対象のシートを削除してしまうと、そのブックやシートのオブジェクトに対しての操作は例外が発生します。下図はその例です。
エクセル側の操作を検出できれば、対策処理が可能ですが、一般的に、他のアプリの操作を検出するのは困難です。IAccessible インターフェイスや UI Automation 関係インターフェイスを使用すればある程度の検出が可能ですが、かなり面倒です。try - except の例外で処理してしまった方が簡単かも知れません。
対象の他のアプリを必要な時に起動し、操作を終了したらできるだけ早い段階で閉じてしまうのが確実な方法です。その時、各オブジェクトに unAssigned を代入しておけば、VarIsEmpty 関数で、そのオブジェクトが生成されているかを判定できます。
VarIsEmpty 関数は、プログラム内で生成したインスタンスを判定するものです。外部からブック等を閉じると、ブックの実体がなくなり、インスタンスが無効となってしまいます。したがって、それらのオブジェクトに対する操作を行うと、例外が発生することになります。
Windows Vista では、他のアプリへのメッセージ送信、他のアプリからのメッセージの受信等、他のアプリの操作関係の制限が強くなっています。
図22
バリアント演算のエラー
オブジェクトが生成されていない状態で、そのオブジェクトの操作コードを実行した場合等に発生する
図23
OLE エラー
操作対象のオブジェクトが失われてしまった場合等に発生する
エクセルのバージョンを指定してブックを開くことはできません。
これは、CreateOleObject でも、TExcelApplication でエクセルを操作する場合でも同じです。起動できるのは、カレントバージョンのエクセルです。
システムに複数のバージョンのエクセルがインストールされている場合、カレントバージョンを変更すれば、ブックを開く時にそのバージョンのエクセルで開くことができます。しかし、カレントバージョンの変更は、インストール作業が行われます。そのため、操作には時間がかかります。
見かけ上、指定したバージョンでブックを開くことは可能です。レジストリから該当するバージョンの実行ファイルのパス名を取得します。そして、ShellExecute 関数等で起動します。起動したら、起動中のエクセルに接続すれば、そのブックの操作が可能になります。
実際のディスク上のバス名は、環境によって違いがある可能性があります。実行ファイルのパス名は、レジストリから取得するのが確実です。