2018年5月3日木曜日

TokyoでJson形式のファイルの読み書きをして見た。

唐突ですが、Json.NET良いですよね。高度なこともできますが、Json形式の文字列のシリアル化/デシリアライズ化がだけで良いな簡単にできますしね。
Delphi(Tokyo)でJson形式のファイルを読み込む必要が出てきたので、Json.NETのような書き方ができなか調べていたところもう1年くらい前の記事になりますが、

@lynatan さんのTJsonSerializerの記事

TJsonSerializerの使い方。
TJsonSerializerの実用例

エンバカデロさんのブログ

TJsonSerializerでJSONに変換する[JAPAN]

DelphiでもTokyoになって、Json.NETのような書き方ができるようになっているとのことでしたので、試して見ました。

読み書きするのは、以下のような内容のファイル

{"ShopName":"ラビットハウス",
  "Clerks":[
    {"Name":"ココア","Age":17},
    {"Name":"チノ","Age":15},
    {"Name":"リゼ","Age":17}
  ]
}

上記の構造にマップできるクラスを作成します。
先ずは、従業員(Clerk)クラス。
パブリックメンバーをシリアライズ対象のするのでクラスに[JsonSerialize(TJsonMemberSerialization.&Public)]
属性を付加しています。
(余談ですが、予約語、指令と被るワードを使う場合、その前に"&"が必要です。)

unit Unit1;
  //パブリックメンバーをシリアライズ対象にする属性
  [JsonSerialize(TJsonMemberSerialization.&Public)]
  TClerk = class
    private
      FName: string;
      FAge: integer;

      procedure SetAge(const Value: integer);
      procedure SetName(const Value: string);
    public
      property Name : string read FName write SetName;
      property Age : integer read FAge write SetAge;
      constructor Create; overload;
      constructor Create(const vName : string; const vAge : integer); overload;
  end;

次に喫茶店(CoffeeShop)クラスの定義
  [JsonSerialize(TJsonMemberSerialization.&Public)]
  TCoffeeShop = class
    private
    FClerks: TList<TClerk>;
    FShopName: string;
    procedure SetClerks(const Value: TList<TClerk>);
    procedure SetShopName(const Value: string);
    public
      property ShopName : string read FShopName write SetShopName;
      
      //TClerkクラスのジェネリックリスト用のコンバーターを登録
      [JsonConverter(TJsonClerkListConverter)]
      property Clerks : TList<TClerk> read FClerks write SetClerks;

      public constructor Create;
  end;

メンバーは、喫茶店名と、従業員のリスト(ジェネリックのリスト)です。
こちらも、パブリックメンバーをシリアル化の対象とします。
ジェネリックのリストは、そのままではシリアライズできないので、Json.Converterユニットに定義済みの TJsonListConverterからTClerk型用の派生クラス

  //TClerk型のリスト用のコンバーター
  TJsonClerkListConverter = class(TJsonListConverter<TClerk>);

を作成し、TClerk型のジェネリックリスト型のメンバーClerks用のコンバーター属性を付加しています。

クラス定義の全体は、以下とおりです。
unit Unit2;

interface
uses
  //Json.SerializersとConverterを使用する。
    System.JSON.Serializers
  , System.JSON.Converters
  //TList<t>を使用する
  , System.Generics.Collections
  ;

type

  //パブリックメンバーをシリアライズ対象にする属性
  [JsonSerialize(TJsonMemberSerialization.&Public)]
  TClerk = class
    private
      FName: string;
      FAge: integer;

      procedure SetAge(const Value: integer);
      procedure SetName(const Value: string);
    public
      property Name : string read FName write SetName;
      property Age : integer read FAge write SetAge;
      constructor Create; overload;
      constructor Create(const vName : string; const vAge : integer); overload;
  end;

  //TClerk型のリスト用のコンバーター
  TJsonClerkListConverter = class(TJsonListConverter<TClerk>;);

  [JsonSerialize(TJsonMemberSerialization.&Public)]
  TCoffeeShop = class
    private
    FClerks: TList<TClerk>;
    FShopName: string;
    procedure SetClerks(const Value: TList<TClerk>);
    procedure SetShopName(const Value: string);
    public
      property ShopName : string read FShopName write SetShopName;
      
      //TClerkクラスのジェネリックリスト用のコンバーターを登録
      [JsonConverter(TJsonClerkListConverter)]
      property Clerks : TList<TClerk> read FClerks write SetClerks;

      public constructor Create;
  end;
