Upload page content

You can upload content for the page named below. If you change the page name, you can also upload content for another page. If the page name is empty, we derive the page name from the file name.

File to load page content from
Page name
Comment

Locked History Actions

swt

SWT

SWTはなぜDLLが要らない(ように見える)のか

大昔のSWTはjarファイルとDLLファイル(Windowsの場合)とに分離しており、これをアプリケーションで使うにはアプリ自体にはjarを付属させ、dllファイルは「パス」に置くしかなかった(起動時にjava.library.pathを指定してもよいが)。しかし、現在のswtはswt.jarのみをアプリに付属させれば良いようになっている。

しかし当然のことながら、相変わらずDLLは必要なのであり、しかもこれはOS側がロードするものなので、それぞれのDLLはそれぞれ一つのファイルとなっていなければならない。一体どうやっているのか?

以下はswt3.7での検証結果である。

jarの中にすべてのdllが含まれている

例えば、win32-win32-x86用のswt.jarを展開してみると、以下のようなファイルが現れる。

2011/09/05  10:43    <DIR>          .
2011/09/05  10:43    <DIR>          ..
2011/06/13  20:51               187 external.xpt
2011/09/05  10:43    <DIR>          META-INF
2011/09/05  10:43    <DIR>          org
2011/05/26  19:13            53,248 swt-awt-win32-3735.dll
2011/05/26  19:13           118,784 swt-gdip-win32-3735.dll
2011/05/26  19:13            77,824 swt-webkit-win32-3735.dll
2011/05/26  19:13            61,440 swt-wgl-win32-3735.dll
2011/05/26  19:13           430,080 swt-win32-3735.dll
2011/05/26  19:13            77,824 swt-xulrunner-win32-3735.dll
2011/06/13  20:51                13 version.txt
               8 個のファイル             819,400 バイト

このように、実はswt.jarのトップに必要なdllが格納された状態になっている。 これをどうやって実行時に「Windowsにロードさせる」だろうか?

DLLロードまでの経緯

ここで、実際にDLLがロードされるまでの経緯を追ってみる。これはswtのソースコードを見ればすぐにわかる。 例えば、org.eclipse.swt.awt.SWT_AWT.javaを見てみる。これは、SWTからSwingの機能を呼び出すためのクラスである。 冒頭に、以下の部分がある。

static synchronized void loadLibrary () {
        if (loaded) return;
        loaded = true;
        Toolkit.getDefaultToolkit();
        /*
        * Note that the jawt library is loaded explicitly
        * because it cannot be found by the library loader.
        * All exceptions are caught because the library may
        * have been loaded already.
        */
        try {
                System.loadLibrary("jawt");
        } catch (Throwable e) {}
        Library.loadLibrary("swt-awt");
}

とりあえず、「System.loadLibrary("jawt")」は無視し、「Library.loadLibrary」("swt-awt")に注目する。 このLibraryとは、org.eclipse.swt.internal.Libraryであるが、このloadLIbraryを見ると、

