Delphi Programming / Object Pascal
[掲載 2013年03月28日] [更新 2013年03月28日] Delphi サンプルプログラム集 |
951_ActiveX オーメーションオブジェクトと VBScript |
動作確認等 |
Windows 7 U64(SP1) + Delphi XE Pro |
|
951_ActiveX_VBScript.zip [805 KB] 2013年03月28日版 (EXE 同梱) |
ActiveX オートメーションオブジェクトと VBScript |
[950_Delphi で VBScript を操作] で,VBScript を実行する際に,値を渡したり取得するテストをしています.
VBScript とのデータのやりとりには,ActiveX オブジェクトを使用する方法も考えられます.ただし,この方法は,やりとりするデータを ActiveX オブジェクトのプロジェクトで,メソッドやプロパティを定義しておく必要があります.手間はかかりますが,いろいろなデータを扱えます.
Delphi のコミュニティの場で,この方法を使用している方の発言がありました.
それは,Malcolm Groves
というサイトの Scripting Your Delphi Applications のページにあったサンプルですが,残念ながら,現在,この元のページは存在しません.サンプルもダウンロードできません.リンクは Internet Archive のものです.
そこで,そのページの記事を元にして同じような動作をするサンプルを作成してみました.変数類等の名前も極力,そのページと同じにしています.
本ページで作成する ActiveX オブジェクトは,ActiveX オートメーションオブジェクトというものですが,この作成手順も掲載することにしました.
ここで作成する ActiveX オブジェクトは,インプロセスサーバとして動作し,VBScript を操作するテストプログラムと同じプロセス内で動作します.
添付のファイル構成は下図のようになっています.ActiveX のプロジェクトは [ExposingObject] に保存しています.プロパティやメソッド類の追加,削除等の編集は,このプロジェクトを起動して行うことになります. |
|
図1
添付のフォルダ構成 |
01_ActiveX オートメーションオブジェクトの作成 |
[ファイル] [新規作成] で [その他] を選択します.下図のダイアログで,[ActiveX]
のノードを選択し,右ペインから [ActiveX ライブラリ] を選択して [OK] ボタンをクリックします.
今回は,Delphi の IDE を起動した後,[ファイル] で [全てを閉じる} で何もファイルを開いてない状態で実行しました.オブジェクト・インスペクタも閉じました. |
[備考]
実際には,[オートメーションオブジェクト] を作成するのですが,まず [ActiveX ライブラリ] を生成して,ActiveX ライブラリの何を作成するのか,という 2 段階の操作となっているようです.[オートメーションオブジェクト] は選択しても [OK] ボタンがクリックできないようになっています.
古い バージョンの Delphi ですと,最初から [オートメーションオブジェクト] の作成ができたようですが,どのバージョンから変わったのかは不明です.
[追記]
IDE を起動した後,[ファイル] [閉じる] だと [オートメーションオブジェクト] が選択できる状態になりました.筆者の環境では,IDE 起動時に [VCL フォームアプリケーション] になっていて,全てを閉じてしまったのが原因のようです. |
|
図2
作成開始 |
|
図3
IDE の状態 |
次に,同じように,[ファイル] [新規作成] [その他] の下図のダイアログで,[オートメーションオブジェクト] を選択し,[OK] ボタンで閉じます. |
|
図4
オブジェクト作成 |
閉じると,下図のダイアログが現れます.ここで CoClass の欄にクラス名を入力します.
ここでは参考記事と同じ,ExposingObjectsDemo としました.実際には任意です.
これで ExposingObjectsDemo という CoClass のインターフェイスができました.ここにプロパティやメソッドを追加していくわけですが,その前にプロジェクトを保存しておくことにします. |
|
図5
実行結果 |
[ファイル] [プロジェクトに名前を付けて保存] を選択します.最初は RIDL ファイルです.タイプライブラリの情報が格納されているファイルです.フォルダ [04_ExposingObjects]
に保存します. |
[備考]
CoClass と同じ名前の ExposingObjectsDemo にするつもりだったのですが,最後の Demo をタイプするのを忘れてしまいました. |
|
図6
RIDL ファイルの保存 |
次は Unit1.pas です.このファイルには,参考記事と同じ uAutoObject という名前を付けることにします.このファイルは,VBScript とデータのやりとりを行うために使用するユニットです.
作成した IExposingObjectsDemo インターフェイスを継承したクラスのユニットです. |
|
図7
Unit1.pas の保存 |
後のファイルは全て,RIDL と同じの ExposingObjects の名前で保存しました.同じにしておいた方がいいでしょう.これらは全て [04_ExposingObjects] のフォルダに保存しました. |
|
図8
XXX_TLB.pas の保存 |
|
図9
dproj ファイルの保存 |
保存すると,左ペインのオブジェクトの名前が,下図のように,
IExposingObjectsDemo
ExposingObjectsDemo
に変わっています.後はメソッドやプロパティを実装するだけです.コードと,下図のデザイン状態との切り替えは,通常のプロジェクトと同じで,[F12] が使えます. |
|
図10
保存結果 |
まずメソッドを実装します.
左ペインの IExposingObjectsDemo を選択し,マウスの右クリックで下図のポップアップメニューを表示します.ここで [メソッド] を選択します. |
|
図11
|
メソッドの名前を SetFormColor とします.左ペインにこのメソッドが追加されます. |
|
図12
|
今度は [パラメータ] タブを表示します.
戻り値はないので,void とします.引数は 1 つだけで OLE_COLOR とします.ActiveX では,MSDN の記事にあるように Windows の型を使用します.修飾子は値をメソッドに渡すだけなので [in] を選択します.複数の引数がある場合は,[追加] ボタンで追加できます.
戻り値は,空白にしておくと,自動的に void になるようです. |
|
図13
|
同じ手順で,SetFormCaption というメソッドを実装します.引数は Caption とし,型は VARIANT とします.VARIANT は,Delphi の OleVariant に相当します.
今回実装するメソッドは,この 2 つだけです. |
|
図14
|
つぎにプロパティを実装します.
最初に Name プロパティを実装します.[プロパティの新規作成] のアイコンの右側の↓をクリックして,[読み取り | 書き込み] を選択します.Name プロパティは読み書き両用です. |
|
図15
|
名前を Name とし,型は VARIANT を選択します.VARIANT は Delphi の OleVariant に相当します.
左ペインに,読み出しと書き込み用のオブジェクトしてのプロパティが 2 つ追加されます. |
|
図16
|
次に Age プロパティを実装します.これも読み書き両用です.名前は Age,型は int とします.
左ペインに読み出し用と書き込み用の Age が追加されます. |
|
図17
|
[実装の変更] アイコンをクリックします.この [実装の変更] によって,これまでの設定内容がコードとして作成または更新されます.
更新したら,プロジェクトを上書き保存します.
これで ActiveX オブジェクトの作成は完了です.Delphi の IDE を閉じます. |
|
図18
|
作成されたファイルは下図のようになっています.
今回は,[実行] [Active サーバ] [登録],実行時引数,ホストアプリケーション等の設定はしませんでした.DLL も作成しません.タイプライブラリをテストプログラムのリソースとして埋め込んで使用することにします.
下図のファイルの内,
ExposingObjects.tlb
ExposingObjects_TLB.pas
uAutoObject.pas
の 3 つを,テストプログラムのフォルダである [07_VBSTestProject] にコピーして使用します. |
|
図19
プロジェクトのファイル
- ExposingObjects.tlb
ExposingObjects_TLB.pas
uAutoObject.pas
のファイルをテストプログラムのフォルダにコピーする
|
以上の操作でできた uAutoObject.pas ユニットの内容です.まだ何も実装されていません.
このユニットを介して,VBScript のコードと Delphi で作成したプログラムで,データのやりとりを行います. |
リスト1
現時点でのユニット uAutoObject.pas の内容 |
unit uAutoObject;
{$WARN SYMBOL_PLATFORM OFF}
interface
uses
ComObj, ActiveX, ExposingObjects_TLB, StdVcl;
type
TExposingObjectsDemo = class(TAutoObject, IExposingObjectsDemo)
protected
procedure SetFormColor(Color: OLE_COLOR); stdcall;
procedure SetFormCaption(Caption: OleVariant); stdcall;
function Get_Age: SYSINT; safecall;
function Get_Name: OleVariant; safecall;
procedure Set_Age(Value: SYSINT); safecall;
procedure Set_Name(Value: OleVariant); safecall;
end;
implementation
uses ComServ;
procedure TExposingObjectsDemo.SetFormColor(Color: OLE_COLOR);
begin
end;
procedure TExposingObjectsDemo.SetFormCaption(Caption: OleVariant);
begin
end;
function TExposingObjectsDemo.Get_Age: SYSINT;
begin
end;
function TExposingObjectsDemo.Get_Name: OleVariant;
begin
end;
procedure TExposingObjectsDemo.Set_Age(Value: SYSINT);
begin
end;
procedure TExposingObjectsDemo.Set_Name(Value: OleVariant);
begin
end;
initialization
TAutoObjectFactory.Create(ComServer, TExposingObjectsDemo, Class_ExposingObjectsDemo,
ciMultiInstance, tmApartment);
end.
05_対象アプリとしての VBScript のコード |
テストで使用する VBScript のコードは次のようにしました.これを HelloTest.vbs という名前でテストプロジェクトのフォルダに保存します.DemoObject は,TExposingObjectsDemo のインスタンスです.実行時に Delphi から TScriptControl の AddObject で設定します. |
Function Hello(Color)
MsgBox "こんにちは " & DemoObject.Name & " さん" & vbCrLf &_
"貴方は " & DemoObject.Age & " 歳ですね"
DemoObject.SetFormColor Color
DemoObject.SetFormCaption InputBox("新しいタイトルの入力")
End Function
テストプログラムのフォルダは,最終的に下図のようになります. |
|
図20
テストプロジェクトのフォルダ内のファイル
- ExposingObjects.tlb
ExposingObjects_TLB.pas
uAutoObject.pas
は ActiveX のプロジェクトからコピーしてきたもの
|
06_VBScript を操作するテストプログラムと Runtime error 217 |
新規にプロジェクトを作成したら,[プロジェクト] で [ソース表示] を選択し,プロジェクトのソースを表示します.ここに次のリストのように,{$R ExposingObjects.tlb} を追加し,タイプライブラリをリソースとして埋め込みます.
これがないと,EXE の実行開始時に Runtime error 217 at XXXXXX というエラーが発生します. |
|
図21
ランタイムエラー |
リスト3
{$R ExposingObjects.tlb} を追加する |
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R ExposingObjects.tlb}
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
サンプルのユニットのコードを次のようにします.
VBScript を使用するので,uses に MSScriptControl_TLB
を追加し,uAutoObject も追加します.
作成した ActiveX がインプロセスで動作するので,VBScript も
TScriptControl.SitehWnd := Self.Handle;
でハンドルをこのテストプログラムのウィンドウ (フォーム) のハンドルとしています.
色の設定に TButtonColor という TeeChart のボタンを利用しています.これは コンポーネントパレットの [TeeChartStd] タブにあります. |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls, TeCanvas, MSScriptControl_TLB, uAutoObject;
type
TForm1 = class(TForm)
LabeledEdit1: TLabeledEdit;
LabeledEdit2: TLabeledEdit;
ButtonColor1: TButtonColor;
Button1: TButton;
Panel1: TPanel;
procedure Button1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{ Private 宣言 }
VBScript : TScriptControl;
FAutoObject : TExposingObjectsDemo;
public
{ Public 宣言 }
function GetNameValue : String;
procedure SetNameValue(AName : String);
function GetAgeValue : Integer;
procedure SetAgeValue(AgeValue : Integer);
procedure ChangeFormColor(AColor : TColor);
procedure ChangeFormCaption(ACaption : String);
end;
var
Form1: TForm1;
implementation
uses ActiveX;
{$R *.dfm}
//=============================================================================
// TScriptControlの生成とコードの生成
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
var
SL : TStringList;
begin
LabeledEdit1.Text := '日本 太郎';
LabeledEdit2.Text := '30';
//VBScriptのオブジェクトを生成
VBScript := TScriptControl.Create(Self);
VBScript.Language := 'VBScript';
VBScript.SitehWnd := Self.Handle;
//TExposingObjectsDemoのインスタンスを生成し,DemoObjectという名前で追加
FAutoObject := TExposingObjectsDemo.Create;
VBScript.AddObject('DemoObject', FAutoObject, False);
//コードを格納するTStringList生成
SL := TStringList.Create;
try
//コードを読込む
SL.LoadFromFile(ExtractFilePath(Application.ExeName) + 'HelloTest.vbs');
//スクリプトにコードを追加
VBScript.AddCode(SL.Text);
finally
FreeAndNil(SL);
end;
end;
//=============================================================================
// 引数を与えてVBScriptを実行
// 引数はバリアント型の配列で与える
//
// PSafeArrayの使用には,usesにActiveXが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
Params : PSafeArray;
V : Variant;
begin
//VBScriptの関数に渡す値をセット
V := VarArrayCreate([0, 0], varVariant);
V[0] := ButtonColor1.SymbolColor;
Params := PSafeArray(TVarData(V).VArray);
//VBSのコード内のHelloという関数を実行
//引数はParamsで渡す
VBScript.Run('Hello', Params);
end;
//=============================================================================
// Panelの色を変更するメソッド
//=============================================================================
procedure TForm1.ChangeFormColor(AColor: TColor);
begin
Panel1.ParentBackground := False;
Panel1.Color := AColor;
end;
//=============================================================================
// フォームのキャプション(タイトル)のテキストを変更するメソッド
//=============================================================================
procedure TForm1.ChangeFormCaption(ACaption: String);
begin
Self.Caption := ACaption;
end;
//=============================================================================
// VBScript内で名前を取得するコードを実行すると実行されるメソッド
// VBScriptの変数に値を返す
//=============================================================================
function TForm1.GetNameValue: String;
begin
Result := LabeledEdit1.Text;
end;
//=============================================================================
// VBScript内で名前をセットすると実行されるメソッド
// 本サンプルでは使用していない
//=============================================================================
procedure TForm1.SetNameValue(AName: String);
begin
LabeledEdit1.Text := AName;
end;
//=============================================================================
// VBScript内で年齢を取得するコードを実行すると実行されるメソッド
// VBScriptの変数に値を返す
//=============================================================================
function TForm1.GetAgeValue: Integer;
begin
Result := StrToIntDef(LabeledEdit2.Text, 0);
end;
//=============================================================================
// VBScript内で年齢をセットすると実行されるメソッド
// 本サンプルでは使用していない
//=============================================================================
procedure TForm1.SetAgeValue(AgeValue: Integer);
begin
LabeledEdit2.Text := IntToStr(AgeValue);
end;
end.
上のコード内の uses 部の uAutoObject.pas を開き,uses 部にテストプログラムのユニット名 unit1 を追加し,処理コードを implementation 部に追加します. |
リスト5
uAutoObject.pas のコード |
unit uAutoObject;
{$WARN SYMBOL_PLATFORM OFF}
interface
uses
ComObj, ActiveX, ExposingObjects_TLB, StdVcl;
type
TExposingObjectsDemo = class(TAutoObject, IExposingObjectsDemo)
protected
procedure SetFormColor(Color: OLE_COLOR); stdcall;
procedure SetFormCaption(Caption: OleVariant); stdcall;
function Get_Age: SYSINT; safecall;
function Get_Name: OleVariant; safecall;
procedure Set_Age(Value: SYSINT); safecall;
procedure Set_Name(Value: OleVariant); safecall;
end;
implementation
uses ComServ, Unit1;
//-----------------------------------------------------------------------------
// VBScript内でSetFormColorメソッドを実行したら,Form1のChangeFormColor
// メソッド実行する
//-----------------------------------------------------------------------------
procedure TExposingObjectsDemo.SetFormColor(Color: OLE_COLOR);
begin
Form1.ChangeFormColor(Color);
end;
//-----------------------------------------------------------------------------
// VBScript内でSetFormCaptionメソッドを実行したら,Form1のChangeFormCaption
// メソッド実行する
//-----------------------------------------------------------------------------
procedure TExposingObjectsDemo.SetFormCaption(Caption: OleVariant);
begin
Form1.ChangeFormCaption(Caption);
end;
//-----------------------------------------------------------------------------
// VBScript内でAgeの値を取得するコードを実行したら,Form1のGetAgeValueメソッド
// 実行して値を取得する
//-----------------------------------------------------------------------------
function TExposingObjectsDemo.Get_Age: SYSINT;
begin
Result := Form1.GetAgeValue;
end;
//-----------------------------------------------------------------------------
// VBScript内でNameの値を取得するコードを実行したら,Form1のGetNameValueメソッ
// ド実行して値を取得する
//-----------------------------------------------------------------------------
function TExposingObjectsDemo.Get_Name: OleVariant;
begin
Result := Form1.GetNameValue;
end;
//-----------------------------------------------------------------------------
// VBScript内でAgeの値をセットしたら,Form1のSetAgetValueメソッドを実行する
// タイプライブラリではAgeの設定メソッドはSet_Age
// 本サンプルでは使用していない
//-----------------------------------------------------------------------------
procedure TExposingObjectsDemo.Set_Age(Value: SYSINT);
begin
Form1.SetAgeValue(Value);
end;
//-----------------------------------------------------------------------------
// VBScript内でNameの値をセットしたら,Form1のSetNameValueメソッドを実行する
// タイプライブラリではNameの設定メソッドはSet_Name
// 本サンプルでは使用していない
//-----------------------------------------------------------------------------
procedure TExposingObjectsDemo.Set_Name(Value: OleVariant);
begin
Form1.SetNameValue(Value);
end;
//-----------------------------------------------------------------------------
initialization
TAutoObjectFactory.Create(ComServer, TExposingObjectsDemo, Class_ExposingObjectsDemo,
ciMultiInstance, tmApartment);
end.
設計時の画面と,実行例の画像です.
こんなことができるというサンプルです.必要であれば,メソッドやプロパティを変更あるいは追加して試してください.
ActiveX オブジェクトのプロジェクトを更新したら,前に述べたファイルをテストプログラムにコピーします.この作業が面倒な場合は,ActiveX のプロジェクトと作成するアプリのプロジェクトを同じフォルダに置きます.
いずれにしても,作成するアプリのプロジェクトを [再構築] または [ビルド] します. |
|
図22
設計時画面 |
|
図23
実行開始時の画面
- [パネルの色] をクリックして色を変更しておく (初期値は黒)
- この色の値を VBScript 内の関数の引数に渡し,VBScript 側から Form1 の Panel1 の色を変更するようになっている
|
|
図24
[VBScript 実行] をクリック
- テストプログラムの 名前と年齢の値が表示されている
- [OK] をクリックすると...
|
|
図25
入力ダイアログ
- このダイアログに何か文字列を入力して [OK} をクリックすると...
|
|
図26
Form1 のキャプションが変わる |
|