implementation

{ TClerk }

constructor TClerk.Create(const vName: string; const vAge: integer);
begin
   FName := vName;
   FAge := vAge;
end;

constructor TClerk.Create;
begin

end;

procedure TClerk.SetAge(const Value: integer);
begin
  FAge := Value;
end;

procedure TClerk.SetName(const Value: string);
begin
  FName := Value;
end;

{ TCoffeeShop }

constructor TCoffeeShop.Create;
begin
  FClerks := TList<TClerk>.Create;
end;

procedure TCoffeeShop.SetClerks(const Value: TList<TClerk>);
begin
  FClerks := Value;
end;

procedure TCoffeeShop.SetShopName(const Value: string);
begin
  FShopName := Value;
end;

end.

上記で定義したクラスに対で、冒頭で示した内容のJson形式の定義ファイルCoffeeShop.Jsonを読み込み、デシリアライズする処理は、以下のとおりとなります。

procedure TForm1.Button2Click(Sender: TObject);
var
  CoffeeShop : TCoffeeShop;
  serializer: TJsonSerializer;
  s : string;
  Clerk : TClerk;
begin

  s := TFile.ReadAllText('CoffeeShop.Json',TEncoding.UTF8);

  serializer := TJsonSerializer.Create;
  try
    CoffeeShop := serializer.Deserialize<TCoffeeShop>(s);
    try
      Memo1.Clear;
      Memo1.Lines.Add(CoffeeShop.ShopName);
      for Clerk in CoffeeShop.Clerks do
      begin
        Memo1.Lines.Add(Clerk.Name + '(' + Clerk.Age.ToString() + ')');
      end;
    finally
      CoffeeShop.Clerks.Clear();
      CoffeeShop.Free;
    end;
  finally
    serializer.Free
  end;

ファイルを読み込み、シリアライザーを生成し、読み込んだJson文字列をデシリアライズして、ショップ名と、店員の情報をメモに表示しています。 (余談ですが、クラス定義で属性を使用しないで、TJsonSerializerインスタンスのConverterリストにTJsonClerkListConverterのインスタンスを登録してもデシリアライズできます。)

冒頭で示した内容のJson形式の定義ファイルを作成する場合は、以下の処理でできます。

procedure TForm1.Button1Click(Sender: TObject);
var
  CoffeeShop : TCoffeeShop;
  serializer: TJsonSerializer;
  s : string;


begin
  CoffeeShop := TCoffeeShop.Create;
  try
    CoffeeShop.ShopName := 'ラビットハウス';
    CoffeeShop.Clerks.Add(TClerk.Create('ココア',17));
    CoffeeShop.Clerks.Add(TClerk.Create('チノ',15));
    CoffeeShop.Clerks.Add(TClerk.Create('リゼ',17));

    serializer := TJsonSerializer.Create;
    try

      s := serializer.Serialize(CoffeeShop);
      Memo1.Text := s;

      TFile.WriteAllText('CoffeeShop.Json',s);

    finally
      serializer.Free;
    end;

  finally
    CoffeeShop.Clerks.Clear();
    CoffeeShop.Free;
  end;
end;

TCoffeeShop型のインスタンスを生成し、メンバーを設定後、シリアライザーを生成しシリアライズ後、ファイルに保存しています。
(確認の為、画面上のメモにも表示しています。)


