= 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
.
2011/09/05 10:43 ..
2011/06/13 20:51 187 external.xpt
2011/09/05 10:43 META-INF
2011/09/05 10:43 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ファイルであるかのように扱ってよい。
{{{
}}}
もちろん、swt.jarが署名されていることが前提である。署名しなければ、、「c:\users\foo\.swt\lib\win32\x86」のようなディレクトリに書きこむことはできず、そもそもネイティブライブラリの呼び出し自体ができない。