マクロ覚え書き(開発者向け)

提供: MeryWiki
2025年1月5日 (日) 17:21時点におけるMSY-07 (トーク | 投稿記録)による版 (ソースコードの整形)
ナビゲーションに移動 検索に移動

マクロ開発者向けの覚え書きです。

リファレンスなどに載っていない、豆知識的なことを集めています。

開発言語について

WSH (Windows Script Host) が対応している言語であれば利用が可能です。

標準では JScript(≒JavaScript)と VBScript が入っています。

他の言語(Perl や Python)もインストールすれば使えますが、一般配布する場合利用者もインストールが必要になります。

どの言語でも良いですが、JScript での開発者が多いため、JScript の方が情報が集まりやすくお勧めです。

マクロを保存する際の文字コード

基本的に単体で利用する場合はどの文字コードでも構いません。

ただし「他のマクロから読まれる」場合は文字コードの選択が重要になります。

今のマクロでは #title のようなプリプロセス処理が必要で、それに対応したロードができる includeライブラリの IO.Include はデフォルトで UTF-8 対応なので、UTF-8 をお勧めします(個別の指定は可能)。

ただし読み込む側が FileSystemObject を利用している場合、SJIS/Unicode しか対応できないのでその場合は SJIS をお勧めします(Mery 1 時代のマクロなどはコレです)。

window.Document と editor.ActiveDocument の違い

window.Document は、マクロ開始時にアクティブなドキュメントを指し続けます。マクロで別のドキュメントを Activate しても変わりません。

editor.ActiveDocument は、その時点でアクティブなドキュメントを指します。マクロで別のドキュメントを Activate すると当然変わります。

editor.documents.Item(0).Activate();
Alert(document.Name);
Alert(editor.ActiveDocument.Name);

また、window.Document は documents.Item() と一致しませんが、editor.ActiveDocument は一致します。

for (var i = 0; i < editor.documents.Count; i++) {
	if (editor.ActiveDocument == editor.documents.Item(i)) {
		Alert("editor.ActiveDocument が一致");
	}
	if (Document == editor.documents.Item(i)) {
		Alert("window.Document が一致");
	}
}

window.Redraw = true は必要か

マクロで

Redraw = false;

にしたとき、最後

Redraw = true;

が必要かという点については、現状確認する限りでは不要です。

矩形選択の扱い

マクロで矩形選択は扱えません(プラグインですら無理)。

ただし、矩形選択中かの判定だけは以下の方法で可能です。

var sel = document.selection;
var isBoxed = (sel.GetBottomPointY(mePosView) - sel.GetTopPointY(mePosView)) != (sel.Text.match(/\n/g) || []).length;

使い道は限られますが、Alt + Shit + カーソル移動のキーコードを送ることで、矩形範囲指定の開始と範囲変更は可能です。

new ActiveXObject("WScript.Shell").SendKeys("+%{left}"); //{right},{up},{down}

document.GetLines の引数

リファレンスには meGetLineView しか書かれていませんが、論理行単位での行数取得には引数に 0 を渡します。

meGetLineLogical という定数はありません。