public static void loadLibrary (String name) {
        loadLibrary (name, true);
}
....
public static void loadLibrary (String name, boolean mapName) {
        String prop = System.getProperty ("sun.arch.data.model"); //$NON-NLS-1$
        if (prop == null) prop = System.getProperty ("com.ibm.vm.bitmode"); //$NON-NLS-1$
        if (prop != null) {
                if ("32".equals (prop) && IS_64) { //$NON-NLS-1$
                        throw new UnsatisfiedLinkError ("Cannot load 64-bit SWT libraries on 32-bit JVM"); //$NON-NLS-1$
                }
                if ("64".equals (prop) && !IS_64) { //$NON-NLS-1$
                        throw new UnsatisfiedLinkError ("Cannot load 32-bit SWT libraries on 64-bit JVM"); //$NON-NLS-1$
                }
        }
        
        /* Compute the library name and mapped name */
        String libName1, libName2, mappedName1, mappedName2;
        if (mapName) {
                String version = System.getProperty ("swt.version"); //$NON-NLS-1$
                if (version == null) {
                        version = "" + MAJOR_VERSION; //$NON-NLS-1$
                        /* Force 3 digits in minor version number */
                        if (MINOR_VERSION < 10) {
                                version += "00"; //$NON-NLS-1$
                        } else {
                                if (MINOR_VERSION < 100) version += "0"; //$NON-NLS-1$
                        }
                        version += MINOR_VERSION;               
                        /* No "r" until first revision */
                        if (REVISION > 0) version += "r" + REVISION; //$NON-NLS-1$
                }
                libName1 = name + "-" + Platform.PLATFORM + "-" + version;  //$NON-NLS-1$ //$NON-NLS-2$
                libName2 = name + "-" + Platform.PLATFORM;  //$NON-NLS-1$
                mappedName1 = mapLibraryName (libName1);
                mappedName2 = mapLibraryName (libName2);
        } else {
                libName1 = libName2 = mappedName1 = mappedName2 = name;
        }

        StringBuffer message = new StringBuffer();
        
        /* Try loading library from swt library path */
        String path = System.getProperty ("swt.library.path"); //$NON-NLS-1$
        if (path != null) {
                path = new File (path).getAbsolutePath ();
                if (load (path + SEPARATOR + mappedName1, message)) return;
                if (mapName && load (path + SEPARATOR + mappedName2, message)) return;
        }

        /* Try loading library from java library path */
        if (load (libName1, message)) return;
        if (mapName && load (libName2, message)) return;

        /* Try loading library from the tmp directory if swt library path is not specified */
        String fileName1 = mappedName1;
        String fileName2 = mappedName2;
        if (path == null) {
                path = System.getProperty ("user.home"); //$NON-NLS-1$
                File dir = new File (path, SWT_LIB_DIR);
                if ((dir.exists () && dir.isDirectory ()) || dir.mkdirs ()) {
                        path = dir.getAbsolutePath ();
                } else {
                        /* fall back to using the home dir directory */
                        if (IS_64) {
                                fileName1 = mapLibraryName (libName1 + SUFFIX_64);
                                fileName2 = mapLibraryName (libName2 + SUFFIX_64);
                        }
                }
                if (load (path + SEPARATOR + fileName1, message)) return;
                if (mapName && load (path + SEPARATOR + fileName2, message)) return;
        }
                
        /* Try extracting and loading library from jar */
        if (path != null) {
                if (extract (path + SEPARATOR + fileName1, mappedName1, message)) return;
                if (mapName && extract (path + SEPARATOR + fileName2, mappedName2, message)) return;
        }
        
        /* Failed to find the library */
        throw new UnsatisfiedLinkError ("Could not load SWT library. Reasons: " + message.toString()); //$NON-NLS-1$
}
}

前半は、与えられた名称をプラットフォームに合わせて適切な名称に変換している。この場合には「swt-awt」を「swt-awt-win32-3735」に変換しているものと思われる。後半はそれをロードするわけであるが、まずは、swt.library.pathや通常のjavaライブラリパスを優先してロードしてみる。

それが失敗すると、「user.home」のSWT_LIB_DIRからロードしようとする。このSWT_LIB_DIRはファイルの先頭で「SWT_LIB_DIR = ".swt" + SEPARATOR + "lib" + SEPARATOR + os() + SEPARATOR + arch();」などと定義されている。 例えば、Windows 7(32ビット)でユーザ名がfooの場合には、「c:\users\foo\.swt\lib\win32\x86」となる。

それにも失敗すると、

        /* Try extracting and loading library from jar */
        if (path != null) {
                if (extract (path + SEPARATOR + fileName1, mappedName1, message)) return;
                if (mapName && extract (path + SEPARATOR + fileName2, mappedName2, message)) return;
        }

として、jarファイルからdllを抜き出してそれをロードするが、これはextract,loadメソッドを見ればわかるが単純な処理である。 単にjar中のリソースとしてdllファイルをオープンし、先のパスのファイルとしてコピーし、それをSystem.loadLibraryでロードしている。

Java Web Startの場合

以上は、swtを使用するスタンドアロンアプリの場合だが、Java Web Startの場合でも全く同じになる。 通常、Java Web Startにネイティブライブラリを含めるには、nativelibというタグを使わなくてはならないが、swtの場合にはあたかもネイティブライブラリ無しのjarファイルであるかのように扱ってよい。

<resources os="Windows" arch="x86">
  <jar href="lib/win32-win32-x86/swt.jar"/>
</resources>

もちろん、swt.jarが署名されていることが前提である。署名しなければ、、「c:\users\foo\.swt\lib\win32\x86」のようなディレクトリに書きこむことはできず、そもそもネイティブライブラリの呼び出し自体ができない。