第III部〜秀丸マクロのいろはにほへと COMオブジェクト操作関連

hidemaru Editor

Hidemaru Q and A

第III部〜秀丸マクロのいろはにほへと
 COMオブジェクト操作関連


●COMオブジェクト操作関連

【0】COMオブジェクトとは
【1】使用前の準備
【2】秀丸マクロからCOMオブジェクトを使ってみる
【3】その他

【0】COMオブジェクトとは

COMとは、Component Object Modelの略です。 Windowsでソフトウェアの再利用を目的とした技術で、アプリケーションソフトウェア間での通信や、 OSとアプリケーションソフトウェアとのAPIに用いられています。

ただし、現状(2019/03/07現在)では、古い技術になっています。 とはいえ、使えなくなったという事ではなく、Word、ExcelなどのOffice製品や、Windows Script Hostなど、 OSの一部の機能には残っていますし、対応しているアプリケーションもまだあります。

秀丸マクロで出来ないことでも、COMオブジェクトを使えば可能になることもあるので、 使い方を覚えておくのも良いでしょう。(いつまで使えるかはわかりませんが)

ここでは、OSに標準搭載されている、「Windows Script Host」等を使って説明を行います。

【1】使用前の準備

まず、使用するCOMオブジェクトの情報を収集します。

今回は以下のCOMオブジェクトを使用し説明を行います。

  1. 「Windows Script Host」の各種オブジェクト
    「Windows Script Host」ですが、以前はchm形式の日本語ヘルプがダウンロードできましたが、 現時点(2017/08/15)ではダウンロード出来なくなっています。

    その為、Microsoftのwebサイトを参照します。

    トップページはMicrosoft Windows スクリプト テクノロジになります。 今回使用するWindows Script Hostを選び、 リファレンスから、 オブジェクトを選びます。

    このページにあるオブジェクトを使用します。

  2. FileSystemObject オブジェクト
    トップページの Microsoft Windows スクリプト テクノロジにある、 Script ランタイム−FileSystemObjectにあります。

  3. Shell オブジェクト
    Shell object (Windows)

各オブジェクトのリンク先には、使用できるプロパティ、メソッドの説明があります。 この情報を元に、秀丸マクロを作成していくことになります。 (逆に言うと、このような情報がない場合には、マクロの作成は出来ません。)

Office製品の場合には、各製品の「VBA リファレンス」が参考になります。 ただし、秀丸マクロは文字と整数しか扱えないので、そのままでは使えない場合もあります。

秀丸マクロ側は「COMの呼び出し」にある、 関数/文 を使うことになります。

プロパティの呼び出し/設定、メソッドで戻り値が、数値、文字、オブジェクトの場合で命令が異なるので、 少し複雑です。また命令が異なるので、使用するCOMオブジェクトの情報がないと、何を使っていいのか、 判断に困まります。

【2】秀丸マクロからCOMオブジェクトを使ってみる

【2.1】「Windows Script Host」の WshShell オブジェクトを使ってみる

WshShell オブジェクト にある、"Popup"メソッドを使ってみます。

秀丸マクロの message文 と同じメッセージを表示する命令ですが、タイムアウト時間を設定でき自動で閉じることが出来ます。
(秀丸エディタ Ver.8.75以降では、message文が拡張され、タイムアウトする事も可能になっています。)

Popupメソッドの引数、戻り値は以下になります。

intButton = object.Popup(strText,[nSecondsToWait],[strTitle],[nType]) 
  • 引数

    object
    WshShell オブジェクトを指定します。
    strText
    表示するメッセージ(文字列)
    nSecondsToWait
    タイムアウト時間。0(zero)だと通常のメッセージBOXと同じで、ユーザーの操作を待ち続ける。(数値)
    strTitle
    タイトルバーに表示する内容(文字列)
    nType
    表示するボタンとアイコンを示す数値(数値)
  • 戻り値

    intButton
    選択されたボタンを示す数値(数値)

WSH(VBScript)の場合は以下になります。

'オブジェクトの生成
Set WshShell = WScript.CreateObject("WScript.Shell")
'メソッドの呼び出し、7秒待機する。
BtnCode = WshShell.Popup("ご機嫌いかがですか?", 7, "お答えください。", 4 + 32)
'結果の表示
Wscript.echo BtnCode
'オブジェクトの解放
Set WshShell = Nothing

これを秀丸マクロに変換していきます。

まず、WshShell オブジェクトを生成するため、createobject 関数を使います。

WSH(VBScript)で実行する場合と違い、秀丸マクロではCOM関連の実行時エラーが出ないので、 メソッドの実行やプロパティアクセス結果のチェックを行う必要があります。 createobject関数は戻り値で判断できますが、基本的には getresultex関数 を使い、成功/失敗を判断します。 (getresultex(10) で、COM関連命令が成功したか、失敗したかを返す)

