GroovyでWindowsのスクリーンセーバーの待ち時間を変更する
C言語も使えるし、JNIでも全く問題ない。
一般的なJNIの使い方としてのJavaからNative(C)の呼び出すのは
もちろん問題なく使える。
逆に、Native(C)からJavaの呼び出しだって、どんとこいだ。
でも、JNIではなくて、JNAを使う。
JNAを使ったことがないので使ってみたいというのと、
さんざんJNIで痛い目を見てきたからだ。
まぁJNAにしても。やりすぎれば痛い目に合うだろうけど(^^;
ダウンロード
Java Native Access (JNA)
3.2.7のjan.jarとplatform.jarをダウンロードした。
最初のソース
import com.sun.jna.*; import com.sun.jna.win32.*; import com.sun.jna.platform.win32.User32; import com.sun.jna.ptr.*; interface MyUser32 extends User32 { MyUser32 INSTANCE = (User32)Native.loadLibrary("User32", MyUser32.class, W32APIOptions.DEFAULT_OPTIONS) // BOOL WINAPI SystemParametersInfo( // UINT uiAction, //__in // UINT uiParam, //__in // PVOID pvParam, //__in_out // UINT fWinIni //__in // ); boolean SystemParametersInfo(int uiAction, int uiParam, ByReference pvParam,int fWinIni) } /* WinUser.h */ def final int SPI_SETSCREENSAVETIMEOUT = 0x000F def final int SPIF_UPDATEINIFILE = 0x0001 timer = new Integer(600) boolean ok = MyUser32.INSTANCE.SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, timer, 0, SPIF_UPDATEINIFILE); if(!ok) { throw new RuntimeException("SystemParametersInfo call failed. ); }
これだけで、はまりにはまった。
はまったところ1
パッケージ構成、クラス名が異なるので、ネットの古いバージョンのソースを参考にしてはまった。
JNAのバージョン3.2.5以前 | JNAのバージョン3.2.7 |
---|---|
examples.jar | platform.jar |
com.sun.jna.examples.win32.W32API | com.sun.jna.win32.W32APIOptions |
com.sun.jna.examples.win32.User32 | com.sun.jna.platform.win32.User32 |
はまったところ2
エラー
Caught: java.lang.IncompatibleClassChangeError: Found interface MyUser32, but class was expected at MyUser32.<clinit>(setScreenSaverTime.groovy:8) at setScreenSaverTime.class$(setScreenSaverTime.groovy) at setScreenSaverTime.$get$$class$MyUser32(setScreenSaverTime.groovy) at setScreenSaverTime.run(setScreenSaverTime.groovy:27)
これいに気づくのにえらい時間がかかった。
「MyUser32 extends User32」 →「User32 extends StdCallLibrary」にしてみたり、
「W32APIOptions.DEFAULT_OPTIONS」を消してみたり、
groovysh、groovyConsoleで試してみたしたり、
$GROOVY_HOME\libにいれたgraphicsbuilderのjarが悪さしてるのかと
まっ更な環境に戻したり。
それでもだめ。
Groovy+JNACommentsのソースを動かしてみるも。
import com.sun.jna.*; import com.sun.jna.win32.*; interface CLibrary extends Library { CLibrary INSTANCE = Native.loadLibrary("msvcrt", CLibrary.class); int _chdir(String dir); } CLibrary.INSTANCE._chdir(args[0]) println "pwd.bat".execute().text
変わらずエラー。
[D:\workspace\groovy_SandBox]groovy JNAKenel32Test.groovy D:\temp\test Caught: java.lang.IncompatibleClassChangeError: Found interface Kernel32Library, but class was expected at Kernel32Library.<clinit>(JNAKenel32Test.groovy:5) at JNAKenel32Test.class$(JNAKenel32Test.groovy) at JNAKenel32Test.$get$$class$Kernel32Library(JNAKenel32Test.groovy) at JNAKenel32Test.run(JNAKenel32Test.groovy:9)
Groovy 1.7.5も入れてあるから、1.7.5にして試してみた。
・・・
上記のカレントディレクトリ移動アプリは動いた。
手元の環境は、pwd.exeではなく、pwd.batなので、ちょっと修正。
[D:\workspace\groovy_SandBox]groovy D:\workspace\groovy_SandBox\JNAKenel32Test.groovy d:\temp\test
d:\temp\test
jan.jar/platform.jar 3.2.7 + Groovy 1.7系のみ対応なのか?
はまったところ3
エラー
Caught: groovy.lang.MissingMethodException: No signature of method: $Proxy4.SystemParametersInfo() is applicable for argument types: (java.lang.Integer, java.lang.Integer, com.sun.jna.Memory, java.lang.Integer) values: [15, 600, allocated@0x3c92dd8 (4 bytes), 1] Possible solutions: SystemParametersInfo(int, int, com.sun.jna.ptr.ByReference, int) at setScreenSaverTime.run(setScreenSaverTime.groovy:29)
対処
ポインター(ByReference)に0を指定してるからだめなようだ。
IntByReference iref = new IntByReference(); で irefを第3パラメータに指定
Groovyの場合、数値もオブジェクトなので、インタフェース宣言もintからIntegerへ変更
はまったところ4
エラー
[D:\workspace\groovy_SandBox]groovy setScreenSaverTime.groovy Caught: java.lang.UnsatisfiedLinkError: Error looking up function 'SystemParametersInfo': ?w?????v???V?[ at setScreenSaverTime.run(setScreenSaverTime.groovy:28)
対処
UnsatisfiedLinkErrorだからDLL名かインタフェースが違うのか。
DLL名、関数名、パラメータ名はMSDNのSystemParametersInfoで確認したんだけどな。。。
実際のDLLを覗く
[D:\workspace\groovy_SandBox]groovy setScreenSaverTime.groovy [D:\workspace\groovy_SandBox]dumpbin /exports c:\WINDOWS\system32\user32.dll|findstr SystemParametersInfo 666 299 0001DEB2 SystemParametersInfoA 667 29A 00009F06 SystemParametersInfoW
直呼びだからANSI版「SystemParametersInfoA」かUnicode 版「SystemParametersInfoW」を
選択しないとだめなのか。
「SystemParametersInfo」→「SystemParametersInfoA」に変更
はまったところ4
エラー
Caught: groovy.lang.MissingPropertyException: No such property: argv for class: setScreenSaverTime at setScreenSaverTime.run(setScreenSaverTime.groovy:26)
対処
argv[0] じゃなくてargs[0]
Cのクセが・・・
動いたソース
import com.sun.jna.*; import com.sun.jna.win32.*; import com.sun.jna.platform.win32.User32; import com.sun.jna.ptr.*; interface MyUser32 extends User32 { MyUser32 INSTANCE = (MyUser32)Native.loadLibrary("user32", MyUser32.class, W32APIOptions.DEFAULT_OPTIONS) // BOOL WINAPI SystemParametersInfo( // UINT uiAction, //__in // UINT uiParam, //__in // PVOID pvParam, //__in__out // UINT fWinIni //__in // ); boolean SystemParametersInfoA(Integer uiAction, Integer uiParam, ByReference pvParam, Integer fWinIni) } /* WinUser.h */ def final int SPI_SETSCREENSAVETIMEOUT = 0x000F def final int SPIF_UPDATEINIFILE = 0x0001 timer = new Integer(args[0]) IntByReference iref = new IntByReference(); boolean ok = MyUser32.INSTANCE.SystemParametersInfoA(SPI_SETSCREENSAVETIMEOUT, timer, iref, SPIF_UPDATEINIFILE); if(!ok) { throw new RuntimeException("SystemParametersInfo call failed."); } println "SUCCESS:The waiting time of the screensaver was changed at $timer seconds."
実行環境
- OS:Windows XP
- Groovy Version: 1.7.5
- JVM: 1.6.0_12
参考
- JNA API Documentation
- JNIより簡単にJavaとC/C++をつなぐ「JNA」とは
- Groovy+JNAComments
- How to determine if a screensaver is running in Java?
- gift: detecting if screensaver is run...]
- Re: [jna-users] Question on mapping
- MSDNのSystemParametersInfo
Groovyの詳細についてはJavadocと以下の書籍を参考にしている。
JNIはこの本しかない。
Groovyイン・アクションを読むならあった方が便利かな。
薄めの本は「ほんたった」で立ててる