プリプロセス(#title など)

対応しているプリプロセスコードは以下の通りです(詳細はディレクティブを参照)。

  • #title = "****" ツール バーのボタンに表示するタイトルの文字列を指定します。
  • #tooltip = "****" ツール バーのボタンにマウスを乗せたときに表示されるヒントの文字列を指定します。
  • #begingroup = true [マクロ] メニューおよびマクロ バーの項目の手前に区切りバーを挿入します。
  • #icon = "****" [, アイコン番号] ツール バーのボタンに表示するアイコンを指定します。
  • #icondark = "****" [, アイコン番号] ダークモード有効時にツール バーのボタンに表示するアイコンを指定します。
  • #include "****.js" 指定したファイルをマクロの先頭に含めます。
  • #language = "****" スクリプト言語を指定します。

これらは必ず ファイル先頭 に書かなければなりません(コメントや VBS の Option Explicit などよりも先)。

他のファイルのロード方法

マクロから他のファイルを直接ロードするには以下の方法があります。

  • FileSystemObject を利用する
  • ADODB.Stream を利用する
  • includeライブラリの IO クラスを利用する

FileSystemObject を利用する

コードが短く簡単に扱えます。

また FileSystemObject 自体に、ファイルの有無やフォルダ作成の機能があります。

ただし扱える文字コードは SJIS または Unicode 限定。

// Scripting.FileSystemObject の定数
var ForReading = 1;          // ファイルを読み取り専用として開きます。このファイルには書き込むことができません。
var ForWriting = 2;          // ファイルを書き込み専用として開きます。
var ForAppending = 8;        // ファイルを開き、ファイルの最後に追加して書き込みます。
var TristateUseDefault = -2; // システム デフォルトを使ってファイルを開きます。
var TriStateTrue = -1;       // ファイルを Unicode ファイルとして開きます。
var TristateFalse = 0;       // ファイルを ASCII ファイルとして開きます。

var fso = new ActiveXObject("Scripting.FileSystemObject");

// ファイルの読み込み
var fsIn = fso.OpenTextFile(document.FullName, ForReading, false, TristateUseDefault);
var text = fsIn.ReadAll();
fsIn.Close();
Alert(text);

// ファイルの書き込み
var fsOut = fso.OpenTextFile("hoge.txt", ForWriting, true, TristateUseDefault);
fsOut.Write(text);
fsOut.Close();

ADODB.Stream を利用する

書き方は若干複雑ですが、システムが対応している文字コードであれば全て扱えます。

また(信頼性は低いが)文字コードの自動判定も可能です。

var adTypeBinary = 1;          // バイナリ データを表します。
var adTypeText = 2;            // 既定値です。Charset で指定された文字セットにあるテキスト データを表します。
var adReadAll = -1;            // 既定値です。現在の位置から EOS マーカー方向に、すべてのバイトをストリームから読み取ります。これは、バイナリ ストリームに唯一有効な StreamReadEnum 値です。
var adReadLine = -2;           // ストリームから次の行を読み取ります (LineSeparator プロパティで指定)。
var adSaveCreateNotExist = 1;  // 既定値です。FileName パラメータで指定したファイルがない場合は新しいファイルが作成されます。
var adSaveCreateOverWrite = 2; // FileName パラメータで指定したファイルがある場合は、現在開かれている Stream オブジェクトのデータでファイルが上書きされます。

// ファイルの読み込み
var adodb = new ActiveXObject('ADODB.Stream');
adodb.Type = adTypeText;
adodb.Charset = 'utf-8';
adodb.Open();
adodb.LoadFromFile(document.FullName);
var text = adodb.ReadText(adReadAll);
adodb.Close();
Alert(text);

// ファイルの書き込み
var adodb = new ActiveXObject('ADODB.Stream');
adodb.Type = adTypeText;
adodb.Charset = 'utf-8';
adodb.Open();
adodb.WriteText(text);
adodb.SaveToFile("hoge.txt", adSaveCreateOverWrite);
adodb.Close();

includeライブラリの IO クラスを利用する

ファイルの読み書きを簡単にするクラスです。

簡単に扱えることと、BOM なしの UTF-8 を書き込めることがメリットです。

逆にソースを読むしか資料がないこと、ユーザに導入してもらう必要があるのが欠点です。

#include "include/IO.js"

// ファイルの読み込み
var text = IO.LoadFromFile(document.FullName, "utf-8");
Alert(text);

// ファイルの書き込み
IO.SaveToFile("hoge.txt", text, "utf-8");

他のマクロの実行方法

他のマクロを実行するには、以下の方法があります。

  • #include で取り込む
  • ファイルをロードして eval する

前者は先頭でしかかけない上、動的にロードの切り替えができません。

後者は文字コードの問題とプリプロセス処理に関する問題があります。

includeライブラリの IO.Include を使うことで、プリプロセス処理に関しては解決できます。

#include "include/IO.js"

eval(IO.Include("test.js", "utf-8")); // #include なども処理される

マクロからファイル選択ダイアログ

以下の関数で可能です。

function OpenFileDlg() {
	var HTASource =
		'<object id=HtmlDlgHelper classid=CLSID:3050f4e1-98b5-11cf-bb82-00aa00bdce0b></object>\n'
		+ '<script language=vbscript>\n'
		+ 'resizeTo 0,0\n'
		+ 'Sub window_onload()\n'
		+ 'CreateObject("Scripting.FileSystemObject").GetStandardStream(1).Write HtmlDlgHelper.object.openfiledlg(,,"All Files(*.*)|*.*|","ファイル選択")\n'
		+ 'close\n'
		+ 'End Sub\n'
		+ '</script>\n'
		+ '<hta:application caption=no showintaskbar=no />\n';

	var oExec = new ActiveXObject('WScript.Shell').Exec(
		'MSHTA.EXE "javascript:new ActiveXObject(\'Scripting.FileSystemObject\').GetStandardStream(0).ReadAll()"'
	);
	oExec.StdIn.Write(HTASource);
	oExec.StdIn.Close();

	var filename = oExec.StdOut.ReadAll();
	var nullcharAt = filename.indexOf(String.fromCharCode(0x0));
	if (nullcharAt > 0) {
		filename = filename.substring(0, nullcharAt);
	}
	return filename;
}

使用例

var filename = OpenFileDlg();
window.alert((filename) ? filename : 'Canceled');

PowerShell 版

HtmlDlgHelper.object.openfiledlg の第1引数はダイアログに初期表示する InitFile。疑似的に初期フォルダを指定することが可能だが、パス区切り文字 "\" で終わることはできない。PowerShell 版なら初期フォルダの明示、複数ファイル選択が可能。Multiselect = $false にしても $dialog.FileName に書き換えせずに $dialog.FileNames でアクセス可能。

function OpenFileDlg() {
	ClipboardData.ClearData();
	var PS1Source =
		'[void][System.Reflection.Assembly]::LoadWithPartialName(\'System.windows.forms\');'
		+ '$dialog = New-Object System.Windows.Forms.OpenFileDialog;'
		+ '$dialog.Filter = \'AllFiles(*.*)|*.*\';'
		+ '$dialog.InitialDirectory = \'C:\\\';'
		+ '$dialog.Title = \'ファイル選択\';'
		+ '$dialog.Multiselect = $true;'
		+ 'if($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK){ Set-Clipboard $dialog.FileNames }'
	new ActiveXObject('WScript.Shell').Run(
		'PowerShell.EXE -sta -ExecutionPolicy RemoteSigned ' + PS1Source, 0, true
	);
	var result = ClipboardData.GetData();
	ClipboardData.ClearData();
	return result;
}

Prompt で空入力とキャンセルの区別

できません。

PopupMenu.Add の ID

PopupMenu.Add() の ID は必ず 0 以外にしましょう。

PopupMenu.Track() はキャンセルされた(メニューが押されなかった)場合、0 を返します。

スポンサーリンク