VBScript と JScript でこんにちは!(その3)

最後に'rem'の使い方の応用です。

rem=["Hello, JScript!"]/*/
[rem]="Hello, VBScript!"
'rem'=1/*/
WScript.Echo([rem])

これを hello3.txt などというファイル名で保存しておいて、コマンドラインから

C:\>cscript //E:VBScript hello3.txt

のように実行すると「Hello, VBScript!」と表示され

C:\>cscript //E:JScript hello3.txt

のように実行すると「Hello, JScript!」と表示されます。

Enjoy!

VBScript と JScript でこんにちは!(その2)

次のステップとしてrem構文とrem変数を活用してみましょう。

rem=null?
[rem]="Hello, VBScript!":rem="Hello, JScript!"
WScript.Echo([rem])

これを hello2.txt などというファイル名で保存しておいて、コマンドラインから

C:\>cscript //E:VBScript hello2.txt

のように実行すると「Hello, VBScript!」と表示され

C:\>cscript //E:JScript hello2.txt

のように実行すると「Hello, JScript!」と表示されます。

Enjoy!

VBScript と JScript でこんにちは!(その1)

VBScript と JScript を交配させるたったひとつの方法 - 葉っぱ日記 のパクリです。

VBScriptJScript はとっても仲がいいので、かんたんに交配させることができます。

rem=[1,2,3]
["Hello, JScript!"] = "Hello, VBScript!"
WScript.Echo(["Hello, JScript!"])

例えば、これを hello1.txt などというファイル名で保存しておいて、コマンドラインから

C:\>cscript //E:VBScript hello1.txt

のように実行すると「Hello, VBScript!」と表示され

C:\>cscript //E:JScript hello1.txt

のように実行すると「Hello, JScript!」と表示されます。

Enjoy!

defined &func の不思議

Ubuntu8.10(x86_64) な環境で perl -e "require 'syscall.ph'" すると

Prototype mismatch: sub main::__LONG_MAX__ () vs none at /usr/lib/perl/5.10/_h2ph_pre.ph line 309.
Constant subroutine __LONG_MAX__ redefined at /usr/lib/perl/5.10/_h2ph_pre.ph line 309.

という警告メッセージが出てしまう問題を深追いしていたんですけど、、、
該当コード /usr/lib/perl/5.10/_h2ph_pre.ph はこんな感じ:

   ...
   192  unless (defined &__LONG_MAX__) { sub __LONG_MAX__() { 9223372036854775807 } }
   ...
   308  # gross hack
   309  unless (defined &__LONG_MAX__) { sub __LONG_MAX__ { 2147483647 } }

Prototype mismatch と Constant subroutine redefined の問題を切り分けるためにサンプルプログラムを書いてみたんですが、

unless (defined &PPP) { sub PPP() { 1 } } warn unless ( PPP() == 2);# Warning: something's wrong at - line 1.
unless (defined &PPP) { sub PPP() { 2 } } warn unless ( PPP() == 2);
unless (defined &PPS) { sub PPS() { 1 } } warn unless (&PPS   == 2);
unless (defined &PPS) { sub PPS() { 2 } } warn unless (&PPS   == 2);
unless (defined &PSP) { sub PSP() { 1 } } warn unless ( PSP() == 2);# Warning: something's wrong at - line 5.
unless (defined &PSP) { sub PSP   { 2 } } warn unless ( PSP() == 2);
unless (defined &PSS) { sub PSS() { 1 } } warn unless (&PSS   == 2);
unless (defined &PSS) { sub PSS   { 2 } } warn unless (&PSS   == 2);
unless (defined &SPP) { sub SPP   { 1 } } warn unless ( SPP() == 2);
unless (defined &SPP) { sub SPP() { 2 } } warn unless ( SPP() == 2);
unless (defined &SPS) { sub SPS   { 1 } } warn unless (&SPS   == 2);
unless (defined &SPS) { sub SPS() { 2 } } warn unless (&SPS   == 2);
unless (defined &SSP) { sub SSP   { 1 } } warn unless ( SSP() == 2);
unless (defined &SSP) { sub SSP   { 2 } } warn unless ( SSP() == 2);
unless (defined &SSS) { sub SSS   { 1 } } warn unless (&SSS   == 2);
unless (defined &SSS) { sub SSS   { 2 } } warn unless (&SSS   == 2);

これを実行すると

Warning: something's wrong at - line 1.
Warning: something's wrong at - line 3.

となっちゃいます。。。

コンパイルフェーズと実行フェーズの違いなんでしょうけど、、、意味がわかりません(><)

元々の意図からすると # gross hack は意味をなしてないってことなんですかね。

