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をダウンロードした。

インストール

以下にコピー

C:\usr\lib\sun.com\jna\3.2.7\jna.jar
                             platform.jar

groovyの実行時のCLASSPATHに上記の2つの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系のみ対応なのか?


対処

Groovy 1.7.5を使う



自分が作ったスクリーンセーバの待ち時間を変えるスクリプトはまだエラーがでてる。

はまったところ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."

実行環境

参考














Groovyの詳細についてはJavadocと以下の書籍を参考にしている。

Groovyイン・アクション
Dierk Konig Andrew Glover Paul King Guillaume Laforge Jon Skeet
毎日コミュニケーションズ
売り上げランキング: 294340




JNIはこの本しかない。


Groovyイン・アクションを読むならあった方が便利かな。

ブックストッパー

トモエ算盤
売り上げランキング: 614


薄めの本は「ほんたった」で立ててる