Roslyn で生成される IL をこそっと編集したい

な、なんだってー(笑

You can create your own command line driver to replace csc.exe using the Roslyn APIs, and change your build system to invoke that instead of csc.exe.

https://social.msdn.microsoft.com/Forums/vstudio/en-US/19c01285-56bc-4404-9819-7f06be104de1/code-rewrite-during-compilation?forum=roslyn

コンパイラ作りたいわけじゃないのですが、難読化や依存性注入みたいなかんじのノリで、ちょっと生成される IL を加工したいだけなんですが…。

最近遊んでいるゲーム

PC

DRAGON QUEST X
DQ10、MMO だけど中身は結構ふつーに過去のドラクエです。シナリオとか雰囲気とかね。Wii 版から Windows 版へ乗り換えました。フルパッシブとか全然届かない程度の普通人プレイです。
FINAL FANTASY XIV
FF14、ナイト装備は全部90で揃えましたが、2.1? からあまりプレイできていません。どーも、覚えて避ける作業がいまいち合わないぽい?
ファンタシースターオンライン2
まったく遊んでいませんが? PC よりも Vita で起動することが多いかもしれません。まったく遊んでいませんが!

Mobile

魔法使いと黒猫のウィズ
初期プレイヤーです。TVCMが打たれる頃からログインのみ生活へ、新カード追加のたびに基本インフレで、古い人はついていけないかんじ? 毎日、画像クイズだけはやってますが、時々進化手前の奴の素材そろえるぐらいはやるかんじ。招待コードは TCA2XLBD
チェインクロニクル
メインシナリオクリア済み、フィーナ覚醒済み(真エンディング済み)。まだまだ、ちまちまやってます、魔人5枚取れるぐらいには。第二部まだー? そういやコレ、招待とかない。フレンド申請は 167,614,022
スリングショットブレイブズ
黒猫で宣伝してたやつ。戦闘システムが違う以外は黒猫と同じノリなのが少し懐かしかった。100万イベントで槍とれなかった…。1戦闘の時間が微妙にモバイル向けには長いこともあって、ほぼログインのみ生活。招待コードは ELGRAND
TSUM TSUM
ツムツムは1回60秒で終わる LINE ゲームです。公式ページから飛ぶのは大変なので LINE のゲームメニューからどうぞ。突然、普段にはない大量得点とれたりして驚くのは、アングリーバードで予想しないうまい結果が出る感じの楽しさに似ている。毎日のミッションを欠かさず終わらせている程度にはやってます。招待した人が遊ばなくても招待ボーナスもらえるけど、まずは LINE の友達を増やさなきゃ招待送れない...
Wonder Flick
気を付けないと再現なく課金できる。まだまだ発展途上なかんじ?遊んであげたいけどあまり時間が…ってかんじです。招待コードは 989-441-341 (招待コードという名のキャラIDだけども)
拡散性ミリオンアーサー
Android, iOS, PSVita で遊べますが、他のゲームと違って別世界扱いです。Vita 版で遊んでいますが、平日やらないかんじでログインボーナスが貯まらない程度のプレイです。招待コードは V8037

遊んでいたもののやめたもの、インストールだけはしてあるもの、遊びはじめたものがいくつかありますが、まあそこらへんは置いときましょう。Dragon Crawn とか Samurai and Dragons とか、一緒にやる人がいたら再開しそうなものはいくつかありはする。

[Game] ゲーマータグにサインインするアカウントを変更する

最近、Surface Pro を購入したこともあって Windows 8 を利用することが多くなりそうなかんじになりました。Windows Vista で導入された Games for Windows による Xbox360Windows ゲームの統合ですが、せっかくなので Windows 8 でもこれまでに利用していた Xbox360 のゲーマータグを継続して利用したいと思いました。
しかし、Xbox360 で使用していた Microsoft アカウントは既にドメインが存在しないメールアドレスに関連付けられていることもあって、Windows 8 では違うメールアドレスを使用した Microsoft アカウントを使用しています。Windows 8 では OS にサインインしている Microsoft アカウント以外でゲームを起動することはできないようです。そこで、ゲーマータグの移動を行おうと思いました。

ゲーマータグの移動は Xbox360 にサインインして

とたどって変更できます。できるはずだったんです。

そもそものはじまりは

Windows 8 で「ゲーム」を選択すると、ゲーマータグの新規作成画面に飛ばされているのは知っていたのです。Xbox360で長いこと遊んでいることもあってキャンセルして新規作成はしていなかったのです。ところが、マインスイーパーを起動したときに「このアプリケーションがあなたのアカウント情報にアクセスすることを許可しますか?」と聞いてきたところに「はい」を選んでしまったのですね。そうすると、自動的にランダムな名前のゲーマータグが作成され*1て自動的に Windows 8 のアカウントに関連付けられてしまったのです。

・・・というわけで、仕方なくゲーマータグの移行を思い立ったのですが。

Xbox360 は長期間利用していない

最初は Windows 8xbox.com から変更できると思っていたんです。しかし、調べても調べても Xbox360 での操作しかでてきません。しかたないので Xbox360 の電源をいれることにしました。とはいえ、2号は電源スイッチを押すと赤色に点灯する状態のため、クローゼットから1号を取り出して2号と入れ替えます。

1号の電源を入れてサインインをしようとすると、「ゲーマープロフィールを保存するためには、ストレージが必要です」というエラーに。メモリカードか HDD がないとダメなんでしたね。

2号に刺さっている 120GB HDD を抜いて刺してもよかったのかもしれませんが、たしか1号から2号に HDD をさしかえたとき、本体の変更とか検出して色々始まったような記憶があったので、再びクローゼットをゴソゴソして 30GB の HDD を取り出して刺します。このディスクは 120GB HDD に付属の引っ越しツールで全消去されているため、刺しただけですんなり使えるようになりました。

ゲーマータグを移動する

目的のゲーマータグにサインインできたので、Microsoft アカウントの変更を実行します。「移動先の Microsoft アカウントには別のゲーマータグが登録されているため、移動でいません。」 あー、はいはい。そうですね。そうですね。

マインスイーパーが作成したランダム単語のゲーマータグを削除できればいいんですが、ちょっと調べたかぎりでは Microsoft アカウントまるごと削除する以外に削除方法はなさそうなかんじでした。http://xbox.com/AccountSwap によると、ゲーマータグを関連付けていない捨てアカウントを作成してローテーションさせろという話ですので、さっさく移動しましょう。

「現在 Microsoft アカウントの変更ができません。」

なんだって? アカウントの安全性認証をしていないからか?でも、メール受け取れないし、とりあえず移動先だけ認証しとくもエラーは同じでかわらず。

結論

先程記載した http://xbox.com/AccountSwap に書いてあるのですが、

  • Microsoft アカウントは、一度変更すると 30 日間は変更できません。
    • これは Xbox LIVE からダウンロードした新しいゲーマータグにも当てはまります。

つまり、つい昨日にマインスイーパーによって作成されたゲーマータグを移動するには、30日後に作業してくれってこと。日常的に利用する Microsoft アカウントは1本化してシングルサインにしておけと。そんな使用する直前になってあれこれ調べてどうにかしようとするなと。

ちなみに、Microsoft アカウントをドロップダウンリストから切替できる「リンクアカウント」の機能は7月いっぱいで廃止されるそうです。Web がメインのときはこの機能で日常利用の Microsoft アカウントとゲーム用の Microsoft アカウントがサクサクと切り替えて使えていたのですけどね。

*1:ゲーマータグのリネームは有料ですが、このランダムな名前のタグを修正するのは無料

/C オプションと PowerShell

/C オプションで呼び出した場合は結果が違うんじゃないか?という話を聞いたので追試。確かに /C オプションを指定すると挙動が違いました。さらに PowerShell から呼び出した場合も似たような状態に。


/C オプションはいいとして、(いや、よくないけどさ) CMD と PowerShell でバッチファイルを単純に実行しただけで、そのバッチの結果が違うってのは互換性的にヤバくないかい…?

CMD の継続演算子

CMD にはコマンドの終了コードを使用して次のコマンドを実行するかどうかを決定する継続演算子があります。

command1 & command2

と記載すると、command1 の終了後に command2 を実行します。

command1 && command2

と記載すると、command1 の終了コードが 0 の場合のみ command2 を実行し、

command1 || command2

と記載すると、command1 の終了コードが 0 以外の場合のみ command2 を実行します。

コマンドの終了コードと ERRORLEVEL は異なります。上記の && と || は ERRORLEVEL ではなく終了コードを見て次のコマンドの実行判定をすることに注意が必要です。特に注意が必要なのはバッチファイルの呼び出し処理です。バッチファイルの呼び出しはファイル名を記載するか CALL コマンドで行うことができますが、前に書いているイメージのように EXIT コマンドが設定するのは ERRORLEVEL であって終了コードではないことに注意が必要です。

終了コードは Win32 のプロセスが終了するときに設定するものであって、CMD.exe という1つのプロセスの中で(外部のバッチファイルの呼び出しを含めて)バッチ処理を進めていく最中に参照されている状態ではないということです。もちろん、CMD の内部コマンドは Win32 のプロセスの終了コードをシミュレートしてくれるので、

REM 存在しないドライブを順番に表示しようとする
dir P: || dir Q: || dir R: || dir S:
 ドライブ R のボリューム ラベルは RAM-DRIVE です
 ボリューム シリアル番号は 1234-5678 です

などとすれば dir コマンドはきちんと終了コードを返しているかのように動き、P: Q: が存在しない場合に dir R: が実行されます。
ここで注意が必要なのは CALL コマンドを含めたバッチファイルの呼び出しの場合です。CALL コマンドは「対象のバッチファイルまたはラベルが呼び出せた場合に成功、呼び出せない場合に失敗」となっています。つまり、

file.bat || echo file.bat がみつかりませんでした。

という処理は file.bat が存在すれば file.bat を実行し、file.bat が存在しなければメッセージを出力するという意味になります。*1 呼び出されたバッチファイル file.bat の中身から呼び出し元に終了状態を伝える方法は、前半に書いているように EXIT コマンドを使用して ERRORLEVEL を設定することです*2が、&& や || は終了コードを判定するため file.bat がどのような結果になったかに関係なく、バッチ処理を呼び出せたかどうかだけが判断の基準になります。
ERRORLEVEL を使用してエラー判定をしようとすると

file.bat && IF ERRORLEVEL 1 ECHO 実行に失敗しました

という感じになりますが、CALL コマンドが失敗した場合、IF 〜 は実行されず「実行に失敗しました」は表示されないことになります。ちょっと困りますよね。場合分けが必要ではないならば

file.bat & IF ERRORLEVEL 1 ECHO file.bat が見つからないか、file.bat が失敗しました

といったかんじで、& にするとファイルがない場合と呼び出し先でエラーになった場合の両方にメッセージを出力できます。呼び出しが成功してエラーが発生しなかった場合のみ実行する場合には、

file.bat && IF NOT ERRORLEVEL 1 ECHO 成功しました

と、&& と NOT を使用する感じになります。ちなみに、CALL コマンドは ERRORLEVEL にエラー明細を設定してくれるので、ファイル名を直接書くよりも細かいエラー判定ができます。

*1:再帰が深すぎて file.bat を実行できない場合にもメッセージが出力されると思いますが…

*2:もちろん、環境変数等も手段としてあります

CMD の EXIT コマンドについて

いまだに、当日記の通算アクセス数トップ&毎月のアクセス数トップは常に id:ladybug:20090530 なぐらい、コマンドプロンプトさんは人気みたいですね。Power Shell にはもうちょっと頑張って欲しいところです。

CMD には EXIT というコマンドがありますが、こいつの機能をきちんと理解されていることは意外に少ないようです。ヘルプにも書いてあるんですが、

  • システム変数*1 ERRORLEVEL を指定した値に設定する (引数に数値を指定)
  • 現在のインタプリタの処理スコープを1つ終了する (引数に /B を指定)
  • 現在のインタプリタを処理スコープをすべて終了する (引数に /B を指定しない)

の3つの機能があります。/B は break の B かな?と思いますが、ループ処理と break, exit のあるC言語などで考えると雰囲気が伝わりますね。

int errlevel = 0;

void proc()
{
    while (true)
    {
        // インタプリタの処理

        if (... EXIT コマンド...)
        {
            if (...ExitCode 指定あり...)
            {
                // システム変数 ERRORLEVEL を設定
                errlevel = ...;
            }

            if (..."/B" 指定あり...)
            {
                // "/B" を指定されたら break
                break;
            }
            else
            {
                // "/B" が指定されなかったら exit
                exit(errlevel);
            }
        }

        if (...CALL コマンドやバッチ処理...)
        {
            proc();
        }
    }
}

void main()
{
    proc();

    // インタプリタは最後の ERRORLEVEL で終了する
    exit(errlevel);
}

環境変数の管理やら入出力の管理があるので、実際はもっと色々あるでしょうけど、EXIT コマンドのイメージとしてはこんなかんじですね。CMD.exe が終了するとき、ERRORLEVEL の最終値が CMD.exe の終了コードとして採用されます。

*1:環境変数とは違います。同名の環境変数を作るとややこしいことになるので注意しましょう。