アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

サクラエディタでFlashをコンパイル

こんにちは、中川です。
最近、暇を見つけては、ちょこちょこFlash(ActionScript)をいじっています。

普段は、FlashDevelop(beta5がでましたね^^)を使って書いているのですが、ちょっとしたスクリプトを書いてみるときには、いちいちFlashDevelopを起動しなくて、サクラエディタを使うほうが楽だったりします。しかし、サクラエディタで書いた場合、コンパイルするためにcmdを起動してコンパイルという作業が非常に面倒くさいわけです。

rascutを使ってやる方法は確かに便利ですが、先日書いたようにサクラエディターの私としてはなんとかサクラエディタだけでやってみたいものです。

そこで、サクラエディタから一発で、Flashコンパイルできる方法を考えてみました。
(flexSDK2やfcshについては【Flexはじめました。 : アシアルブログ】を見てみてください。)


まず、サクラエディタでは、マクロをJScriptVBScriptでかけますので、これを利用します。
mxmlcだけを使った簡単なものは、以下のようになります。

[mxmlc.js]


var shell = new ActiveXObject("WScript.Shell").Run("cmd.exe /k mxmlc " + '"' + GetFilename + '"');

(cmd.exeに「mxmlc filename」を引数で渡しているだけです。)

これをマクロに登録すれば、とりあえずコンパイルはできるのですが、生のmxmlcでは遅くてやってられません。

で、fcshを使ってコンパイルする方法を検討してみたいと思います。
最終的には、課題がいろいろと残りましたが、なんとか動いています。

では、マクロからのfcshを使ったコンパイルの流れとしては、


fcshシェルが起動されているか確認。ない場合cmd.exeを起動しfcshを起動。
fcshシェル内でmxmlc
コマンドプロンプトのプロセスIDを記憶。

こんな感じです。


fcshでは、2回目以降のコンパイルは最初に起動したシェルにコマンドを渡したいので、最初のcmdのプロセスIDが必要です。さっきみたいに、コマンドプロンプトを起動すると、 WScript.Shell の Run ではcmdのプロセスIDがとれませんでした。
で、WScript.Shell のExecを使ってcmdを起動しても、一瞬で窓が閉じてしまいます。

そこで、【WMI による Windows の管理 [WMI からメモ帳を起動する]】を参考に
プロセスIDを取れるように起動するには以下のようにしました。



function CreateProcess(title, msg, defaultValue) {
	var oSC = new ActiveXObject("ScriptControl");
	oSC.Language = "VBScript";
	var sFunc = 'Function ProcessCreate() \n';
	sFunc 	 += '    Set Process = GetObject("winmgmts:{impersonationLevel=impersonate}!Win32_Process") \n';
	sFunc    += '    Process.Create "cmd.exe",Null,Null,pid \n';
	sFunc    += '    ProcessCreate = pid \n';
	sFunc    += 'End Function \n';
	oSC.AddCode(sFunc);
	var Ret = oSC.Run("ProcessCreate");
	return Ret; 
}


cmd.exeの起動は、VBScriptでやっています。JScriptでもできるかもしれませんが、分りませんでした。そこでJScript内で、VBを使うために、「ScriptControl」を使用します。
(詳しくは【サクラエディタマクロ - ScriptControlの使用】を参照。)

これで、起動したcmdのプロセスIDが取得できました。

あとは、


var WshShell = new ActiveXObject("Wscript.Shell");
var res = WshShell.AppActivate(pid);

で、アクティブにして、コマンドを送ればOKです。

が、しかし、「WshShell.SendKeys(str);」では、日本語(パスの「デスクトップ」など)が送れません。
方法をいろいろ調べてみたのですが、単独のファイルでは、無理かもしれません・・・。
別途外部ファイルを使えば、【WSH の SendKeys で日本語を入力する】できる方法もあるようです。)
とりあえず、今回はパスに日本語が含まれないということ前提でいきましょう。