プロジェクト一式は、https://bitbucket.org/OldTPFun/delphitest/src/master/JsonTest/Proj1/
に配置しております。

2014年1月25日土曜日

GExpertsのMove to Matching Delimiter 機能

数日前、@_fm2さんが、ツイッターで

"DelphiのIDEで  begin 対する end を検索する、ショートカットなんぞはありませんか?"

とのつぶやかれてまた。

これ、IDEの標準機能では、出来なさそうだけど、GExperts のEditor Exports機能で
できます。

GExperts自体は、有名なツールなので、ご存知の方も多いと思います。

先ずは、GExpertsのインストール。

XE4までは、GExperts のサイトにインストーラがあるので、インストラーを使って
セットアップできます。

XE5は、残念ながら、インストーラがないので手動インストールが必要です。
インストール方法は、以下のとおりです。(Delphi XE5が必要です。)

1) SourceForgeのリポジトリからソースを入手します。

2) XE5用のプロジェクトを開きビルドします。
    ビルドすると、GExpertsRSXE5.dllができます。

3) レジストリ登録を行います。
 レジストリエディターで、

 HKEY_CURRENT_USER\Software\Embarcadero\BDS\12.0\Experts\

 に、文字列キーGExpertsを作成し、値にGExpertsRSXE5.dllをフルパスで
 設定します。

インストールに成功すれば、Delphi ( RadStudio )を起動するとメニューに
GExpertsが表示されます。

この状態で、Delphiのソースコードを開き"begin"のところにカーソルをあてて
[CTRL] + [ALT] + [右矢印キー]を押すと、対応する"end"に移動します。
この位置で、もう一回[CTRL] + [ALT] + [右矢印キー]を押すと、"begin"に
戻ります。(このショートカットキーは変更可能です。)

GExpertsのメニューから、Configurationを選択し、設定用のダイアログを開き、
Editor Expertsタブを選択し、Move to Matching Delimiterのところにカーソルを
あてると、機能の説明が確認できます。
説明には、

"  This expert enables you to quickly move to a matching beginning/ending delimiter for the following Delphi tokens: begin, try, case, repeat, for, with, while, asm, do, if, then, else, class, record, array, interface, implementation, uses, private, protected, public, published, until, end, try, finally, except, (/), and [/], .
  It also supports the following C++ tokens: {/}, (/), and [/]
The following steps are taken to match delimiters:
 - Beginning at the current cursor position, the expert looks to the left on the current line for a delimiter token such as "begin".
 - If a delimiter token is found, the expert scans for the matching token, such as "end" in the above instance.
 - If no delimiter is found to the left, as in the case "if|(i=0)" (where '|' represents the cursor), the expert attempts to find a delimiter token to the right of the current cursor position. In the mentioned example, this will identify the opening parenthesis, and an attempt is made to locate the closing parenthesis. "

とありますので、指定したトークンとペアになるトークンがあれば、その位置に
移動するようです。

試しに、try 〜 finally 〜 end で試した場合、finallyの位置でこの機能を使用した
場合の移動先は状況に依存するようでした。
(tryからはfinallyに、endからはfinallyにそれぞれ移動します。)

ソースも公開されてますので、GX_eFindDelimiter.pas等を書き換えれば
動きのカスタマイズも可能かと思います。










2012年12月13日木曜日

publishedなメソッドを隠す(ネストした型宣言使用編)

前回の続きです。

さて、最近のDelphiでは、ネストした型宣言が可能で、クラス宣言の中にクラスの宣言ができます。

この機能を使って、クラスの使用者からpublishedなメソッドを隠すことを試してみました。

以下、ソースです。

先ず、 【関数名でメソッドが呼び出されるクラスのサンプル】

unit Unit2;

interface

  type TMyCallCalc = class
    public
      function CallCalc(CalcName : string; a, b: double) : double;
    private
      type TMyCalc = class
        published
          function Add(a,b : double) : double;
          function Subtract(a,b : double) : double;
      end;

  end;