#WshShell = createobject("WScript.Shell")
if(#WshShell == 0){ //if(getresultex(10) == false){ 
    message "WScript.Shell オブジェクトの生成に失敗しました。";
    endmacro;
}

次に"Popupメソッド"を呼び出します。

呼び出すメソッドの戻り値が、

  • 数値の場合は、"callmethod_returnnum関数"
  • 文字列の場合は、"callmethod_returnstr関数"
  • オブジェクトの場合は、"callmethod_returnobj関数"
  • ない場合は、"callmethod文"

を使います。

プロパティの場合は、取得する/設定する内容により命令を使い分けます。

  • 数値の場合は、"getpropnum関数"、"setpropnum文"
  • 文字列の場合は、"getpropstr関数"、"setpropstr文"
  • オブジェクトの場合は、"getpropobj関数"、"setpropobj文"

Popupメソッドで、戻り値が数値なので、"callmethod_returnnum関数"を使います。

#BtnCode = callmethod_returnnum(#WshShell, "Popup", "ご機嫌いかがですか?", 7, "お答えください。", 4 + 32);
if(getresultex(10) == false){ 
    message "Popupメソッドの呼び出しに失敗しました。";
    endmacro;
}

あとは、結果の表示だけなので、最終的には以下のようなマクロになります。