これで、運がよければ動くのですが、
コマンドプロンプト起動後のコマンド実行がアクティブにする前に実行されてしまう場合があります。
そのため、起動後のコマンドは遅延させたいのですが、「WScript.Sleep」は、「wscript.exe」 か 「cscript.exe」がホストの場合にしか使えないみたいです。
そこで、今回はしょうがなく、whileでAppActivateの戻り値を監視するという危険な感じになっています。
(別に考えたのでは、for文をブン回しておくとか。。。。)



while(WshShell.AppActivate(pid) == 0) {
	//こんな危険な・・・。
}


これで、遅延(起動アクティブ後の実行)もOKとなりました。

最後にPIDの保持ですが、マクロではさすがに値の保持は無理っぽいので、ファイルに書き出す方法をとっています。
「filename.pid」にpidを保存し、2回目以降のマクロ実行では、「filename.pid」からpidを取得しています。
この方法だと、「filename.pid」ファイルが最終的にはゴミとして残ってしまうので、あまりいい方法とは言えませんね。。。

とりあえず、こんな感じで、fcshを使ったコンパイルのマクロは動作しました。
これで、サクラエディタからも、flashの作成がちょっとは簡単になったような気がします。

※動作確認は、WindowsXP SP2 (FlexSDK2)環境でしか行っておりません。
 VBJScriptはほとんど触ったことないので、変なことしている可能性が多々あります。
 このマクロを使用する場合は自己責任でお願いいたしますm(_ _)m


■[fcsh.js]ソース
>>JS
var FLEX_HOME = 'C:\\usr\\flex_sdk_2'; // FLEXディレクトリを設定してね。
var FCSH_PATH = FLEX_HOME + '\\lib\\fcsh.jar';

/**
* local functions
*/
function delayedSendKeys(str) {
//WScript.Sleep(100);
//sleep(100000);
WshShell.SendKeys(str);
}
/*
function sleep(count) {
WScript.Sleep();
for(var i = 0; i < count; i++) {
//遅延させたいだけなのにーー(><)
}
}
*/

function CreateProcess(title, msg, defaultValue) {
var oSC = new ActiveXObject("ScriptControl");
oSC.Language = "VBScript";
var sFunc = 'Function ProcessCreate() \n';
sFunc += ' Set Process = GetObject("winmgmts:{impersonationLevel=impersonate}!Win32_Process") \n';
sFunc += ' Process.Create "cmd.exe",Null,Null,pid \n';
sFunc += ' ProcessCreate = pid \n';
sFunc += 'End Function \n';
oSC.AddCode(sFunc);
var Ret = oSC.Run("ProcessCreate");
return Ret;
}

function writePid(file, pid) {
var fso, f, r
var ForReading = 1, ForWriting = 2;
fso = new ActiveXObject("Scripting.FileSystemObject")
f = fso.OpenTextFile(file, ForWriting, true)
f.Write(pid);
f.Close();
f = fso.OpenTextFile(file, ForReading);
r = f.ReadLine();
return(r);
}

function readPid(file) {
var fso, f, r;
var ForReading = 1;
fso = new ActiveXObject("Scripting.FileSystemObject")
if (fso.FileExists(file)) {
f = fso.OpenTextFile(file, ForReading);
r = f.ReadLine();
} else {
r = false;
}
return(r);
}


/**
* ここから実行
*/
var fileName = GetFilename;
var pidFileName = fileName + ".pid";
var _pid = readPid(pidFileName);

var WshShell = new ActiveXObject("Wscript.Shell");
var res = WshShell.AppActivate(_pid);
if (res == 0) {
var pid = CreateProcess();
var isInit = true;
} else {
var pid = _pid;
var isInit = false;
}

//sleep(10000);
//var res = WshShell.AppActivate(pid);
while(WshShell.AppActivate(pid) == 0) {
//こんな危険な・・・。
}

if (isInit) {
//delayedSendKeys('cd "' + WshShell.CurrentDirectory + '"~');
delayedSendKeys("java -Dapplication.home=" + FLEX_HOME + " -jar " + FCSH_PATH + "~");
}
delayedSendKeys("mxmlc " + fileName + "~");

var res = writePid(pidFileName, pid);