implementation
{ TMyCalc }

function TMyCallCalc.TMyCalc.Add(a, b: double): double;
begin
  Result := a + b;
end;

function TMyCallCalc.TMyCalc.Subtract(a, b: double): double;
begin
  Result := a - b;
end;


type TMyCalcFunc = function(a,b : double) : double of object;

function TMyCallCalc.CallCalc(CalcName : string; a, b: double) : double;
var
  MyCalc     : TMyCalc;
  MyCalcFunc : TMyCalcFunc;
  MethodVar : TMethod;
begin

  MyCalc := TMyCalc.Create;
  try
    MethodVar.Data := MyCalc;
    MethodVar.Code := MyCalc.MethodAddress(CalcName);


    if Assigned(MethodVar.Code) then
    begin
      MyCalcFunc := TMyCalcFunc(MethodVar);
      Result := MyCalcFunc(a,b);
    end;
  finally
    MyCalc.Free;
  end;

end;

end.

親クラスのprivateセクションにpublishedなメソッドを持つ子クラスを宣言しています。
これで、ユニットの使用者からは、子クラスのメソッドの宣言が見えなくなり、直接呼び出す
ことができなくなります。
ユニットの使用者には、publicセクションにメソッドを宣言することで、間接的に目的の
メソッドが呼び出せるようにします。

次に上記のクラスを使用するコード


unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    LabeledEdit1: TLabeledEdit;
    LabeledEdit2: TLabeledEdit;
    StaticText1: TStaticText;
    StaticText2: TStaticText;
    procedure OnCalcBtnClick(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses Unit2;

procedure TForm1.OnCalcBtnClick(Sender: TObject);
var
  CallCalc : TMyCallCalc;
begin
  CallCalc := TMyCallCalc.Create;
  try
    StaticText2.Caption := FloatToStr(CallCalc.CallCalc(
                                    TButton(Sender).Caption,
                                    StrToFloat(LabeledEdit1.Text),
                                    StrToFloat(LabeledEdit2.Text)));
  finally
    CallCalc.Free;
  end;

end;

end.

ユニットを使用する側からは子クラスが見えませんので、親クラスのpublicなメソッドのみが
使用できます。



publishedなメソッドを隠す

前回の続きです。

TObjectのMethodAddressメソッドを使用すれば関数名(文字列)でメソッドをコールできます。

しかし、 MethodAddressメソッドでメソッドのアドレスを取得するには、可視性をpublishedに
する必要があり、 publishedにした瞬間にメソッドの存在が丸わかりになってしまいます。

前回の例のような単純な演算であればまあ良いかと思うのですが、いわゆるFactoryメソッド
とかだと、使用者にその存在を隠したい場合があり、このままではちょっと不完全です。

さて、どうしようか?というのが今回のテーマになります。(2010以降の拡張Rttiを使えば良い
というのはあるのですが、今回は別の方法を考えます。)

ところで、DelphiのUnitのimplementationセクションで定義した型(クラスを含む)は別のユニット
から参照できない仕様となっています。

したがって、クラスの定義を  implementation で行えば、外部から処理の存在を隠したうえで
 MethodAddressメソッド が使用可能なクラスができそうです。

で、実際に、作ってみました。

先ずは、【メソッドポインタ経由で呼び出されるクラスのサンプル】

unit Unit2;

interface

  function CallCalc(CalcName : string; a, b: double) : double;

implementation

  type TMyCalc = class
  published
    function Add(a,b : double) : double;
    function Subtract(a,b : double) : double;
  end;

  type TMyCalcFunc = function(a,b : double) : double of object;

{ TMyCalc }

function TMyCalc.Add(a, b: double): double;
begin
  Result := a + b;
end;

function TMyCalc.Subtract(a, b: double): double;
begin
  Result := a - b;
end;


function CallCalc(CalcName : string; a, b: double) : double;
var
  MyCalc     : TMyCalc;
  MyCalcFunc : TMyCalcFunc;
  MethodVar : TMethod;
begin

  MyCalc := TMyCalc.Create;
  try
    MethodVar.Data := MyCalc;
    MethodVar.Code := MyCalc.MethodAddress(CalcName);


    if Assigned(MethodVar.Code) then
    begin
      MyCalcFunc := TMyCalcFunc(MethodVar);
      Result := MyCalcFunc(a,b);
    end;
  finally
    MyCalc.Free;
  end;

end;

end.

publishedの可視性を持つクラスをimplementationセクションに定義しています。
但し、そのままでは、外部のユニットからメソッドをコールできませんので、
外部からのアクセスようにラッパー関数を定義しています。

次に、【関数名を文字列で指定して処理を呼び出すサンプル】

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    LabeledEdit1: TLabeledEdit;
    LabeledEdit2: TLabeledEdit;
    StaticText1: TStaticText;
    StaticText2: TStaticText;
    procedure OnCalcBtnClick(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses Unit2;

procedure TForm1.OnCalcBtnClick(Sender: TObject);
begin
  StaticText2.Caption := FloatToStr(CallCalc(
                                    TButton(Sender).Caption,
                                    StrToFloat(LabeledEdit1.Text),
                                    StrToFloat(LabeledEdit2.Text)));
end;

end.

unit2のinterfaceセクションに定義したラッパーを使って処理を呼び出しています。


implementationセクションにクラスを定義することで、外部からその存在を隠しつつ
メソッド名を文字列で指定してメソッドをコールすることが可能です。

但し、この方法ですとクラスそのものも隠れてしまいすのでもう少しなんとかしたい
ところです。

ネストした型宣言が可能なバージョンのDelphiであれば、何とかできそうな気が
しますが、その検証はまた後日・・・

2012年12月12日水曜日

メソッドを名前で呼び出す

前回の続きです。

関数(function)のメソッドポインタが定義できれば、TObjectのMethodAddressを使って
関数名を(文字で)指定してコールすることが可能です。

以下、サンプルソース。

先ずは、【メソッドポインタ経由で呼び出されるクラスのサンプル】

unit Unit2;

interface

  type TMyCalc = class
  published
    function Add(a,b : double) : double;
    function Subtract(a,b : double) : double;
  end;

implementation

{ TMyCalc }

function TMyCalc.Add(a, b: double): double;
begin
  Result := a + b;
end;

function TMyCalc.Subtract(a, b: double): double;
begin
  Result := a - b;
end;

end.

MethodAddressを使用するためにpublishedを指定してることに注意願います。

次に、【関数名を文字列で指定して処理を呼び出すサンプル】

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    LabeledEdit1: TLabeledEdit;
    LabeledEdit2: TLabeledEdit;
    StaticText1: TStaticText;
    StaticText2: TStaticText;
    procedure OnCalcBtnClick(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses Unit2;

type TMyCalcFunc = function(a,b : double) : double of object;

procedure TForm1.OnCalcBtnClick(Sender: TObject);
var
  MyCalc     : TMyCalc;
  MyCalcFunc : TMyCalcFunc;
  MethodVar : TMethod;
begin

  MyCalc := TMyCalc.Create;
  try
    MethodVar.Data := MyCalc;
    MethodVar.Code := MyCalc.MethodAddress(TButton(Sender).Caption);


    if Assigned(MethodVar.Code) then
    begin
      MyCalcFunc := TMyCalcFunc(MethodVar);
      StaticText2.Caption := FloatToStr(MyCalcFunc(StrToFloat(LabeledEdit1.Text),StrToFloat(LabeledEdit2.Text)));
    end;
  finally
    MyCalc.Free;
  end;

end;

end.

ボタンのキャプション名が名前のメソッドが定義されているという前提で、ボタンのキャプションを
使って関数をコールして、結果を求めています。


関数のメソッドポインタ

メソッドポインタでfunctionを使った例のサンプルです。
あまり見当たらなかったので、備忘録がわりに公開しときます。


【メソッドポインタ経由でfunctionを呼び出すサンプル】


unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    LabeledEdit1: TLabeledEdit;
    LabeledEdit2: TLabeledEdit;
    StaticText1: TStaticText;
    StaticText2: TStaticText;
    procedure OnCalcBtnClick(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses Unit2;

type TMyCalcFunc = function(a,b : double) : double of object;

procedure TForm1.OnCalcBtnClick(Sender: TObject);
var
  MyCalc     : TMyCalc;
  MyCalcFunc : TMyCalcFunc;
  Method: TMethod;
begin

  MyCalc := TMyCalc.Create;
  try
    //Method.Data := MyCalc;
    if TButton(Sender).Caption = 'Add' then
    begin
      MyCalcFunc := MyCalc.Add;
    end
    else
    begin
      MyCalcFunc := MyCalc.Subtract;
    end;
    StaticText2.Caption := FloatToStr(MyCalcFunc(StrToFloat(LabeledEdit1.Text),StrToFloat(LabeledEdit2.Text)));
  finally
    MyCalc.Free;
  end;

end;

end.


【メソッドポインタ経由で呼び出されるクラスのサンプル】


unit Unit2;

interface

  type TMyCalc = class
    function Add(a,b : double) : double;
    function Subtract(a,b : double) : double;
  end;

implementation

{ TMyCalc }

function TMyCalc.Add(a, b: double): double;
begin
  Result := a + b;
end;

function TMyCalc.Subtract(a, b: double): double;
begin
  Result := a - b;
end;

end.


メソッドポインタはヘルプにもあるように、

Type 型名 = "メソッドの定義" of object

のように宣言します。

ここで、"メソッドの定義"は、通常の手続き(関数)の名前を抜いたものになるので、
例えば、TObject型のSenderを引数に持ち、戻り値がない"メソッドの定義"は、

procedure(Sender : TObject)

同様に、Double型のaとbを引数に持ち、Double型の戻り値がある"メソッドの定義"は

function(a,b : Double) : Double

となります。

上記のサンプル1では、ボタンのキャプションに応じてif分で関数を切り替えています。

この例では、関数が2つだけなので、メソッドポインタ経由ではなく、直接目的の関数を
コールしたほうが良いのですが、
同じ型の引数を持つ関数が多数ある場合、メソッドポインタと、TObject.MethodAddressを
組み合わるとコード量を減らせる可能性があります。(上の例だとボタンのCaptionと関数名を
合わせおくことによりif文(あるいは、Case文)を無くすことが可能です。)

そのへんの話はまた後日・・・




2012年11月10日土曜日

TZipFile.ZipDirectoryContentsのCOMラッパー

仕事で、VB6でフォルダーを圧縮する必要が出てきて、どうしようかと迷ってた時
DEKOさんが以前フォルダごと圧縮してZipファイルを作成するデモを紹介してたのを
思い出したのでCOM化してVB6,VBAから呼び出せるようにしてみた。

以下、ソース


unit DirZipImp;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
  ComObj, ActiveX, SimpleDirZip_TLB, StdVcl;

type
  TDirZip = class(TAutoObject, IDirZip)
  protected
    procedure Compress(const SrcDirName, ZipFileName: WideString); safecall;
    procedure Extract(const ZipFileName, DestDirName: WideString); safecall;

  end;

implementation

uses ComServ,System.Zip;

procedure TDirZip.Compress(const SrcDirName, ZipFileName: WideString);
begin
  TZipFile.ZipDirectoryContents(ZipFileName,SrcDirName);
end;

procedure TDirZip.Extract(const ZipFileName, DestDirName: WideString);
begin
  TZipFile.ExtractZipFile(ZipFileName,DestDirName);
end;

initialization
  TAutoObjectFactory.Create(ComServer, TDirZip, Class_DirZip,
    ciMultiInstance, tmApartment);
end.



Delphiが作成したタイプライブラリーは
// ************************************************************************ //
// 警告
// -------
// このファイルはタイプ ライブラリ インポータまたはタイプ ライブラリ エディタで生成されています。
// 構文エラーがない場合には、エディタはファイルへの変更を構文解析します。
// ただし、エディタで変更したときは、このファイルは再生成され、
// コメントやフォーマットの変更は失われます。
// ************************************************************************ //
// 2012/11/10 20:48:27 に生成されたファイル (- $Rev: 12980 $, 52614628)

[
  uuid(A77B985B-35E5-49A6-91CB-5BCD38D54C22),
  version(1.0)

]
library SimpleDirZip
{

  importlib("stdole2.tlb");

  interface IDirZip;
  coclass DirZip;


  [
    uuid(2CCEF7D7-0EF5-467E-BFEC-F4574049C00D),
    helpstring("DirZip Object のディスパッチ インターフェイス"),
    dual,
    oleautomation
  ]
  interface IDirZip: IDispatch
  {
    [id(0x000000C9)]
    HRESULT _stdcall Compress([in] BSTR SrcDirName, [in] BSTR ZipFileName);
    [id(0x000000CA)]
    HRESULT _stdcall Extract([in] BSTR ZipFileName, [in] BSTR DestDirName);
  };

  [
    uuid(2B59D5EC-076B-4577-8C09-F3968AEFF615),
    helpstring("DirZip Object")
  ]
  coclass DirZip
  {
    [default] interface IDirZip;
  };

};

unit SimpleDirZip_TLB;

// ************************************************************************ //
// 警告
// -------
// このファイルで宣言されている型はタイプ ライブラリから読み取られたデータから
// 生成されています。このタイプ ライブラリが明示的あるいは(このタイプ ライブラ
// リを参照しているほかのタイプ ライブラリ経由で)間接的に再インポートされた
// り、タイプ ライブライブラリの編集中にタイプ ライブラリ エディタの[更新]コマ
// ンドを実行した場合、このファイルの内容はすべて再生成され、手動で加えた変更
// はすべて失われます。
// ************************************************************************ //

// $Rev: 52393 $
// 下に説明されたタイプ ライブラリから 2012/11/10 20:48:24 に生成されたファイル。

// ************************************************************************  //
// タイプ ライブラリ: C:\Users\saka_xps\Documents\Saka_Develop\DelProj\SimpleDirZip (1)
// LIBID: {A77B985B-35E5-49A6-91CB-5BCD38D54C22}
// LCID: 0
// ヘルプファイル:
// ヘルプ文字列:
// 依存関係リスト:
//   (1) v2.0 stdole, (C:\Windows\SysWOW64\stdole2.tlb)
// SYS_KIND: SYS_WIN32
// ************************************************************************ //
{$TYPEDADDRESS OFF} // ポインタの型チェックをオフにしてコンパイルすること
{$WARN SYMBOL_PLATFORM OFF}
{$WRITEABLECONST ON}
{$VARPROPSETTER ON}
{$ALIGN 4}

interface

uses Winapi.Windows, System.Classes, System.Variants, System.Win.StdVCL, Vcl.Graphics, Vcl.OleServer, Winapi.ActiveX;

// *********************************************************************//
// タイプ ライブラリで宣言される GUID. 以下のプレフィックスを使う:
//   Type Libraries     : LIBID_xxxx
//   CoClasses          : CLASS_xxxx
//   DISPInterfaces     : DIID_xxxx
//   Non-DISP interfaces: IID_xxxx
// *********************************************************************//
const
  // タイプ ライブラリのメジャー バージョンとマイナー バージョン
  SimpleDirZipMajorVersion = 1;
  SimpleDirZipMinorVersion = 0;

  LIBID_SimpleDirZip: TGUID = '{A77B985B-35E5-49A6-91CB-5BCD38D54C22}';

  IID_IDirZip: TGUID = '{2CCEF7D7-0EF5-467E-BFEC-F4574049C00D}';
  CLASS_DirZip: TGUID = '{2B59D5EC-076B-4577-8C09-F3968AEFF615}';
type
// *********************************************************************//
// タイプ ライブラリで宣言される前方参照
// *********************************************************************//
  IDirZip = interface;
  IDirZipDisp = dispinterface;
// *********************************************************************//
// タイプ ライブラリで宣言される CoClass
// (注意: ここで各 CoClass とデフォルトのインターフェイスをマッピングする)
// *********************************************************************//
  DirZip = IDirZip;


// *********************************************************************//
// インターフェイス: IDirZip
// フラグ: (4416) Dual OleAutomation Dispatchable
// GUID: {2CCEF7D7-0EF5-467E-BFEC-F4574049C00D}
// *********************************************************************//
  IDirZip = interface(IDispatch)
    ['{2CCEF7D7-0EF5-467E-BFEC-F4574049C00D}']
    procedure Compress(const SrcDirName: WideString; const ZipFileName: WideString); safecall;
    procedure Extract(const ZipFileName: WideString; const DestDirName: WideString); safecall;
  end;

// *********************************************************************//
// DispIntf:  IDirZipDisp
// フラグ:     (4416) Dual OleAutomation Dispatchable
// GUID:      {2CCEF7D7-0EF5-467E-BFEC-F4574049C00D}
// *********************************************************************//
  IDirZipDisp = dispinterface
    ['{2CCEF7D7-0EF5-467E-BFEC-F4574049C00D}']
    procedure Compress(const SrcDirName: WideString; const ZipFileName: WideString); dispid 201;
    procedure Extract(const ZipFileName: WideString; const DestDirName: WideString); dispid 202;
  end;

// *********************************************************************//
// クラス DirZip は、Create および CreateRemote メソッドを使用して
// CoClass DirZip が公開するデフォルトのインターフェイス IDirZip の
// インスタンスを作成する。このタイプ ライブラリのサーバーによって
// 公開された CoClass オブジェクトに対し、オートメーションを行いたい
// クライアントが用いるために、これらの関数が存在する。
// *********************************************************************//
  CoDirZip = class
    class function Create: IDirZip;
    class function CreateRemote(const MachineName: string): IDirZip;
  end;

implementation

uses System.Win.ComObj;

class function CoDirZip.Create: IDirZip;
begin
  Result := CreateComObject(CLASS_DirZip) as IDirZip;
end;

class function CoDirZip.CreateRemote(const MachineName: string): IDirZip;
begin
  Result := CreateRemoteComObject(MachineName, CLASS_DirZip) as IDirZip;
end;

end.

VB6,VBAの呼び出しのサンプルは、


Private Sub CommandButton1_Click()
    Dim DirZip As SimpleDirZip.DirZip
    
    Set DirZip = New SimpleDirZip.DirZip
    
    Call DirZip.Compress("C:\Users\ppp", "C:\Users\pp\ppp.zip")    
    Set DirZip = Nothing
    
End Sub

Private Sub CommandButton2_Click()
    
    Dim DirZip As SimpleDirZip.DirZip
    
    Set DirZip = New SimpleDirZip.DirZip
    
    Call DirZip.Extract("C:\Users\pp\ppp.zip", "C:\Users\pp\p")
    
    Set DirZip = Nothing

End Sub

C#とかでComを作るより簡単かも。

ところで、タイプライブラリでインターフェイスのメソッド名を編集時にメソッド名が全部消せないのは
仕様なんかしら・・・。
とりあえず2007では消せたんだけど・・・・。