Perl で Win32::API プログラミング入門

ActivePerl (Windows版) には Win32::API - Perl Win32 API Import Facility - metacpan.org が付属しているので、今日からすぐに Win32::API を利用したプログラミングが出来ます。

簡単なメッセージボックスを表示するPerlプログラムは以下になります。

#!/usr/bin/perl
use Win32::API;
my $MessageBox = Win32::API->new("user32", "MessageBoxA", "NPPN", "N");
   $MessageBox->Call(0, "Hello, World!\n", "Message", 0);

このプログラム実行すると、「Hello, World!」と書かれたWindowsメッセージボックスが表示されます。

これを Win32::API を使わない形に書き直すと以下のようになります。

#!/usr/bin/perl
use DynaLoader;
sub GetProcAddress {
	my ($DLL, $API) = @_;
	my $path = "$ENV{SystemRoot}\\system32\\$DLL";
	my $libref = DynaLoader::dl_load_file($path);
	pack "L", DynaLoader::dl_find_symbol($libref, $API);
}
my $x86 = ""
. "h\0\0\0\0"
. "h" . pack("P", "Message")
. "h" . pack("P", "Hello, World!\n")
. "h\0\0\0\0"
. "\xb8" . GetProcAddress("user32.dll", "MessageBoxA")
. "\xff\xd0" # call eax
. "\xc3"     # ret
;
DynaLoader::dl_install_xsub("X",unpack"L",pack"P",$x86);&X;

DynaLoader はとっても便利なモジュールですね。

Perl 5.10.0 / 日本語 Windows XP SP2 (x86) の環境で試しました。

(もしかしたら Vista や 64bit Windows 環境では動かないかもしれませんが、手元に環境がないので)→追記の動作報告があり、動いたみたいです。

■追記:

Twitter/ワッサーで以下の方々から動作報告をいただきました。Vistaやstrawberry perlでも動作するようです。

  • "Win32::APIを使わない方" WinXP pro, Strawberry Perl v5.10.0でも実行可能なことを確認しますた by likkさん
  • ちなむと、便利なDynaLoaderはVista上のActivePerlでもちゃんと動いてますよ。 by printf("hsegawa\n"); //DEBUG さん
  • x64版Vistaでも動きました! (Perlは5.10 x86) by kaorunさん

ありがとうございます!

Perl で動的 x86_64 プログラミング

相対ジャンプのcall命令を動的に生成できるようにしておくとx86実行コード中に任意の文字列を埋め込めるので大変便利です。

Linux/x86_64 で /usr/bin/perl -e 'print "Hello, world!\n"' を実行するサンプル

#!/usr/bin/perl -w
sub P{"\xe8".pack('L',1+length$_[0]).$_[0]."\x00"};
use DynaLoader;DynaLoader::dl_install_xsub('X',unpack'L',pack'P',
$_=P($^X)."_h-e\x00\x00H\x89\xe1".P('print "Hello, world!\n"')
."[1\xd2RSQWH\x89\xe6j;X\x0f\x05j\x01_j<\x0f\x05");&X

CentOS 4.4/x86_64 + AMD Opteron 240 EE + Perl 5.8.5 で動作確認

Hello, world!

上記プログラムは AMD64 ABI(*1) を利用した syscall で Linuxシステムコールを呼び出しているため、i386 環境では動作しません。uname -i が x86_64 な環境で動作可能です。

● 参考文献

  1. http://www.x86-64.org/documentation/abi-0.99.2.pdf

はじめての x86_64/Linux プログラミング

$ vi amd64linux.pl

#!/usr/bin/perl
use DynaLoader;
my $x=""
. "\x48\xb8Hello64!"              #  mov    'Hello64!',%rax
. "\x50"                          #  push   %rax
. "\x48\x89\xe6"                  #  mov    %rsp,%rsi
. "\x48\xc7\xc7\x01\x00\x00\x00"  #  mov    $0x1,%rdi
. "\x48\xc7\xc2\x08\x00\x00\x00"  #  mov    $0x8,%rdx
. "\x48\xc7\xc0\x01\x00\x00\x00"  #  mov    $0x1,%rax
. "\x0f\x05"                      #  syscall
. "\x48\x89\xf8"                  #  mov    %rdi,%rax
. "\x0f\x05"                      #  syscall
. "\x58"                          #  pop   %rax
. "\xc3"                          #  ret
;
DynaLoader::dl_install_xsub("X",unpack("L",pack("P*",$x)));X();
print "ok\n";

$ perl amd64linux.pl

Hello64!Hello64!ok

i386/Linux の頃からいろいろ変わってますね。