2009年8月31日月曜日

今日ははつねさんの誕生日

…ということで、いろいろ動きがある模様。

・初音ミク2周年!どふーンとプレゼント企画!(PIAPRO)
http://blog.piapro.jp/2009/08/2-1.html

誕生日恒例(?)のプレゼント企画。
去年は外れた人にも壁紙が配布されたりしていた。



ねんどろいどぷち ボーカロイド シリーズ01 BOXがAmazon.co.jpで予約受付開始!
ねんどろいどぷち Vocaloid #01
ねんどろいどぷち Vocaloid#01


…'`ァ,、ァ(*´Д`*)'`ァ,、ァ

ねんどろいどは、転売erが湧いて全然買えない上に高騰しまくるのが普通なので、ちょっとでも欲しいと思ったらAmazon.co.jpで予約受付しているうちに予約しておくのが良いかと('A`)
Amazonで代引きならキャンセル出来るからね…私はキャンセルしないけどね(´・ω・`)

ちょっと謎なのが、
>1BOX12個入り (10種+シークレット1種)
…1個何処行った…?


あとこんなのも…



…Amazon見るたびに、財布の中が寂しくなっていくのを痛感出来るな…


このBlogにミクの記事を書き始めてちょうど一年。
最初は躊躇していた筈なのに、今ではこの有様…'`,、('∀`) '`,、

それにしても、一年って早いね…(´・ω・`)

2009年8月26日水曜日

Opera 10 RCが公開されていた件

何故かFirefox嫌い(?)な私が常用しているwebブラウザ、Operaのバージョン10のRelease Candidate版が、Opera Desktop Term Blogにて公開されていた。

http://my.opera.com/desktopteam/blog/2009/08/25/opera-10-0-release-candidate

正式版は9月1日にリリースされる予定らしい。
新しい物好き・Beta版好きな私は、正式版を待たず、早速RC版をインストールしてみた。

Opera 10 RC

Beta3から変わった点は、日本語化された(Beta時代は英語)ことと、デフォルトのUIが変わった位…か?
相変わらずフォントの設定が面倒くさいこと以外は、なかなか良い感じ。




※Firefoxが嫌いというより、私の家の回線が遅い上に不安定で、新しいページを開き損ねた時に、一瞬で前開いていたページを読めるOperaが便利過ぎてずっと使っている、ということだったりする。

Opera turboなど、回線が細くても快適なブラウジングができる機能が他にもあるので、回線が細い人にはとてもオススメ。

2009年8月22日土曜日

Multi thread Media Encoder Frontend v1.2 完成

コマンドプロンプトだと使いにくいって言われるから、そろそろGUI付けようか…(´・ω・`)
と考え続けて半年。
VisualuRubyが無茶苦茶使いやすいことに気付き、とりあえずVisualuRubyで作ることに決めて4日。

ようやくGUI付きのMulti thread Media Encoder Frontendが完成したよ…(´;ω;`)ウッ…
Malti thread Media Encoder Frontend GUI
これで本当に「フロントエンド」を名乗れる…気がしなくもない。
…地味だけど、これでいいよね…('A`)

使用方法、プログラムのダウンロードは、http://mtmef.g.hachune.net/からどうぞ。

2009年8月20日木曜日

MinGWを使ったRuby/GTK2のコンパイルが上手くいかない件

Windowsでも最新のRuby/GTK2を使用できるようにしようと、MinGWを使用したコンパイルに何度も挑戦しているが、どうしても上手くいかない。

gtk2.soを作るところで、ld.exe: cannot find -lruby-gtk2となり、コケてしまう。

環境は、
Windows 7 RC x64
MSYS 1.0.11(32bit)
MinGW 5.1.4(32bit)
Rubyソースruby-1.8.7-p174.tar.gzをmake、D:/RubyGTKにインストール。
GTKバイナリgtk+-bundle_2.16.4-20090708_win32.zipをMSYSの/に展開。
Ruby/GTK2ソースruby-gtk2-0.19.1.tar.gzをmake。


hatsune@HATSUNE-SERVER ~/RubyGTK/ruby-gtk2-0.19.1
$ make
make[1]: Entering directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/glib'
make[2]: Entering directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/glib/src'
make[2]: Nothing to be done for `all'.
make[2]: Leaving directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/glib/src'
make[1]: Leaving directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/glib'
make[1]: Entering directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/gdkpixbuf'
make[1]: Nothing to be done for `all'.
make[1]: Leaving directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/gdkpixbuf'
make[1]: Entering directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/pango'
make[2]: Entering directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/pango/src'
make[2]: Nothing to be done for `all'.
make[2]: Leaving directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/pango/src'
make[1]: Leaving directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/pango'
make[1]: Entering directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/atk'
make[2]: Entering directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/atk/src'
make[2]: Nothing to be done for `all'.
make[2]: Leaving directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/atk/src'
make[1]: Leaving directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/atk'
make[1]: Entering directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/gtk'
make[2]: Entering directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/gtk/src'
gcc -shared -s -o gtk2.so init.o rbgdk.o rbgdkatom.o rbgdkcairo.o rbgdkcolor.o rbgdkcolormap.o rbgdkconst.o rbgdkcursor.o rbgdkdevice.o rbgdkdisplay.o rbgdkdisplaymanager.o rbgdkdragcontext.o rbgdkdraw.o rbgdkevent.o rbgdkgc.o rbgdkgeometry.o rbgdkimage.o rbgdkinput.o rbgdkkeymap.o rbgdkkeyval.o rbgdkpango.o rbgdkpangorenderer.o rbgdkpixbuf.o rbgdkpixmap.o rbgdkproperty.o rbgdkrectangle.o rbgdkregion.o rbgdkrgb.o rbgdkscreen.o rbgdkselection.o rbgdkthreads.o rbgdktimecoord.o rbgdkvisual.o rbgdkwindow.o rbgdkwindowattr.o rbgdkx11.o rbgtk.o rbgtkaboutdialog.o rbgtkaccelerator.o rbgtkaccelgroup.o rbgtkaccelgroupentry.o rbgtkaccelkey.o rbgtkaccellabel.o rbgtkaccelmap.o rbgtkaccessible.o rbgtkaction.o rbgtkactiongroup.o rbgtkadjustment.o rbgtkalignment.o rbgtkallocation.o rbgtkarrow.o rbgtkaspectframe.o rbgtkassistant.o rbgtkbbox.o rbgtkbin.o rbgtkbindingset.o rbgtkborder.o rbgtkbox.o rbgtkbuildable.o rbgtkbuilder.o rbgtkbutton.o rbgtkcalendar.o rbgtkcelleditable.o rbgtkcelllayout.o rbgtkcellrenderer.o rbgtkcellrendereraccel.o rbgtkcellrenderercombo.o rbgtkcellrendererpixbuf.o rbgtkcellrendererprogress.o rbgtkcellrendererspin.o rbgtkcellrenderertext.o rbgtkcellrenderertoggle.o rbgtkcellview.o rbgtkcheckbutton.o rbgtkcheckmenuitem.o rbgtkclipboard.o rbgtkcolorbutton.o rbgtkcolorsel.o rbgtkcolorselectiondialog.o rbgtkcombo.o rbgtkcombobox.o rbgtkcomboboxentry.o rbgtkconst.o rbgtkcontainer.o rbgtkcurve.o rbgtkdialog.o rbgtkdrag.o rbgtkdrawingarea.o rbgtkeditable.o rbgtkentry.o rbgtkentrycompletion.o rbgtkeventbox.o rbgtkexpander.o rbgtkfilechooser.o rbgtkfilechooserbutton.o rbgtkfilechooserdialog.o rbgtkfilechooserwidget.o rbgtkfilefilter.o rbgtkfilesel.o rbgtkfixed.o rbgtkfontbutton.o rbgtkfontselection.o rbgtkfontselectiondialog.o rbgtkframe.o rbgtkgamma.o rbgtkhandlebox.o rbgtkhbbox.o rbgtkhbox.o rbgtkhpaned.o rbgtkhruler.o rbgtkhscale.o rbgtkhscrollbar.o rbgtkhseparator.o rbgtkiconfactory.orbgtkiconinfo.o rbgtkiconset.o rbgtkiconsize.o rbgtkiconsource.o rbgtkicontheme.o rbgtkiconview.o rbgtkimage.o rbgtkimagemenuitem.o rbgtkimcontext.o rbgtkimcontextsimple.o rbgtkimmulticontext.o rbgtkinits.o rbgtkinputdialog.o rbgtkinvisible.o rbgtkitem.o rbgtkitemfactory.o rbgtklabel.o rbgtklayout.o rbgtklinkbutton.o rbgtkliststore.o rbgtkmain.o rbgtkmenu.orbgtkmenubar.o rbgtkmenuitem.o rbgtkmenushell.o rbgtkmenutoolbutton.o rbgtkmessagedialog.o rbgtkmisc.o rbgtknotebook.o rbgtkobject.o rbgtkoptionmenu.o rbgtkpagesetup.o rbgtkpagesetupunixdialog.o rbgtkpaned.o rbgtkpapersize.o rbgtkplug.o rbgtkprintcontext.o rbgtkprinter.o rbgtkprintjob.o rbgtkprintoperation.o rbgtkprintoperationpreview.o rbgtkprintsettings.orbgtkprintunixdialog.o rbgtkprogress.o rbgtkprogressbar.o rbgtkradioaction.o rbgtkradiobutton.o rbgtkradiomenuitem.o rbgtkradiotoolbutton.o rbgtkrange.o rbgtkrc.o rbgtkrcstyle.o rbgtkrecentaction.o rbgtkrecentchooser.o rbgtkrecentchooserdialog.o rbgtkrecentchoosermenu.o rbgtkrecentchooserwidget.o rbgtkrecentdata.o rbgtkrecentfilter.o rbgtkrecentfilterinfo.orbgtkrecentinfo.o rbgtkrecentmanager.o rbgtkruler.o rbgtkscale.o rbgtkscalebutton.o rbgtkscrollbar.o rbgtkscrolledwindow.o rbgtkselection.o rbgtkselectiondata.o rbgtkseparator.o rbgtkseparatormenuitem.o rbgtkseparatortoolitem.o rbgtksettings.o rbgtksizegroup.o rbgtksocket.o rbgtkspinbutton.o rbgtkstatusbar.o rbgtkstatusicon.o rbgtkstock.o rbgtkstyle.o rbgtktable.o rbgtktargetlist.o rbgtktearoffmenuitem.o rbgtktextappearance.o rbgtktextattributes.o rbgtktextbuffer.o rbgtktextchild.o rbgtktextiter.o rbgtktextmark.o rbgtktexttag.o rbgtktexttagtable.o rbgtktextview.o rbgtktoggleaction.o rbgtktogglebutton.o rbgtktoggletoolbutton.o rbgtktoolbar.o rbgtktoolbutton.o rbgtktoolitem.o rbgtktooltip.o rbgtktooltips.o rbgtktreedragdest.o rbgtktreedragsource.o rbgtktreeiter.o rbgtktreemodel.o rbgtktreemodelfilter.o rbgtktreemodelsort.o rbgtktreepath.o rbgtktreerowreference.o rbgtktreeselection.o rbgtktreesortable.o rbgtktreestore.o rbgtktreeview.o rbgtktreeviewcolumn.o rbgtkuimanager.o rbgtkvbbox.o rbgtkvbox.o rbgtkviewport.o rbgtkvolumebutton.o rbgtkvpaned.o rbgtkvruler.o rbgtkvscale.o rbgtkvscrollbar.o rbgtkvseparator.o rbgtkwidget.o rbgtkwindow.o rbgtkwindowgroup.o -L. -Ld:/RubyGTK/lib -L. -LD:/MSYS32/lib -LD:/MSYS32/lib -LD:/MSYS32/lib -LD:/MSYS32/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/glib/src -LD:/MSYS32/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/glib/src -LD:/MSYS32/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/pango/src -LD:/MSYS32/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/pango/src -LD:/MSYS32/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/gtk/src -LD:/MSYS32/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/gtk/src -Wl,--enable-auto-image-base,--enable-auto-import,--export-all,--out-implib=libruby-gtk2.a -lmsvcrt-ruby18 -lgthread-2.0 -lglib-2.0 -lintl -lgtk-win32-2.0 -lgdk-win32-2.0 -latk-1.0 -lgio-2.0 -lgdk_pixbuf-2.0 -lpangowin32-1.0 -lgdi32 -lpangocairo-1.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 -lglib-2.0 -lintl -lcairo -lruby-glib2 -lruby-glib2 -lruby-pango -lruby-pango -lruby-gtk2 -lruby-gtk2 -lshell32 -lws2_32
D:\MinGW32\bin\..\lib\gcc\mingw32\3.4.5\..\..\..\..\mingw32\bin\ld.exe: cannot find -lruby-gtk2
collect2: ld returned 1 exit status
make[2]: *** [gtk2.so] Error 1
make[2]: Leaving directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/gtk/src'
make[1]: *** [all] Error 2
make[1]: Leaving directory `/home/hatsune/RubyGTK/ruby-gtk2-0.19.1/gtk'

-----
SUCCEEDED: glib gdkpixbuf pango atk
FAILED: gtk
-----
Done.



…gtk以外はコンパイル出来るのだが…
…諦めようか…(ヽ´ω`)

2009年8月16日日曜日

Windows Server 2008 R2 RTMの評価版配布開始

つい最近Windows 7(RTM)がTechnetとMSDNのサブスクリプションで配布され始めたが、それと同時期にWindows Server 2008 R2 x64(RTM)の評価版が、一般向けにこっそり(?)配布されていた。

中身はだいたいWindows 7 x64(RTM)と同じなので、Windows 7を買いたいが、互換性やら使用感がどうなるのか気になると言う人は、これを使ってみると良いと思う。
本当はWindows 7の評価版をリリースしてくれると助かるのだが…
http://technet.microsoft.com/ja-jp/evalcenter/dd459137.aspx

登録メンドクセ('A`)って人はこちらから。
http://www.microsoft.com/downloads/details.aspx?FamilyID=ba571339-5436-4cf5-9c37-6ed7dab6f781&DisplayLang=ja


ついでに、Server 2008 R2のHyper-V向けVHDイメージも配布中。
http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=9040a4be-c3cf-44a5-9052-a70314452305


それなりのスペックのx64PC(Server)があれば全て無料で行えるので、これは面白そうな気がしなくもない。
…ダウンロードに8時間…(´;ω;`)ウッ…


※追記
isoイメージのダウンロードが終わったのでVMwareにインストールしてみた。

7600.16385.090713-1255_x64fre_server_eval_ja-jp-GRMSXEVAL_JA_DVD.iso
SHA-1Hash: 7955de7fb76a731441bc3e28772a5c70057040cf

Windows server 2008 R2 RTM


ずーっと7 RC使い続けていた & Server 2008 R2 Beta時代から何度もインストールしていた、ということで、真新しさが全く感じられないという…('A`)
NT7.0マダァー(*´Д`)?

マルチスレッド対応 マルチフロントエンド(もどき) 改め Multi thread Media Encoder Frontendのまとめサイトがようやく完成

マルチスレッド対応 マルチフロントエンド(もどき)では、どういうソフトなのか分かりづらいという反省点を踏まえ改名。ついでに、物凄く分かりづらかった使い方の説明や、最新版の配布を行うためのまとめサイトを作ってみた。

まとめサイトは以下のリンクから。
http://mtmef.g.hachune.net/


更についでに、ソフト本体の更新も行った。
今回(v1.1)の変更点は、以下の通り。
・lame(mp3)に加え、ogg Vorbis(ogg)、Free Lossless Audio Encoder(flac)、The True Audio(tta)、neroAacEnc(aac、m4a)、Windows Media Audio(wma)によるエンコードに対応。
・ディレクトリ選択時に、ディレクトリパス名の補完をTabキーで行えるようになった。
・-vオプションを付けて起動するか、スレッド数を1にすることで、エンコード/デコード処理の詳細を表示するようにした。
・その他細かな修正


プログラムを直接ダウンロードしたい場合は以下のリンクからどうぞ。
https://sites.google.com/a/g.hachune.net/mtmef/home/download

…(´ぅω・`)ネムタス

2009年8月13日木曜日

Windows 7 x64にMinGW x64環境を構築

折角Windows 7 x64を使っているのに、x86アプリばかり動かしている現状が嫌になったので、MinGW x64をインストールして、Windows x64ネイティブなバイナリを作れるようしようとしていた…のだが、なかなか設定が分からず苦戦していた。

ようやくlameがコンパイルできたので、環境構築手順を纏めてみる。



まず、MinGW-w64環境を構築する。

MinGW-w64のバイナリをhttp://sourceforge.net/projects/mingw-w64/から持ってくる。
私は、現時点の最新版である、mingw-w64-bin_i686-mingw_20090811.zipをダウンロードした。

ダウンロードが終わったら、アーカイブ(zip)を解凍する。
解凍先は、C:\MinGWなど、自分が分かりやすいパスを指定する。
私の場合は、D:\MinGWに解凍した。

解凍後、binフォルダに入っている
x86_64-w64-mingw32-addr2line.exe、x86_64-w64-mingw32-ar.exe......
というファイルを、全て
addr2line.exe、ar.exe......
となるようにリネームする。


一つずつリネームするのが面倒なら、以下の内容のバッチファイルをbinフォルダに作成し、実行すると楽かもしれない。
@echo on
rename x86_64-w64-mingw32-addr2line.exe addr2line.exe
rename x86_64-w64-mingw32-ar.exe ar.exe
rename x86_64-w64-mingw32-as.exe as.exe
rename x86_64-w64-mingw32-c++.exe c++.exe
rename x86_64-w64-mingw32-c++filt.exe c++filt.exe
rename x86_64-w64-mingw32-cpp.exe cpp.exe
rename x86_64-w64-mingw32-dlltool.exe dlltool.exe
rename x86_64-w64-mingw32-dllwrap.exe dllwrap.exe
rename x86_64-w64-mingw32-g++.exe g++.exe
rename x86_64-w64-mingw32-gcc-4.5.0.exe gcc-4.5.0.exe
rename x86_64-w64-mingw32-gcc.exe gcc.exe
rename x86_64-w64-mingw32-gccbug gccbug
rename x86_64-w64-mingw32-gcj.exe gcj.exe
rename x86_64-w64-mingw32-gcov.exe gcov.exe
rename x86_64-w64-mingw32-gfortran.exe gfortran.exe
rename x86_64-w64-mingw32-gprof.exe gprof.exe
rename x86_64-w64-mingw32-jcf-dump.exe jcf-dump.exe
rename x86_64-w64-mingw32-ld.exe ld.exe
rename x86_64-w64-mingw32-nm.exe nm.exe
rename x86_64-w64-mingw32-objcopy.exe objcopy.exe
rename x86_64-w64-mingw32-objdump.exe objdump.exe
rename x86_64-w64-mingw32-ranlib.exe ranlib.exe
rename x86_64-w64-mingw32-readelf.exe readelf.exe
rename x86_64-w64-mingw32-size.exe size.exe
rename x86_64-w64-mingw32-strings.exe strings.exe
rename x86_64-w64-mingw32-strip.exe strip.exe
rename x86_64-w64-mingw32-windmc.exe windmc.exe
rename x86_64-w64-mingw32-windres.exe windres.exe
pause

(最初からgcc.exeのようなファイル名になっている、/mingwフォルダや/x86_64-w64-mingw32フォルダに入っているものを使用しようとすると、コンパイル時に

hatsune@HATSUNE-SERVER ~
$ gcc -v time.c
Using built-in specs.
Target: x86_64-w64-mingw32
Configured with: ../../../build/gcc/gcc/configure --target=x86_64-w64-mingw32 --
prefix=/c/buildbot/vista64-mingw32/mingw-x86-x86_64/build/build/root --with-sysr
oot=/c/buildbot/vista64-mingw32/mingw-x86-x86_64/build/build/root --enable-langu
ages=all,obj-c++ --enable-fully-dynamic-string --disable-multilib
Thread model: win32
gcc version 4.5.0 20090806 (experimental) (GCC)
COLLECT_GCC_OPTIONS='-v' '-mtune=generic'
cc1 -quiet -v -iprefix d:\mingw\mingw\bin\../lib/gcc/x86_64-w64-mingw32/4.5.0/
time.c -quiet -dumpbase time.c -mtune=generic -auxbase time -version -o S:\UserT
emp\hatsune\ccd8FXk1.s
gcc.exe: CreateProcess: No such file or directory

とエラーが発生してしまい、コンパイルできない問題にハマってしまった)





次に、MinGWを使用する上で必要なMSYS環境を構築する。
MSYSバイナリをhttp://sourceforge.net/projects/mingw/から持ってくる。
現時点の最新版である、MSYS-1.0.11.exeをダウンロードした。

ダウンロード後、インストーラーを実行し、MSYSのインストールパスを指定、インストールすると、コマンドプロンプトが開き、
This is a post install process that will try to normalize between your MinGW install if any as well as your previous MSYS installs if any. I don't have any traps as aborts will not hurt anything.
Do you wish to continue with the post install? [yn ]

と出るので"y"を入力。

すると、
Do you have MinGW installed? [yn ]
と出るので、これも"y"を入力。

次に、
Please answer the following in the form of c:/foo/bar.
Where is your MinGW installation?

と出るので、MinGWをインストールしたパスを指定する。
私の場合は、D:/MinGWを指定した。
ここでパスを指定する際に、パスの区切りを"\(円記号・バックスラッシュ)"ではなく"/(スラッシュ)"にする必要があるので注意する。

MSYS Setup

後は、てきとうなCのソースコードを持ってきて、MSYS上でコンパイルして実行する。
lameなら、いつも通り

$ ./configure
$ make
$ make install

とするだけで、コンパイルできる。

タスクマネージャーでそのプロセスを見て、"*32"がプロセス名に付いていなかったら、x64のバイナリが作成・実行出来ているということになる。
lame x64


手順並べるとこんなに簡単なのに、何故あんなに苦労したんだ…('A`)

2009年8月8日土曜日

今日から4日ほど

大学のゼミ合宿に行ってきます(`・ω・´)シャキーン

4日間EeePCだけで過ごさないといけないのか…('A`)

2009年8月7日金曜日

ダイナミックDNSの自動更新ツールをWindowsサービス化してみた

Windowsサービスアプリケーションの作り方が分かったので、DynamicDNSのアップデートツールを、作り直してみた。

以前作っていたものは、MyDNS専用かつアカウント一つしか使用できなかったのだが、今回は、
・通常のHTTPか、ベーシック認証を利用できるDDNSであればなんでも利用可能
・複数同時登録可能
・Windowsサービスとして動作
というものを作ってみた。

Windowsサービスとして動作するので、Windowsが起動してさえいれば、ログインしていなくても自動的に更新が行われるので、たぶん便利。


プログラム本体は、以下からダウンロードできます。



Windows 7 RC x64でしか動作確認していないので、それ以外(特にXP)で正しく動作するかちょっと不安…(´・ω・`)
…もしコケたら…ごめんね…(´;ω;`)ブワッ

2009年8月3日月曜日

RubyでWindowsのサービスを作る

以前DDNSUpdaterを作った時に、「Windowsサービス化はむりぽ」と書いたが、
ようやくWindowsサービスをRubyで作れるレベルになったので、纏めてみる。

まず、WindowsサービスをRubyで作成する上で必要なライブラリWin32-Serviceを入手する。
RubyGemsを使用して入手するのが一番楽だと思われる。

Win32アプリケーションを作成するにあたって、Win32Utilsはかなり重要になってくる(と思われる)ので、他にも必要としているものが無いか探しておくと、後で悩むことが無くなって良いと思う。

RubyGemsとWin32-Serviceのインストール
http://projectzero-swb.blogspot.com/2009/08/win32-servicerubygems.html


RubyでWindowsサービスを作成する上で必要なことは、
・サービス登録を行うスクリプトの作成
・サービスデーモンの作成
・サービスデーモンに行わせる処理の作成

の3つ。

この中で、・サービス登録を行うスクリプトの作成は、Windows標準付属のsc.exeで代用することも可能だが、そこも自動化できないといろいろ面倒なので、普通にスクリプトを作成する。
作ると言っても、殆どライブラリ付属のサンプルスクリプトのままでおkなのだが…


以下がサービスを管理するクラス。
定数SERVICE_NAME、SERVICE_DISPLAYNAME、SERVICE_DESCRIPTION、SERVICE_COMMANDを編集することによって、どんなサービスにも応用できる…はず。

サービスを登録…service_install
サービスを開始…service_start
サービスを停止…service_stop
サービスを再開…service_resume
サービスを削除…service_uninstall
サービスの状態を返す…service_stat


#! ruby.exe

require 'rubygems'
require 'win32/service'
include Win32

# サービスの作成に必要な情報を定義(ServiceSetupクラスで使用)
SERVICE_NAME        = "Rubyservice"                  # サービスの正式な名前。これで全て管理される。
SERVICE_DISPLAYNAME = "RubyService"                  # サービスの表示名。サービス一覧などに表示される名前
SERVICE_DESCRIPTION = "Rubyで作成したサービスです。" #
# Rubyスクリプトそのままだと、インタプリタのパス指定間違いによる193 0xc1エラーが発生するなど、
# いろいろ面倒なのでexerbでWindows用exeファイル化しておく
SERVICE_COMMAND     = "C:\\Service\\Rubyservice.exe"


class ServiceSetup
  def initialize
    raise TypeError if SERVICE_NAME        == nil || SERVICE_NAME.class        != String
    raise TypeError if SERVICE_DISPLAYNAME == nil || SERVICE_DISPLAYNAME.class != String
    raise TypeError if SERVICE_DESCRIPTION == nil || SERVICE_DESCRIPTION.class != String
    raise TypeError if SERVICE_COMMAND     == nil || SERVICE_COMMAND.class     != String
  end

  # サービスインストール
  def service_install
    Service.new(
                :service_name     => SERVICE_NAME,
                :display_name     => SERVICE_DISPLAYNAME,
                :description      => SERVICE_DESCRIPTION,
                :binary_path_name => SERVICE_COMMAND
                )
    sleep 1
    puts 'サービス ' + SERVICE_NAME + ' をインストールしました。' 
  end

  # サービス開始
  def service_start
    if Service.status(SERVICE_NAME).current_state != 'running'
      Service.start(SERVICE_NAME)
      while Service.status(SERVICE_NAME).current_state != 'running'
        puts 'One moment...' + Service.status(SERVICE_NAME).current_state
        sleep 1
      end
      puts 'サービス ' + SERVICE_NAME + ' を開始しました。'
    else
      puts SERVICE_NAME + " は既に実行されています。"
    end
  end

  # サービス停止
  def service_stop
    if Service.status(SERVICE_NAME).current_state != 'paused'
      Service.pause(SERVICE_NAME)
      while Service.status(SERVICE_NAME).current_state != 'paused'
        puts 'One moment...' + Service.status(SERVICE_NAME).current_state
        sleep 1
      end
      puts 'サービス ' + SERVICE_NAME + ' を停止しました。'
    else
      puts SERVICE_NAME + ' は既に停止しています。'
    end
  end

  # サービス再開
  def service_resume
    if Service.status(SERVICE_NAME).current_state != 'running'
      Service.resume(SERVICE_NAME)
      while Service.status(SERVICE_NAME).current_state != 'running'
        puts 'One moment...' + Service.status(SERVICE_NAME).current_state
        sleep 1
      end
      puts 'サービス ' + SERVICE_NAME + ' を再開しました。'
    else
      puts SERVICE_NAME + " は既に実行されています。"
    end
  end

  # サービスアンインストール
  def service_uninstall
    if Service.status(SERVICE_NAME).current_state != 'stopped'
      Service.stop(SERVICE_NAME)
    end
    while Service.status(SERVICE_NAME).current_state != 'stopped'
      puts 'One moment...' + Service.status(SERVICE_NAME).current_state
      sleep 1
    end
    Service.delete(SERVICE_NAME)
    puts 'サービス ' + SERVICE_NAME + ' を削除しました。'
  end

  # サービスの状態を返す
  def service_stat
    begin
      stat = Service.status(SERVICE_NAME).current_state
    rescue
      return false
    end
    return stat
  end
end




次に、サービスデーモンを作成する。
ここでは例として、DDNSUpdaterのおまけ(?)として作っていた「みくろっく」をWindowsサービスデーモン化してみた。

Win32::Daemonクラスには、
サービスの開始時に実行…service_init
サービスのメイン処理…service_main
サービス停止時の処理…service_stop
サービス一時停止時の処理…service_pause
サービスらい開示の処理…service_resume
を定義することができる。
それぞれのメソッドは、対応するシグナルが送られてきた時に実行される。

通常は、
service_init→service_main
の順で実行される。


#! ruby.exe

begin
  require 'rubygems'
  require 'win32/daemon'
  # require 'win32/sound' # サービスにすると音が出なかったので
  include Win32
  SYSTEM_BINDIR = ENV["ProgramFiles"] + "\\Mikulock"
  SYSTEM_WAVDIR = SYSTEM_BINDIR + "\\Sound"
  SYSTEM_LOGDIR = ENV["ProgramData"]  + "\\Mikulock"
  SYSTEM_BINARY = SYSTEM_BINDIR + "\\mikulock_service.exe"
  SYSTEM_LOG    = SYSTEM_LOGDIR + "\\mikulock.log"

  #Win32::Daemonクラスの継承
  class RubyDaemon < Daemon
    # サービスの初期化
    def service_init
      File.open(SYSTEM_LOG, "a") {|writelog|
        writelog.printf("%-36s - Service Start.\n", Time.now.to_s)
      }
      sleep 10
    end

    module Mikulock
      def self.mikulock_play(play)
        if File.exist?("Sound\\" + play) == true
          IO.popen("mpg123 -q Sound\\#{play}")
          File.open(SYSTEM_LOG, "a") do |writelog|
            writelog.printf("%-36s - Play: %s\n", Time.now.to_s, File.expand_path("Sound\\" + play))
          end
        end
      end

      def self.mikulock_timecheck
        if Time.now.to_a[1] == 0
          case Time.now.to_a[2]
          when 0
            return "0h.mp3"
          when 1, 13
            return "1h.mp3"
          when 2, 14
            return "2h.mp3"
          when 3, 15
            return "3h.mp3"
          when 4, 16
            return "4h.mp3"
          when 5, 17
            return "5h.mp3"
          when 6, 18
            return "5h.mp3"
          when 7, 19
            return "7h.mp3"
          when 8, 20
            return "8h.mp3"
          when 9, 21
            return "9h.mp3"
          when 10, 22
            return "10h.mp3"
          when 11, 23
            return "11h.mp3"
          when 12
            return "12h.mp3"
          end
        elsif Time.now.to_a[1] == 30
          return "30min.mp3"
        else
          return false
        end
      end
    end

    # サービスのメイン処理
    def service_main
      play     = String.new
      play_end = String.new #お知らせが終わったものを記録

      # 最初の一回を設定
      if Time.now.to_a[2] >= 5  && Time.now.to_a[2] < 11
        play = "morning.mp3"
      elsif Time.now.to_a[2] >= 11 && Time.now.to_a[2] < 18
        play = "noon.mp3"
      else
        play = "night.mp3"
      end

      # 音声を再生する為のスレッド
      thread = Thread.new do
        while running?
          if state == RUNNING
            if play != false && play != play_end
              Mikulock.mikulock_play(play)
              play_end = play
            end
            sleep 5
          end
        end
      end

      # サービスの状態がrunningの時のみ繰り返す
      while running?
        if state == RUNNING
          play = Mikulock.mikulock_timecheck
          File.open(SYSTEM_LOG, "a") {|writelog|
            writelog.printf("%-36s - Running\n", Time.now.to_s)
          }
          sleep 5
        else
          sleep 1
        end
      end
    end

    # Stop(停止)シグナルが送られてきた時の処理
    def service_stop
      File.open(SYSTEM_LOG, "a"){|writelog|
        writelog.printf("%-36s - Stop\n", Time.now.to_s)
      }
    end

    # Pause(一時停止)シグナルが送られてきた時の処理
    def service_pause
      File.open(SYSTEM_LOG, "a"){|writelog|
        writelog.printf("%-36s - Pause\n", Time.now.to_s)
      }
    end

    # Resume(再開)シグナルが送られてきたときの処理
    def service_resume
      File.open(SYSTEM_LOG, "a"){|writelog|
        writelog.printf("%-36s - Resume\n", Time.now.to_s)
      }
    end
  end

  # mpg123のインストール先にcdしておく
  Dir.chdir(SYSTEM_BINDIR + "\\mpg123"){
    # このループの中で全ての処理が行われる
    RubyDaemon.mainloop
  }

  # 例外が発生した時は、例外の内容をログに書き込み、さらに例外を発生させる
rescue => error
  File.open(SYSTEM_LOG, "a"){|writelog|
    writelog.printf("%-36s - Error: %s\n", Time.now.to_s, error)
  }
  raise
end





これをそのまま実行しても、`mainloop': Service_Main thread exited abnormally.....となり、実行できないので、
サービス登録クラスを組み込んだスクリプトを利用し、サービスに登録、ログを読みながらデバッグする必要がある。

サービス作成中に良くあるミスは、
・Rubyインタプリタのパス、プログラムのソースのパスが間違っている
・service_mainのwhile running?の中が正しく書かれていない
・while running?の中にsleep 60などと書いてしまい、60秒待たないとサービスを停止できなくなる
・例外処理が不十分でサービスが落ちる
だった。


サービスの登録が終わったら、
コントロールパネル→管理ツール→サービス
を開き、登録したサービスが正しく実行されているか確認する。
サービスの状態が「開始」になっていれば、問題無く動作している…はず。
サービス一覧

OS起動時に自動実行したいときは、「サービス」でMikulockのプロパティを開き、
「スタートアップの種類」を「自動」もしくは「自動(遅延開始)」に設定すれば良い。
サービスのプロパティ
09.08.04 追記
・スクリプトの中で「スタートアップの種類」を「自動」に設定するにはどうしたら…?
:start_type => Service::AUTO_START
を、サービスインストール時の引数に渡せば良い模様。

ここまでで、とりあえずWindowsサービスを利用したRubyアプリケーションが作れるということが分かった。



以前はWin32/Soundを使用し、音を鳴らしていたが、サービスにすると上手く鳴らないようだったので、
mpg123を使用し、mp3を再生するようにしてみたのだが、
mpg123の-qオプションを使用しても、なにかが標準入力か標準出力を開くようで、
「対話型サービスの検出」ウィンドウが現れてしまうという問題が…
デスクトップとの対話
「デスクトップとの対話をサービスに許可する」を無効にすれば一応出現しなくなるが、Windowsのアプリケーションログに残ってしまうので、根本的な解決にはならない。

更に、mpg123には、どんなmp3ファイルを再生しても最後の2〜3秒がカットされてしまうという問題も…


今回作ったみくろっく(Windowsサービス版)のダウンロードは以下から。
Win32-Serviceのテスト用途にどうぞ。


※09.09.17 追記
みくろっく改良版



※09.08.07 追記
Dynamic DNS UpdaterをWindowsサービス化してみた。
以下からダウンロードできます。

Win32-Serviceが欲しかったのでRubyGemsをインストール

RubyでWindowsサービスを作成しようと、Win32-Serviceをインストールしようとしたところ、RubyGemsが無いと面倒くさそうだったので、まずはRubyGemsをインストールすることにした。


09.08.03時点で最新のrubygems-1.3.5.zipをダウンロードしてきて解凍する。
コマンドプロンプトを開き、setup.rbを実行。
C:\Users\hatsune\Desktop\rubygems-1.3.5> ruby setup.rb


バージョンを確認して、
C:\Users\hatsune\Desktop\rubygems-1.3.5> gem -v
1.3.5

となれば、インストールに成功していると思われる。

あとは、好きなライブラリをgemsから持ってくることが出来る。
C:\> gem install win32-service



…と思っていても、上手くいかないことがあるのが普通


今回は、zlibとsslのライブラリが足りないと言われた。
Ruby-mswin32をそのまま使用している場合はまず発生すると思われる。

Ruby-mswin32のインストールを読むと、
http://jarp.does.notwork.org/win32/にライブラリがあるので、これをインストールしろ」とのこと。


仕方が無いので、ライブラリをインストール。
openssl-0.9.8d-1-mswin32.zip
zlib-1.1.4-1-mswin32.zipをダウンロードしてくる。
これを、フォルダ階層を維持しつつ、Rubyのバイナリがインストールしてあるフォルダにコピーする。
(\binはC:\Ruby\binへ、\libはC:\Ruby\libへ…という感じに)


これで実行できる…と思い、再度実行すると、今度はssleay32.dllが見つからないと言われた。
"ssleay32.dll"でググったところ、自分でOpenSSLをコンパイルするか、
OpenSSLのWindows用バイナリを配布してくれているサイトから持ってきて、
ssleay32.dllをコピーすればいいらしい。
(コンパイルするのは面倒なので、http://curl.haxx.se/download/openssl_w32vc6-0.9.8g.7zをダウンロード、インストールした。)


これでようやくRubyGemsが動き、Win32-Serviceをインストールすることが出来た。