//オブジェクトの生成
#WshShell = createobject("WScript.Shell");
if(#WshShell == 0){ //if(getresultex(10) == false){ 
    message "WScript.Shell オブジェクトの生成に失敗しました。";
    endmacro;
}
// メソッドの呼び出し、7秒待機する。
#BtnCode = callmethod_returnnum(#WshShell, "Popup", "ご機嫌いかがですか?", 7, "お答えください。", 4 + 32);
if(getresultex(10) == false){ 
    message "Popupメソッドの呼び出しに失敗しました。";
    endmacro;
}
//結果の表示
message str(#BtnCode);
//オブジェクトの解放
releaseobject #WshShell;
endmacro;

エラー検出処理が入っているので、WSH(VBScript)と比較するとマクロが冗長になってしまいますが、 エラー検出をしないと、正常動作しない場合に、どこでエラーになっているのか追いかけるのが大変です。 面倒ですが、ある程度動作確認が出来るまでは、各メソッドの呼び出しやプロパティアクセス毎に、 チェックする事をお勧めします。

また、WSH(VBScript、JScript)をつかって、事前に秀丸マクロで実行したい処理を実行してみるのも良いです。 そもそも、処理方法自体が間違っているのか、秀丸マクロにする時点で間違っているのか、判断の材料になります。

【2.2】"Shell"オブジェクトと、"FileSystemObject"オブジェクトを使ってみる

次に、"Shell"オブジェクトと、"FileSystemObject"オブジェクトを使ってファイルの一覧を新規秀丸エディタに出力します。 "Shell"オブジェクトでフォルダを選択し、"FileSystemObject"オブジェクトでファイル一覧を取得します。 ファイル一覧(コレクション)にアクセスするために、"getcollection"を使っています。

このマクロでは、色々なオブジェクトのプロパティ、メソッドを使っていることから、 戻り値の型に従い、getpropobj、getpropstr、callmethod_returnobj、callmethod_returnstr等を使い分けています。

各メソッド、プロパティの説明に従い適切な命令を使う必要がありますが、どうしてもわからない場合は、 "member文", "member関数"を使う方法もあります。(プロパティは取得する場合のみで、セットすることは出来ない)

// ファイル一覧の取得
newfile;

//shellオブジェクトの作成
//Set WshShell = Wscript.CreateObject("Shell.Application")
//Set getfolder = WshShell.BrowseForFolder(0,"フォルダを選んでください", &h10)
//If (getfolder Is Nothing) Then
//    WScript.Quit
//End If
//Searchfolder = getfolder.Items.Item.path

//shellオブジェクトの作成
#shell = createobject("Shell.Application");
if(#shell == 0){
    message "Shellのオブジェクト作成に失敗";
    endmacro;
}
insert "shellオブジェクトの作成\n";
insert "フォルダ選択ダイアログの表示\n";
#getfolder = callmethod_returnobj(#shell, "BrowseForFolder", 0,"フォルダを選んでください",0x10);
if(#getfolder == 0){
    message "BrowseForFolderが失敗";
    endmacro;
}
#folderitems = callmethod_returnobj(#getfolder, "Items");
if(#folderitems == 0){
    message "#folderitemsが失敗";
    endmacro;
}
#folderitem = callmethod_returnobj(#folderitems, "Item");
if(#folderitem == 0){
    message "#folderitemが失敗";
    endmacro;
}
$Searchfolder = getpropstr(#folderitem, "path");
if(getresultex(10) == false){ 
    message "pathプロパティが失敗";
    endmacro;
}
insert "読み込む場所:" + $Searchfolder + "\n";
releaseobject #shell;

message "出力するファイルの拡張子を入力してください。\n" + 
        "一度に複数の拡張子を指定する場合は\";\"で区切ってください。\n" + 
        "\".\"は入力しないでください。例)txt;log)\nキャンセルで入力を終了します。";
$chk_ext = "";
while(1){
    $ext = input("拡張子を入力してください。キャンセルで終了");
    if(!result) break;
    if($ext == "")break;
    $chk_ext = $chk_ext + tolower($ext) + ";";
}
if(strlen($chk_ext) > 0){
    insert "表示するファイルの種類:" + $chk_ext + "\n";
    $chk_ext = ";" + $chk_ext;
}else{
    insert "表示するファイルの種類:全て\n";
}

//Set WshFso = Wscript.CreateObject("Scripting.FileSystemObject")
//'フォルダオブジェクトの取得
//Set SFolder = WshFso.GetFolder(Searchfolder)
//'ファイルコレクションを取得
//Set SFiles = SFolder.Files
//for each Checkfile in SFile
//    get_ext = WshFso.GetExtensionName(Checkfile.Path)
//next
/
//ファイル一覧の取得
#WshFso = createobject("Scripting.FileSystemObject");
if(#WshFso == 0){ 
    message "#folderitemが失敗";
    endmacro;
}

#Folder = callmethod_returnobj(#WshFso, "GetFolder", $Searchfolder);
if(#Folder == 0){ 
    message "Folderオブジェクトの取得に失敗";
    endmacro;
}
#Files  = getpropobj(#Folder,"Files");
if(getresultex(10) == false){ 
    message "Filesコレクションの取得に失敗";
    endmacro;
}
insert "ファイルの一覧\n";
while(1){
    //filesコレクションから呼び出し
    #File = getcollection(#Files);
    if(#File == 0) break;
    //ファイルオブジェクトから、パスプロパティを読み出し
    $get_filepath = getpropstr(#File, "Path");
    if(getresultex(10) == true){ 
        //拡張子を取得
        $get_ext = callmethod_returnstr(#WshFso, "GetExtensionName", $get_filepath);
        if(getresultex(10) == true){ 
            if(strlen($chk_ext) > 0){
                $get_ext = tolower($get_ext);
                if(strstr($chk_ext, ";" + $get_ext + ";") > -1){
                    insert $get_filepath + "\n";
                }
            }else{
                insert $get_filepath + "\n";
            }
        }
    }
}
#File = getcollection(#Files, 3);
releaseobject #Files;
releaseobject #Folder;
releaseobject #WshFso;

endmacro;

【2.3】COM関連の特殊な命令

allowobjparam 文

秀丸エディタマクロで使用できるのは、数値・文字列だけですが、メソッド・プロパティの戻り値がバイナリデータの場合や、 引数にオブジェクト・バイナリデータを指定する場合もあります。

そのような場合に、allowobjparam 文を使用します。

引数に「1」を指定すると、数値をオブジェクトとして解釈する事を許可します。
引数に「2」を指定すると、文字列をバイト配列(バイナリデータ)として解釈する事を許可します。
引数に「0」を指定すると、「1」、「2」の許可を無効化します。
第二引数以降を指定すると、呼び出すメソッド等のパラメータに対し、対象を個別に指定することが出来ます。

オブジェクトを引数に渡す場合

COMのメソッドやプロパティには、引数にオブジェクトを指定する場合があります。

//ArrayList オブジェクト生成
#array = createobject("System.Collections.ArrayList");
//GetArrayListDataTypeメソッドは、
//第一引数にArrayList オブジェクト
//第二引数に、数値
//を指定する必要がある。
callmethod_returnstr(#lib, "GetArrayListDataType", #array, #n);

第一引数に「#array」を指定して、オブジェクトを指定しているように見えますが、上記マクロは正常に動作しません。

createobject、callmethod_returnobj、getpropobj で、戻り値を数値変数で受け取っていますが、 これは、「オブジェクト識別用の番号」が格納されているだけで、オブジェクト本体ではありません。

つまり、以下の"GetArrayListDataType"メソッド呼び出しは、第一引数に「オブジェクト」を要求しているのに、 「数値」を渡している事になり、失敗します。

callmethod_returnstr(#lib, "GetArrayListDataType", #array, #n);

引数に「オブジェクト」を渡したい場合は、allowobjparam 文 を使い、オブジェクトそのものを渡すように指示します。

使い方は2通りありますが、後者の方法を推奨します。

  • 使い方その1

    allowobjparam 文の引数として、「1」を指定します。

    allowobjparam 1;
    callmethod_returnstr(#lib, "GetArrayListDataType", #array, #n);
    allowobjparam 0;

    この場合、callmethod_returnstr 文が失敗した場合、パラメータの数値部分を、 オブジェクトとして再度実行します。

  • 使い方その2

    対象となるパラメータを指定します。

    allowobjparam 文で、第二引数以降を指定すると、対象になるパラメータを指定できます。 呼び出すメソッドの引数の数に応じて、解釈する方法を順に指定します。引数の意味を変換しない場合は、「0」を指定します。

    オブジェクトはパラメータとして渡すので、第一引数は"0"でもかまいません。 (メソッドの戻り値が、バイト配列の場合、第一引数に"2"を指定する必要があります。)

    "GetArrayListDataType" の場合、引数は2個で、最初はオブジェクトを渡すので「1」を、 次の引数は変換しないので「0」を、第二引数以降に順次指定していきます。

    allowobjparam 0,1,0;
    callmethod_returnstr(#lib, "GetArrayListDataType", #array, #n);
    allowobjparam 0;

変換を有効にした場合は、解除するのを忘れないようにしましょう。

バイト配列を引数に渡したし、戻り値で受け取る場合

戻り値として「バイト配列」を受け取る場合は、第1引数のみ指定します。

//対象のヘッダ部分を読み込み為、ADODBを使用する。
#objStream = createobject( "ADODB.Stream" );
setpropnum #objStream, "Type", 1;//adTypeBinary
callmethod #objStream, "Open";
callmethod #objStream, "LoadFromFile", $target;
//バイナリデータとして扱うように設定
allowobjparam 2;
//先頭から512バイト読み込み
$buff = callmethod_returnstr(#objStream, "Read", 512);

引数として指定する場合は、オブジェクトと同じ方法が使えますが、指定する値は「2」になります。

allowobjparam 0,2;
//出力用ストリームにバイナリで書き込み
callmethod ##Stm_out, "Write", $$bindata;
allowobjparam 0;

【3】その他

「.NET Freamework」のクラス

「.NET Freamework」のクラスも、COMから使える物もあります。

以下のコレクションクラスが使えることが確認されています。

  • ArrayList(System.Collections.ArrayList)
    動的配列
  • Hashtable(System.Collections.Hashtable)
    連想配列
  • SortedList(System.Collections.SortedList)
    キーによってソートされる連想配列
  • Queue(System.Collections.Queue)
    先入れ先出しリスト
  • Stack(System.Collections.Stack)
    後入れ先出しリスト

詳細は、以下のページから各クラスのページを参照してください。
System.Collections 名前空間 "ArrayList"は、動的配列なので、配列サイズは気にしなくても良いし、ソート/逆順ソートも可能になってます。

連想配列は、WSHでも可能で、Dictionary オブジェクト を使います。

COMオブジェクトの有効範囲

createobject文を実行した秀丸エディタ上のみ有効です。秀丸エディタを切り替えた場合は、無効です。 (DLLのロードと同じ。)

次のマクロでは、2回目の"Popup"メソッドは、新規秀丸エディタを開いて呼び出しているため、失敗します。 (新規秀丸エディタ上で、createobjectを実行していないので、"WScript.Shell" オブジェクトは存在してない。)

//オブジェクトの生成
#WshShell = createobject("WScript.Shell");
if(#WshShell == 0){ //if(getresultex(10) == false){ 
    message "WScript.Shell オブジェクトの生成に失敗しました。";
    endmacro;
}
// メソッドの呼び出し、7秒待機する。
#BtnCode = callmethod_returnnum(#WshShell, "Popup", "ご機嫌いかがですか?", 7, "お答えください。", 4 + 32);
if(getresultex(10) == false){ 
    message "Popupメソッドの呼び出しに失敗しました。(1)";
    endmacro;
}
//結果の表示
message str(#BtnCode);

//新規秀丸エディタを開く
newfile;
// メソッドの呼び出し この行は失敗する。
#BtnCode = callmethod_returnnum(#WshShell, "Popup", "ご機嫌いかがですか?", 7, "お答えください。", 4 + 32);
if(getresultex(10) == false){ 
    #err = getresultex(11);
    message "Popupメソッドの呼び出しに失敗しました。(2)\n0x" + hex(#err);
    endmacro;
}

//オブジェクトの解放
releaseobject #WshShell;
endmacro;

DLLの場合と違い、呼び出しに失敗してもマクロの実行は止まらない為、異常には気がつきにくいです。 (メソッドの呼び出し、プロパティアクセス後は、エラーチェックをした方が良いです。)


目次に戻る